RewriteGenerator.java

  1. /*
  2.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.revwalk;

  11. import java.io.IOException;

  12. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  13. import org.eclipse.jgit.errors.MissingObjectException;

  14. /**
  15.  * Replaces a RevCommit's parents until not colored with REWRITE.
  16.  * <p>
  17.  * Before a RevCommit is returned to the caller its parents are updated to
  18.  * create a dense DAG. Instead of reporting the actual parents as recorded when
  19.  * the commit was created the returned commit will reflect the next closest
  20.  * commit that matched the revision walker's filters.
  21.  * <p>
  22.  * This generator is the second phase of a path limited revision walk and
  23.  * assumes it is receiving RevCommits from {@link TreeRevFilter}.
  24.  *
  25.  * @see TreeRevFilter
  26.  */
  27. class RewriteGenerator extends Generator {
  28.     private static final int REWRITE = RevWalk.REWRITE;

  29.     /** For {@link #cleanup(RevCommit[])} to remove duplicate parents. */
  30.     private static final int DUPLICATE = RevWalk.TEMP_MARK;

  31.     private final Generator source;

  32.     private final FIFORevQueue pending;

  33.     RewriteGenerator(Generator s) {
  34.         super(s.firstParent);
  35.         source = s;
  36.         pending = new FIFORevQueue(s.firstParent);
  37.     }

  38.     @Override
  39.     void shareFreeList(BlockRevQueue q) {
  40.         source.shareFreeList(q);
  41.     }

  42.     @Override
  43.     int outputType() {
  44.         return source.outputType() & ~NEEDS_REWRITE;
  45.     }

  46.     @SuppressWarnings("ReferenceEquality")
  47.     @Override
  48.     RevCommit next() throws MissingObjectException,
  49.             IncorrectObjectTypeException, IOException {
  50.         RevCommit c = pending.next();

  51.         if (c == null) {
  52.             c = source.next();
  53.             if (c == null) {
  54.                 // We are done: Both the source generator and our internal list
  55.                 // are completely exhausted.
  56.                 return null;
  57.             }
  58.         }

  59.         applyFilterToParents(c);

  60.         boolean rewrote = false;
  61.         final RevCommit[] pList = c.getParents();
  62.         final int nParents = pList.length;
  63.         for (int i = 0; i < nParents; i++) {
  64.             final RevCommit oldp = pList[i];
  65.             final RevCommit newp = rewrite(oldp);
  66.             if (firstParent) {
  67.                 if (newp == null) {
  68.                     c.parents = RevCommit.NO_PARENTS;
  69.                 } else {
  70.                     c.parents = new RevCommit[] { newp };
  71.                 }
  72.                 return c;
  73.             }
  74.             if (oldp != newp) {
  75.                 pList[i] = newp;
  76.                 rewrote = true;
  77.             }
  78.         }
  79.         if (rewrote) {
  80.             c.parents = cleanup(pList);
  81.         }
  82.         return c;
  83.     }

  84.     /**
  85.      * Makes sure that the {@link TreeRevFilter} has been applied to all parents
  86.      * of this commit by the previous {@link PendingGenerator}.
  87.      *
  88.      * @param c
  89.      * @throws MissingObjectException
  90.      * @throws IncorrectObjectTypeException
  91.      * @throws IOException
  92.      */
  93.     private void applyFilterToParents(RevCommit c)
  94.             throws MissingObjectException, IncorrectObjectTypeException,
  95.             IOException {
  96.         for (RevCommit parent : c.getParents()) {
  97.             while ((parent.flags & RevWalk.TREE_REV_FILTER_APPLIED) == 0) {

  98.                 RevCommit n = source.next();

  99.                 if (n != null) {
  100.                     pending.add(n);
  101.                 } else {
  102.                     // Source generator is exhausted; filter has been applied to
  103.                     // all commits
  104.                     return;
  105.                 }

  106.             }

  107.         }
  108.     }

  109.     private RevCommit rewrite(RevCommit p) throws MissingObjectException,
  110.             IncorrectObjectTypeException, IOException {
  111.         for (;;) {

  112.             if (p.getParentCount() > 1) {
  113.                 // This parent is a merge, so keep it.
  114.                 //
  115.                 return p;
  116.             }

  117.             if ((p.flags & RevWalk.UNINTERESTING) != 0) {
  118.                 // Retain uninteresting parents. They show where the
  119.                 // DAG was cut off because it wasn't interesting.
  120.                 //
  121.                 return p;
  122.             }

  123.             if ((p.flags & REWRITE) == 0) {
  124.                 // This parent was not eligible for rewriting. We
  125.                 // need to keep it in the DAG.
  126.                 //
  127.                 return p;
  128.             }

  129.             if (p.getParentCount() == 0) {
  130.                 // We can't go back any further, other than to
  131.                 // just delete the parent entirely.
  132.                 //
  133.                 return null;
  134.             }

  135.             applyFilterToParents(p.getParent(0));
  136.             p = p.getParent(0);

  137.         }
  138.     }

  139.     private RevCommit[] cleanup(RevCommit[] oldList) {
  140.         // Remove any duplicate parents caused due to rewrites (e.g. a merge
  141.         // with two sides that both simplified back into the merge base).
  142.         // We also may have deleted a parent by marking it null.
  143.         //
  144.         int newCnt = 0;
  145.         for (int o = 0; o < oldList.length; o++) {
  146.             final RevCommit p = oldList[o];
  147.             if (p == null)
  148.                 continue;
  149.             if ((p.flags & DUPLICATE) != 0) {
  150.                 oldList[o] = null;
  151.                 continue;
  152.             }
  153.             p.flags |= DUPLICATE;
  154.             newCnt++;
  155.         }

  156.         if (newCnt == oldList.length) {
  157.             for (RevCommit p : oldList)
  158.                 p.flags &= ~DUPLICATE;
  159.             return oldList;
  160.         }

  161.         final RevCommit[] newList = new RevCommit[newCnt];
  162.         newCnt = 0;
  163.         for (RevCommit p : oldList) {
  164.             if (p != null) {
  165.                 newList[newCnt++] = p;
  166.                 p.flags &= ~DUPLICATE;
  167.             }
  168.         }

  169.         return newList;
  170.     }
  171. }