PendingGenerator.java

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

  11. package org.eclipse.jgit.revwalk;

  12. import java.io.IOException;

  13. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  14. import org.eclipse.jgit.errors.MissingObjectException;
  15. import org.eclipse.jgit.errors.StopWalkException;
  16. import org.eclipse.jgit.lib.ObjectId;
  17. import org.eclipse.jgit.revwalk.filter.RevFilter;

  18. /**
  19.  * Default (and first pass) RevCommit Generator implementation for RevWalk.
  20.  * <p>
  21.  * This generator starts from a set of one or more commits and process them in
  22.  * descending (newest to oldest) commit time order. Commits automatically cause
  23.  * their parents to be enqueued for further processing, allowing the entire
  24.  * commit graph to be walked. A {@link RevFilter} may be used to select a subset
  25.  * of the commits and return them to the caller.
  26.  */
  27. class PendingGenerator extends Generator {
  28.     private static final int PARSED = RevWalk.PARSED;

  29.     private static final int SEEN = RevWalk.SEEN;

  30.     private static final int UNINTERESTING = RevWalk.UNINTERESTING;

  31.     /**
  32.      * Number of additional commits to scan after we think we are done.
  33.      * <p>
  34.      * This small buffer of commits is scanned to ensure we didn't miss anything
  35.      * as a result of clock skew when the commits were made. We need to set our
  36.      * constant to 1 additional commit due to the use of a pre-increment
  37.      * operator when accessing the value.
  38.      */
  39.     static final int OVER_SCAN = 5 + 1;

  40.     /** A commit near the end of time, to initialize {@link #last} with. */
  41.     private static final RevCommit INIT_LAST;

  42.     static {
  43.         INIT_LAST = new RevCommit(ObjectId.zeroId());
  44.         INIT_LAST.commitTime = Integer.MAX_VALUE;
  45.     }

  46.     private final RevWalk walker;

  47.     private final DateRevQueue pending;

  48.     private final RevFilter filter;

  49.     private final int output;

  50.     /** Last commit produced to the caller from {@link #next()}. */
  51.     private RevCommit last = INIT_LAST;

  52.     /**
  53.      * Number of commits we have remaining in our over-scan allotment.
  54.      * <p>
  55.      * Only relevant if there are {@link #UNINTERESTING} commits in the
  56.      * {@link #pending} queue.
  57.      */
  58.     private int overScan = OVER_SCAN;

  59.     boolean canDispose;

  60.     PendingGenerator(final RevWalk w, final DateRevQueue p,
  61.             final RevFilter f, final int out) {
  62.         super(w.isFirstParent());
  63.         walker = w;
  64.         pending = p;
  65.         filter = f;
  66.         output = out;
  67.         canDispose = true;
  68.     }

  69.     @Override
  70.     int outputType() {
  71.         return output | SORT_COMMIT_TIME_DESC;
  72.     }

  73.     @Override
  74.     RevCommit next() throws MissingObjectException,
  75.             IncorrectObjectTypeException, IOException {
  76.         try {
  77.             for (;;) {
  78.                 final RevCommit c = pending.next();
  79.                 if (c == null) {
  80.                     return null;
  81.                 }

  82.                 final boolean produce;
  83.                 if ((c.flags & UNINTERESTING) != 0)
  84.                     produce = false;
  85.                 else {
  86.                     if (filter.requiresCommitBody())
  87.                         c.parseBody(walker);
  88.                     produce = filter.include(walker, c);
  89.                 }

  90.                 for (int i = 0; i < c.parents.length; i++) {
  91.                     RevCommit p = c.parents[i];
  92.                     // If the commit is uninteresting, don't try to prune
  93.                     // parents because we want the maximal uninteresting set.
  94.                     if (firstParent && i > 0 && (c.flags & UNINTERESTING) == 0) {
  95.                         continue;
  96.                     }
  97.                     if ((p.flags & SEEN) != 0)
  98.                         continue;
  99.                     if ((p.flags & PARSED) == 0)
  100.                         p.parseHeaders(walker);
  101.                     p.flags |= SEEN;
  102.                     pending.add(p);
  103.                 }
  104.                 walker.carryFlagsImpl(c);

  105.                 if ((c.flags & UNINTERESTING) != 0) {
  106.                     if (pending.everbodyHasFlag(UNINTERESTING)) {
  107.                         final RevCommit n = pending.peek();
  108.                         if (n != null && n.commitTime >= last.commitTime) {
  109.                             // This is too close to call. The next commit we
  110.                             // would pop is dated after the last one produced.
  111.                             // We have to keep going to ensure that we carry
  112.                             // flags as much as necessary.
  113.                             //
  114.                             overScan = OVER_SCAN;
  115.                         } else if (--overScan == 0)
  116.                             throw StopWalkException.INSTANCE;
  117.                     } else {
  118.                         overScan = OVER_SCAN;
  119.                     }
  120.                     if (canDispose)
  121.                         c.disposeBody();
  122.                     continue;
  123.                 }

  124.                 if (produce)
  125.                     return last = c;
  126.                 else if (canDispose)
  127.                     c.disposeBody();
  128.             }
  129.         } catch (StopWalkException swe) {
  130.             pending.clear();
  131.             return null;
  132.         }
  133.     }
  134. }