RevWalk.java

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

  12. package org.eclipse.jgit.revwalk;

  13. import java.io.IOException;
  14. import java.text.MessageFormat;
  15. import java.util.ArrayList;
  16. import java.util.Collection;
  17. import java.util.EnumSet;
  18. import java.util.Iterator;
  19. import java.util.List;

  20. import org.eclipse.jgit.annotations.NonNull;
  21. import org.eclipse.jgit.annotations.Nullable;
  22. import org.eclipse.jgit.errors.CorruptObjectException;
  23. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  24. import org.eclipse.jgit.errors.LargeObjectException;
  25. import org.eclipse.jgit.errors.MissingObjectException;
  26. import org.eclipse.jgit.errors.RevWalkException;
  27. import org.eclipse.jgit.internal.JGitText;
  28. import org.eclipse.jgit.lib.AnyObjectId;
  29. import org.eclipse.jgit.lib.AsyncObjectLoaderQueue;
  30. import org.eclipse.jgit.lib.Constants;
  31. import org.eclipse.jgit.lib.MutableObjectId;
  32. import org.eclipse.jgit.lib.ObjectId;
  33. import org.eclipse.jgit.lib.ObjectIdOwnerMap;
  34. import org.eclipse.jgit.lib.ObjectLoader;
  35. import org.eclipse.jgit.lib.ObjectReader;
  36. import org.eclipse.jgit.lib.Repository;
  37. import org.eclipse.jgit.revwalk.filter.RevFilter;
  38. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  39. import org.eclipse.jgit.util.References;

  40. /**
  41.  * Walks a commit graph and produces the matching commits in order.
  42.  * <p>
  43.  * A RevWalk instance can only be used once to generate results. Running a
  44.  * second time requires creating a new RevWalk instance, or invoking
  45.  * {@link #reset()} before starting again. Resetting an existing instance may be
  46.  * faster for some applications as commit body parsing can be avoided on the
  47.  * later invocations.
  48.  * <p>
  49.  * RevWalk instances are not thread-safe. Applications must either restrict
  50.  * usage of a RevWalk instance to a single thread, or implement their own
  51.  * synchronization at a higher level.
  52.  * <p>
  53.  * Multiple simultaneous RevWalk instances per
  54.  * {@link org.eclipse.jgit.lib.Repository} are permitted, even from concurrent
  55.  * threads. Equality of {@link org.eclipse.jgit.revwalk.RevCommit}s from two
  56.  * different RevWalk instances is never true, even if their
  57.  * {@link org.eclipse.jgit.lib.ObjectId}s are equal (and thus they describe the
  58.  * same commit).
  59.  * <p>
  60.  * The offered iterator is over the list of RevCommits described by the
  61.  * configuration of this instance. Applications should restrict themselves to
  62.  * using either the provided Iterator or {@link #next()}, but never use both on
  63.  * the same RevWalk at the same time. The Iterator may buffer RevCommits, while
  64.  * {@link #next()} does not.
  65.  */
  66. public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
  67.     private static final int MB = 1 << 20;

  68.     /**
  69.      * Set on objects whose important header data has been loaded.
  70.      * <p>
  71.      * For a RevCommit this indicates we have pulled apart the tree and parent
  72.      * references from the raw bytes available in the repository and translated
  73.      * those to our own local RevTree and RevCommit instances. The raw buffer is
  74.      * also available for message and other header filtering.
  75.      * <p>
  76.      * For a RevTag this indicates we have pulled part the tag references to
  77.      * find out who the tag refers to, and what that object's type is.
  78.      */
  79.     static final int PARSED = 1 << 0;

  80.     /**
  81.      * Set on RevCommit instances added to our {@link #pending} queue.
  82.      * <p>
  83.      * We use this flag to avoid adding the same commit instance twice to our
  84.      * queue, especially if we reached it by more than one path.
  85.      */
  86.     static final int SEEN = 1 << 1;

  87.     /**
  88.      * Set on RevCommit instances the caller does not want output.
  89.      * <p>
  90.      * We flag commits as uninteresting if the caller does not want commits
  91.      * reachable from a commit given to {@link #markUninteresting(RevCommit)}.
  92.      * This flag is always carried into the commit's parents and is a key part
  93.      * of the "rev-list B --not A" feature; A is marked UNINTERESTING.
  94.      */
  95.     static final int UNINTERESTING = 1 << 2;

  96.     /**
  97.      * Set on a RevCommit that can collapse out of the history.
  98.      * <p>
  99.      * If the {@link #treeFilter} concluded that this commit matches his
  100.      * parents' for all of the paths that the filter is interested in then we
  101.      * mark the commit REWRITE. Later we can rewrite the parents of a REWRITE
  102.      * child to remove chains of REWRITE commits before we produce the child to
  103.      * the application.
  104.      *
  105.      * @see RewriteGenerator
  106.      */
  107.     static final int REWRITE = 1 << 3;

  108.     /**
  109.      * Temporary mark for use within generators or filters.
  110.      * <p>
  111.      * This mark is only for local use within a single scope. If someone sets
  112.      * the mark they must unset it before any other code can see the mark.
  113.      */
  114.     static final int TEMP_MARK = 1 << 4;

  115.     /**
  116.      * Temporary mark for use within {@link TopoSortGenerator}.
  117.      * <p>
  118.      * This mark indicates the commit could not produce when it wanted to, as at
  119.      * least one child was behind it. Commits with this flag are delayed until
  120.      * all children have been output first.
  121.      */
  122.     static final int TOPO_DELAY = 1 << 5;

  123.     /**
  124.      * Temporary mark for use within {@link TopoNonIntermixSortGenerator}.
  125.      * <p>
  126.      * This mark indicates the commit has been queued for emission in
  127.      * {@link TopoSortGenerator} and can be produced. This mark is removed when
  128.      * the commit has been produced.
  129.      */
  130.     static final int TOPO_QUEUED = 1 << 6;

  131.     /** Number of flag bits we keep internal for our own use. See above flags. */
  132.     static final int RESERVED_FLAGS = 7;

  133.     private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1);

  134.     final ObjectReader reader;

  135.     private final boolean closeReader;

  136.     final MutableObjectId idBuffer;

  137.     ObjectIdOwnerMap<RevObject> objects;

  138.     int freeFlags = APP_FLAGS;

  139.     private int delayFreeFlags;

  140.     private int retainOnReset;

  141.     int carryFlags = UNINTERESTING;

  142.     final ArrayList<RevCommit> roots;

  143.     AbstractRevQueue queue;

  144.     Generator pending;

  145.     private final EnumSet<RevSort> sorting;

  146.     private RevFilter filter;

  147.     private TreeFilter treeFilter;

  148.     private boolean retainBody = true;

  149.     private boolean rewriteParents = true;

  150.     private boolean firstParent;

  151.     boolean shallowCommitsInitialized;

  152.     /**
  153.      * Create a new revision walker for a given repository.
  154.      *
  155.      * @param repo
  156.      *            the repository the walker will obtain data from. An
  157.      *            ObjectReader will be created by the walker, and will be closed
  158.      *            when the walker is closed.
  159.      */
  160.     public RevWalk(Repository repo) {
  161.         this(repo.newObjectReader(), true);
  162.     }

  163.     /**
  164.      * Create a new revision walker for a given repository.
  165.      * <p>
  166.      *
  167.      * @param or
  168.      *            the reader the walker will obtain data from. The reader is not
  169.      *            closed when the walker is closed (but is closed by {@link
  170.      *            #dispose()}.
  171.      */
  172.     public RevWalk(ObjectReader or) {
  173.         this(or, false);
  174.     }

  175.     private RevWalk(ObjectReader or, boolean closeReader) {
  176.         reader = or;
  177.         idBuffer = new MutableObjectId();
  178.         objects = new ObjectIdOwnerMap<>();
  179.         roots = new ArrayList<>();
  180.         queue = new DateRevQueue(false);
  181.         pending = new StartGenerator(this);
  182.         sorting = EnumSet.of(RevSort.NONE);
  183.         filter = RevFilter.ALL;
  184.         treeFilter = TreeFilter.ALL;
  185.         this.closeReader = closeReader;
  186.     }

  187.     /**
  188.      * Get the reader this walker is using to load objects.
  189.      *
  190.      * @return the reader this walker is using to load objects.
  191.      */
  192.     public ObjectReader getObjectReader() {
  193.         return reader;
  194.     }

  195.     /**
  196.      * Get a reachability checker for commits over this revwalk.
  197.      *
  198.      * @return the most efficient reachability checker for this repository.
  199.      * @throws IOException
  200.      *             if it cannot open any of the underlying indices.
  201.      *
  202.      * @since 5.4
  203.      * @deprecated use {@code ObjectReader#createReachabilityChecker(RevWalk)}
  204.      *             instead.
  205.      */
  206.     @Deprecated
  207.     public final ReachabilityChecker createReachabilityChecker()
  208.             throws IOException {
  209.         return reader.createReachabilityChecker(this);
  210.     }

  211.     /**
  212.      * {@inheritDoc}
  213.      * <p>
  214.      * Release any resources used by this walker's reader.
  215.      * <p>
  216.      * A walker that has been released can be used again, but may need to be
  217.      * released after the subsequent usage.
  218.      *
  219.      * @since 4.0
  220.      */
  221.     @Override
  222.     public void close() {
  223.         if (closeReader) {
  224.             reader.close();
  225.         }
  226.     }

  227.     /**
  228.      * Mark a commit to start graph traversal from.
  229.      * <p>
  230.      * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain
  231.      * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as
  232.      * this method requires the commit to be parsed before it can be added as a
  233.      * root for the traversal.
  234.      * <p>
  235.      * The method will automatically parse an unparsed commit, but error
  236.      * handling may be more difficult for the application to explain why a
  237.      * RevCommit is not actually a commit. The object pool of this walker would
  238.      * also be 'poisoned' by the non-commit RevCommit.
  239.      *
  240.      * @param c
  241.      *            the commit to start traversing from. The commit passed must be
  242.      *            from this same revision walker.
  243.      * @throws org.eclipse.jgit.errors.MissingObjectException
  244.      *             the commit supplied is not available from the object
  245.      *             database. This usually indicates the supplied commit is
  246.      *             invalid, but the reference was constructed during an earlier
  247.      *             invocation to {@link #lookupCommit(AnyObjectId)}.
  248.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  249.      *             the object was not parsed yet and it was discovered during
  250.      *             parsing that it is not actually a commit. This usually
  251.      *             indicates the caller supplied a non-commit SHA-1 to
  252.      *             {@link #lookupCommit(AnyObjectId)}.
  253.      * @throws java.io.IOException
  254.      *             a pack file or loose object could not be read.
  255.      */
  256.     public void markStart(RevCommit c) throws MissingObjectException,
  257.             IncorrectObjectTypeException, IOException {
  258.         if ((c.flags & SEEN) != 0)
  259.             return;
  260.         if ((c.flags & PARSED) == 0)
  261.             c.parseHeaders(this);
  262.         c.flags |= SEEN;
  263.         roots.add(c);
  264.         queue.add(c);
  265.     }

  266.     /**
  267.      * Mark commits to start graph traversal from.
  268.      *
  269.      * @param list
  270.      *            commits to start traversing from. The commits passed must be
  271.      *            from this same revision walker.
  272.      * @throws org.eclipse.jgit.errors.MissingObjectException
  273.      *             one of the commits supplied is not available from the object
  274.      *             database. This usually indicates the supplied commit is
  275.      *             invalid, but the reference was constructed during an earlier
  276.      *             invocation to {@link #lookupCommit(AnyObjectId)}.
  277.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  278.      *             the object was not parsed yet and it was discovered during
  279.      *             parsing that it is not actually a commit. This usually
  280.      *             indicates the caller supplied a non-commit SHA-1 to
  281.      *             {@link #lookupCommit(AnyObjectId)}.
  282.      * @throws java.io.IOException
  283.      *             a pack file or loose object could not be read.
  284.      */
  285.     public void markStart(Collection<RevCommit> list)
  286.             throws MissingObjectException, IncorrectObjectTypeException,
  287.             IOException {
  288.         for (RevCommit c : list)
  289.             markStart(c);
  290.     }

  291.     /**
  292.      * Mark a commit to not produce in the output.
  293.      * <p>
  294.      * Uninteresting commits denote not just themselves but also their entire
  295.      * ancestry chain, back until the merge base of an uninteresting commit and
  296.      * an otherwise interesting commit.
  297.      * <p>
  298.      * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain
  299.      * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as
  300.      * this method requires the commit to be parsed before it can be added as a
  301.      * root for the traversal.
  302.      * <p>
  303.      * The method will automatically parse an unparsed commit, but error
  304.      * handling may be more difficult for the application to explain why a
  305.      * RevCommit is not actually a commit. The object pool of this walker would
  306.      * also be 'poisoned' by the non-commit RevCommit.
  307.      *
  308.      * @param c
  309.      *            the commit to start traversing from. The commit passed must be
  310.      *            from this same revision walker.
  311.      * @throws org.eclipse.jgit.errors.MissingObjectException
  312.      *             the commit supplied is not available from the object
  313.      *             database. This usually indicates the supplied commit is
  314.      *             invalid, but the reference was constructed during an earlier
  315.      *             invocation to {@link #lookupCommit(AnyObjectId)}.
  316.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  317.      *             the object was not parsed yet and it was discovered during
  318.      *             parsing that it is not actually a commit. This usually
  319.      *             indicates the caller supplied a non-commit SHA-1 to
  320.      *             {@link #lookupCommit(AnyObjectId)}.
  321.      * @throws java.io.IOException
  322.      *             a pack file or loose object could not be read.
  323.      */
  324.     public void markUninteresting(RevCommit c)
  325.             throws MissingObjectException, IncorrectObjectTypeException,
  326.             IOException {
  327.         c.flags |= UNINTERESTING;
  328.         carryFlagsImpl(c);
  329.         markStart(c);
  330.     }

  331.     /**
  332.      * Determine if a commit is reachable from another commit.
  333.      * <p>
  334.      * A commit <code>base</code> is an ancestor of <code>tip</code> if we
  335.      * can find a path of commits that leads from <code>tip</code> and ends at
  336.      * <code>base</code>.
  337.      * <p>
  338.      * This utility function resets the walker, inserts the two supplied
  339.      * commits, and then executes a walk until an answer can be obtained.
  340.      * Currently allocated RevFlags that have been added to RevCommit instances
  341.      * will be retained through the reset.
  342.      *
  343.      * @param base
  344.      *            commit the caller thinks is reachable from <code>tip</code>.
  345.      * @param tip
  346.      *            commit to start iteration from, and which is most likely a
  347.      *            descendant (child) of <code>base</code>.
  348.      * @return true if there is a path directly from <code>tip</code> to
  349.      *         <code>base</code> (and thus <code>base</code> is fully merged
  350.      *         into <code>tip</code>); false otherwise.
  351.      * @throws org.eclipse.jgit.errors.MissingObjectException
  352.      *             one or more of the next commit's parents are not available
  353.      *             from the object database, but were thought to be candidates
  354.      *             for traversal. This usually indicates a broken link.
  355.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  356.      *             one or more of the next commit's parents are not actually
  357.      *             commit objects.
  358.      * @throws java.io.IOException
  359.      *             a pack file or loose object could not be read.
  360.      */
  361.     public boolean isMergedInto(RevCommit base, RevCommit tip)
  362.             throws MissingObjectException, IncorrectObjectTypeException,
  363.             IOException {
  364.         final RevFilter oldRF = filter;
  365.         final TreeFilter oldTF = treeFilter;
  366.         try {
  367.             finishDelayedFreeFlags();
  368.             reset(~freeFlags & APP_FLAGS);
  369.             filter = RevFilter.MERGE_BASE;
  370.             treeFilter = TreeFilter.ALL;
  371.             markStart(tip);
  372.             markStart(base);
  373.             RevCommit mergeBase;
  374.             while ((mergeBase = next()) != null) {
  375.                 if (References.isSameObject(mergeBase, base)) {
  376.                     return true;
  377.                 }
  378.             }
  379.             return false;
  380.         } finally {
  381.             filter = oldRF;
  382.             treeFilter = oldTF;
  383.         }
  384.     }

  385.     /**
  386.      * Pop the next most recent commit.
  387.      *
  388.      * @return next most recent commit; null if traversal is over.
  389.      * @throws org.eclipse.jgit.errors.MissingObjectException
  390.      *             one or more of the next commit's parents are not available
  391.      *             from the object database, but were thought to be candidates
  392.      *             for traversal. This usually indicates a broken link.
  393.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  394.      *             one or more of the next commit's parents are not actually
  395.      *             commit objects.
  396.      * @throws java.io.IOException
  397.      *             a pack file or loose object could not be read.
  398.      */
  399.     public RevCommit next() throws MissingObjectException,
  400.             IncorrectObjectTypeException, IOException {
  401.         return pending.next();
  402.     }

  403.     /**
  404.      * Obtain the sort types applied to the commits returned.
  405.      *
  406.      * @return the sorting strategies employed. At least one strategy is always
  407.      *         used, but that strategy may be
  408.      *         {@link org.eclipse.jgit.revwalk.RevSort#NONE}.
  409.      */
  410.     public EnumSet<RevSort> getRevSort() {
  411.         return sorting.clone();
  412.     }

  413.     /**
  414.      * Check whether the provided sorting strategy is enabled.
  415.      *
  416.      * @param sort
  417.      *            a sorting strategy to look for.
  418.      * @return true if this strategy is enabled, false otherwise
  419.      */
  420.     public boolean hasRevSort(RevSort sort) {
  421.         return sorting.contains(sort);
  422.     }

  423.     /**
  424.      * Select a single sorting strategy for the returned commits.
  425.      * <p>
  426.      * Disables all sorting strategies, then enables only the single strategy
  427.      * supplied by the caller.
  428.      *
  429.      * @param s
  430.      *            a sorting strategy to enable.
  431.      */
  432.     public void sort(RevSort s) {
  433.         assertNotStarted();
  434.         sorting.clear();
  435.         sorting.add(s);
  436.     }

  437.     /**
  438.      * Add or remove a sorting strategy for the returned commits.
  439.      * <p>
  440.      * Multiple strategies can be applied at once, in which case some strategies
  441.      * may take precedence over others. As an example,
  442.      * {@link org.eclipse.jgit.revwalk.RevSort#TOPO} must take precedence over
  443.      * {@link org.eclipse.jgit.revwalk.RevSort#COMMIT_TIME_DESC}, otherwise it
  444.      * cannot enforce its ordering.
  445.      *
  446.      * @param s
  447.      *            a sorting strategy to enable or disable.
  448.      * @param use
  449.      *            true if this strategy should be used, false if it should be
  450.      *            removed.
  451.      */
  452.     public void sort(RevSort s, boolean use) {
  453.         assertNotStarted();
  454.         if (use)
  455.             sorting.add(s);
  456.         else
  457.             sorting.remove(s);

  458.         if (sorting.size() > 1)
  459.             sorting.remove(RevSort.NONE);
  460.         else if (sorting.isEmpty())
  461.             sorting.add(RevSort.NONE);
  462.     }

  463.     /**
  464.      * Get the currently configured commit filter.
  465.      *
  466.      * @return the current filter. Never null as a filter is always needed.
  467.      */
  468.     @NonNull
  469.     public RevFilter getRevFilter() {
  470.         return filter;
  471.     }

  472.     /**
  473.      * Set the commit filter for this walker.
  474.      * <p>
  475.      * Multiple filters may be combined by constructing an arbitrary tree of
  476.      * <code>AndRevFilter</code> or <code>OrRevFilter</code> instances to
  477.      * describe the boolean expression required by the application. Custom
  478.      * filter implementations may also be constructed by applications.
  479.      * <p>
  480.      * Note that filters are not thread-safe and may not be shared by concurrent
  481.      * RevWalk instances. Every RevWalk must be supplied its own unique filter,
  482.      * unless the filter implementation specifically states it is (and always
  483.      * will be) thread-safe. Callers may use
  484.      * {@link org.eclipse.jgit.revwalk.filter.RevFilter#clone()} to create a
  485.      * unique filter tree for this RevWalk instance.
  486.      *
  487.      * @param newFilter
  488.      *            the new filter. If null the special
  489.      *            {@link org.eclipse.jgit.revwalk.filter.RevFilter#ALL} filter
  490.      *            will be used instead, as it matches every commit.
  491.      * @see org.eclipse.jgit.revwalk.filter.AndRevFilter
  492.      * @see org.eclipse.jgit.revwalk.filter.OrRevFilter
  493.      */
  494.     public void setRevFilter(RevFilter newFilter) {
  495.         assertNotStarted();
  496.         filter = newFilter != null ? newFilter : RevFilter.ALL;
  497.     }

  498.     /**
  499.      * Get the tree filter used to simplify commits by modified paths.
  500.      *
  501.      * @return the current filter. Never null as a filter is always needed. If
  502.      *         no filter is being applied
  503.      *         {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} is
  504.      *         returned.
  505.      */
  506.     @NonNull
  507.     public TreeFilter getTreeFilter() {
  508.         return treeFilter;
  509.     }

  510.     /**
  511.      * Set the tree filter used to simplify commits by modified paths.
  512.      * <p>
  513.      * If null or {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} the
  514.      * path limiter is removed. Commits will not be simplified.
  515.      * <p>
  516.      * If non-null and not
  517.      * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} then the tree
  518.      * filter will be installed. Commits will have their ancestry simplified to
  519.      * hide commits that do not contain tree entries matched by the filter,
  520.      * unless {@code setRewriteParents(false)} is called.
  521.      * <p>
  522.      * Usually callers should be inserting a filter graph including
  523.      * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ANY_DIFF} along with
  524.      * one or more {@link org.eclipse.jgit.treewalk.filter.PathFilter}
  525.      * instances.
  526.      *
  527.      * @param newFilter
  528.      *            new filter. If null the special
  529.      *            {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} filter
  530.      *            will be used instead, as it matches everything.
  531.      * @see org.eclipse.jgit.treewalk.filter.PathFilter
  532.      */
  533.     public void setTreeFilter(TreeFilter newFilter) {
  534.         assertNotStarted();
  535.         treeFilter = newFilter != null ? newFilter : TreeFilter.ALL;
  536.     }

  537.     /**
  538.      * Set whether to rewrite parent pointers when filtering by modified paths.
  539.      * <p>
  540.      * By default, when {@link #setTreeFilter(TreeFilter)} is called with non-
  541.      * null and non-{@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL}
  542.      * filter, commits will have their ancestry simplified and parents rewritten
  543.      * to hide commits that do not match the filter.
  544.      * <p>
  545.      * This behavior can be bypassed by passing false to this method.
  546.      *
  547.      * @param rewrite
  548.      *            whether to rewrite parents; defaults to true.
  549.      * @since 3.4
  550.      */
  551.     public void setRewriteParents(boolean rewrite) {
  552.         rewriteParents = rewrite;
  553.     }

  554.     boolean getRewriteParents() {
  555.         return rewriteParents;
  556.     }

  557.     /**
  558.      * Should the body of a commit or tag be retained after parsing its headers?
  559.      * <p>
  560.      * Usually the body is always retained, but some application code might not
  561.      * care and would prefer to discard the body of a commit as early as
  562.      * possible, to reduce memory usage.
  563.      * <p>
  564.      * True by default on {@link org.eclipse.jgit.revwalk.RevWalk} and false by
  565.      * default for {@link org.eclipse.jgit.revwalk.ObjectWalk}.
  566.      *
  567.      * @return true if the body should be retained; false it is discarded.
  568.      */
  569.     public boolean isRetainBody() {
  570.         return retainBody;
  571.     }

  572.     /**
  573.      * Set whether or not the body of a commit or tag is retained.
  574.      * <p>
  575.      * If a body of a commit or tag is not retained, the application must call
  576.      * {@link #parseBody(RevObject)} before the body can be safely accessed
  577.      * through the type specific access methods.
  578.      * <p>
  579.      * True by default on {@link org.eclipse.jgit.revwalk.RevWalk} and false by
  580.      * default for {@link org.eclipse.jgit.revwalk.ObjectWalk}.
  581.      *
  582.      * @param retain
  583.      *            true to retain bodies; false to discard them early.
  584.      */
  585.     public void setRetainBody(boolean retain) {
  586.         retainBody = retain;
  587.     }

  588.     /**
  589.      * @return whether only first-parent links should be followed when walking.
  590.      *
  591.      * @since 5.5
  592.      */
  593.     public boolean isFirstParent() {
  594.         return firstParent;
  595.     }

  596.     /**
  597.      * Set whether or not only first parent links should be followed.
  598.      * <p>
  599.      * If set, second- and higher-parent links are not traversed at all.
  600.      * <p>
  601.      * This must be called prior to {@link #markStart(RevCommit)}.
  602.      *
  603.      * @param enable
  604.      *            true to walk only first-parent links.
  605.      *
  606.      * @since 5.5
  607.      */
  608.     public void setFirstParent(boolean enable) {
  609.         assertNotStarted();
  610.         assertNoCommitsMarkedStart();
  611.         firstParent = enable;
  612.         queue = new DateRevQueue(firstParent);
  613.         pending = new StartGenerator(this);
  614.     }

  615.     /**
  616.      * Locate a reference to a blob without loading it.
  617.      * <p>
  618.      * The blob may or may not exist in the repository. It is impossible to tell
  619.      * from this method's return value.
  620.      *
  621.      * @param id
  622.      *            name of the blob object.
  623.      * @return reference to the blob object. Never null.
  624.      */
  625.     @NonNull
  626.     public RevBlob lookupBlob(AnyObjectId id) {
  627.         RevBlob c = (RevBlob) objects.get(id);
  628.         if (c == null) {
  629.             c = new RevBlob(id);
  630.             objects.add(c);
  631.         }
  632.         return c;
  633.     }

  634.     /**
  635.      * Locate a reference to a tree without loading it.
  636.      * <p>
  637.      * The tree may or may not exist in the repository. It is impossible to tell
  638.      * from this method's return value.
  639.      *
  640.      * @param id
  641.      *            name of the tree object.
  642.      * @return reference to the tree object. Never null.
  643.      */
  644.     @NonNull
  645.     public RevTree lookupTree(AnyObjectId id) {
  646.         RevTree c = (RevTree) objects.get(id);
  647.         if (c == null) {
  648.             c = new RevTree(id);
  649.             objects.add(c);
  650.         }
  651.         return c;
  652.     }

  653.     /**
  654.      * Locate a reference to a commit without loading it.
  655.      * <p>
  656.      * The commit may or may not exist in the repository. It is impossible to
  657.      * tell from this method's return value.
  658.      * <p>
  659.      * See {@link #parseHeaders(RevObject)} and {@link #parseBody(RevObject)}
  660.      * for loading contents.
  661.      *
  662.      * @param id
  663.      *            name of the commit object.
  664.      * @return reference to the commit object. Never null.
  665.      */
  666.     @NonNull
  667.     public RevCommit lookupCommit(AnyObjectId id) {
  668.         RevCommit c = (RevCommit) objects.get(id);
  669.         if (c == null) {
  670.             c = createCommit(id);
  671.             objects.add(c);
  672.         }
  673.         return c;
  674.     }

  675.     /**
  676.      * Locate a reference to a tag without loading it.
  677.      * <p>
  678.      * The tag may or may not exist in the repository. It is impossible to tell
  679.      * from this method's return value.
  680.      *
  681.      * @param id
  682.      *            name of the tag object.
  683.      * @return reference to the tag object. Never null.
  684.      */
  685.     @NonNull
  686.     public RevTag lookupTag(AnyObjectId id) {
  687.         RevTag c = (RevTag) objects.get(id);
  688.         if (c == null) {
  689.             c = new RevTag(id);
  690.             objects.add(c);
  691.         }
  692.         return c;
  693.     }

  694.     /**
  695.      * Locate a reference to any object without loading it.
  696.      * <p>
  697.      * The object may or may not exist in the repository. It is impossible to
  698.      * tell from this method's return value.
  699.      *
  700.      * @param id
  701.      *            name of the object.
  702.      * @param type
  703.      *            type of the object. Must be a valid Git object type.
  704.      * @return reference to the object. Never null.
  705.      */
  706.     @NonNull
  707.     public RevObject lookupAny(AnyObjectId id, int type) {
  708.         RevObject r = objects.get(id);
  709.         if (r == null) {
  710.             switch (type) {
  711.             case Constants.OBJ_COMMIT:
  712.                 r = createCommit(id);
  713.                 break;
  714.             case Constants.OBJ_TREE:
  715.                 r = new RevTree(id);
  716.                 break;
  717.             case Constants.OBJ_BLOB:
  718.                 r = new RevBlob(id);
  719.                 break;
  720.             case Constants.OBJ_TAG:
  721.                 r = new RevTag(id);
  722.                 break;
  723.             default:
  724.                 throw new IllegalArgumentException(MessageFormat.format(
  725.                         JGitText.get().invalidGitType, Integer.valueOf(type)));
  726.             }
  727.             objects.add(r);
  728.         }
  729.         return r;
  730.     }

  731.     /**
  732.      * Locate an object that was previously allocated in this walk.
  733.      *
  734.      * @param id
  735.      *            name of the object.
  736.      * @return reference to the object if it has been previously located;
  737.      *         otherwise null.
  738.      */
  739.     public RevObject lookupOrNull(AnyObjectId id) {
  740.         return objects.get(id);
  741.     }

  742.     /**
  743.      * Locate a reference to a commit and immediately parse its content.
  744.      * <p>
  745.      * Unlike {@link #lookupCommit(AnyObjectId)} this method only returns
  746.      * successfully if the commit object exists, is verified to be a commit, and
  747.      * was parsed without error.
  748.      *
  749.      * @param id
  750.      *            name of the commit object.
  751.      * @return reference to the commit object. Never null.
  752.      * @throws org.eclipse.jgit.errors.MissingObjectException
  753.      *             the supplied commit does not exist.
  754.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  755.      *             the supplied id is not a commit or an annotated tag.
  756.      * @throws java.io.IOException
  757.      *             a pack file or loose object could not be read.
  758.      */
  759.     @NonNull
  760.     public RevCommit parseCommit(AnyObjectId id)
  761.             throws MissingObjectException, IncorrectObjectTypeException,
  762.             IOException {
  763.         RevObject c = peel(parseAny(id));
  764.         if (!(c instanceof RevCommit))
  765.             throw new IncorrectObjectTypeException(id.toObjectId(),
  766.                     Constants.TYPE_COMMIT);
  767.         return (RevCommit) c;
  768.     }

  769.     /**
  770.      * Locate a reference to a tree.
  771.      * <p>
  772.      * This method only returns successfully if the tree object exists, is
  773.      * verified to be a tree.
  774.      *
  775.      * @param id
  776.      *            name of the tree object, or a commit or annotated tag that may
  777.      *            reference a tree.
  778.      * @return reference to the tree object. Never null.
  779.      * @throws org.eclipse.jgit.errors.MissingObjectException
  780.      *             the supplied tree does not exist.
  781.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  782.      *             the supplied id is not a tree, a commit or an annotated tag.
  783.      * @throws java.io.IOException
  784.      *             a pack file or loose object could not be read.
  785.      */
  786.     @NonNull
  787.     public RevTree parseTree(AnyObjectId id)
  788.             throws MissingObjectException, IncorrectObjectTypeException,
  789.             IOException {
  790.         RevObject c = peel(parseAny(id));

  791.         final RevTree t;
  792.         if (c instanceof RevCommit)
  793.             t = ((RevCommit) c).getTree();
  794.         else if (!(c instanceof RevTree))
  795.             throw new IncorrectObjectTypeException(id.toObjectId(),
  796.                     Constants.TYPE_TREE);
  797.         else
  798.             t = (RevTree) c;
  799.         parseHeaders(t);
  800.         return t;
  801.     }

  802.     /**
  803.      * Locate a reference to an annotated tag and immediately parse its content.
  804.      * <p>
  805.      * Unlike {@link #lookupTag(AnyObjectId)} this method only returns
  806.      * successfully if the tag object exists, is verified to be a tag, and was
  807.      * parsed without error.
  808.      *
  809.      * @param id
  810.      *            name of the tag object.
  811.      * @return reference to the tag object. Never null.
  812.      * @throws org.eclipse.jgit.errors.MissingObjectException
  813.      *             the supplied tag does not exist.
  814.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  815.      *             the supplied id is not a tag or an annotated tag.
  816.      * @throws java.io.IOException
  817.      *             a pack file or loose object could not be read.
  818.      */
  819.     @NonNull
  820.     public RevTag parseTag(AnyObjectId id) throws MissingObjectException,
  821.             IncorrectObjectTypeException, IOException {
  822.         RevObject c = parseAny(id);
  823.         if (!(c instanceof RevTag))
  824.             throw new IncorrectObjectTypeException(id.toObjectId(),
  825.                     Constants.TYPE_TAG);
  826.         return (RevTag) c;
  827.     }

  828.     /**
  829.      * Locate a reference to any object and immediately parse its headers.
  830.      * <p>
  831.      * This method only returns successfully if the object exists and was parsed
  832.      * without error. Parsing an object can be expensive as the type must be
  833.      * determined. For blobs this may mean the blob content was unpacked
  834.      * unnecessarily, and thrown away.
  835.      *
  836.      * @param id
  837.      *            name of the object.
  838.      * @return reference to the object. Never null.
  839.      * @throws org.eclipse.jgit.errors.MissingObjectException
  840.      *             the supplied does not exist.
  841.      * @throws java.io.IOException
  842.      *             a pack file or loose object could not be read.
  843.      */
  844.     @NonNull
  845.     public RevObject parseAny(AnyObjectId id)
  846.             throws MissingObjectException, IOException {
  847.         RevObject r = objects.get(id);
  848.         if (r == null)
  849.             r = parseNew(id, reader.open(id));
  850.         else
  851.             parseHeaders(r);
  852.         return r;
  853.     }

  854.     private RevObject parseNew(AnyObjectId id, ObjectLoader ldr)
  855.             throws LargeObjectException, CorruptObjectException,
  856.             MissingObjectException, IOException {
  857.         RevObject r;
  858.         int type = ldr.getType();
  859.         switch (type) {
  860.         case Constants.OBJ_COMMIT: {
  861.             final RevCommit c = createCommit(id);
  862.             c.parseCanonical(this, getCachedBytes(c, ldr));
  863.             r = c;
  864.             break;
  865.         }
  866.         case Constants.OBJ_TREE: {
  867.             r = new RevTree(id);
  868.             r.flags |= PARSED;
  869.             break;
  870.         }
  871.         case Constants.OBJ_BLOB: {
  872.             r = new RevBlob(id);
  873.             r.flags |= PARSED;
  874.             break;
  875.         }
  876.         case Constants.OBJ_TAG: {
  877.             final RevTag t = new RevTag(id);
  878.             t.parseCanonical(this, getCachedBytes(t, ldr));
  879.             r = t;
  880.             break;
  881.         }
  882.         default:
  883.             throw new IllegalArgumentException(MessageFormat.format(
  884.                     JGitText.get().badObjectType, Integer.valueOf(type)));
  885.         }
  886.         objects.add(r);
  887.         return r;
  888.     }

  889.     byte[] getCachedBytes(RevObject obj) throws LargeObjectException,
  890.             MissingObjectException, IncorrectObjectTypeException, IOException {
  891.         return getCachedBytes(obj, reader.open(obj, obj.getType()));
  892.     }

  893.     byte[] getCachedBytes(RevObject obj, ObjectLoader ldr)
  894.             throws LargeObjectException, MissingObjectException, IOException {
  895.         try {
  896.             return ldr.getCachedBytes(5 * MB);
  897.         } catch (LargeObjectException tooBig) {
  898.             tooBig.setObjectId(obj);
  899.             throw tooBig;
  900.         }
  901.     }

  902.     /**
  903.      * Asynchronous object parsing.
  904.      *
  905.      * @param objectIds
  906.      *            objects to open from the object store. The supplied collection
  907.      *            must not be modified until the queue has finished.
  908.      * @param reportMissing
  909.      *            if true missing objects are reported by calling failure with a
  910.      *            MissingObjectException. This may be more expensive for the
  911.      *            implementation to guarantee. If false the implementation may
  912.      *            choose to report MissingObjectException, or silently skip over
  913.      *            the object with no warning.
  914.      * @return queue to read the objects from.
  915.      */
  916.     public <T extends ObjectId> AsyncRevObjectQueue parseAny(
  917.             Iterable<T> objectIds, boolean reportMissing) {
  918.         List<T> need = new ArrayList<>();
  919.         List<RevObject> have = new ArrayList<>();
  920.         for (T id : objectIds) {
  921.             RevObject r = objects.get(id);
  922.             if (r != null && (r.flags & PARSED) != 0)
  923.                 have.add(r);
  924.             else
  925.                 need.add(id);
  926.         }

  927.         final Iterator<RevObject> objItr = have.iterator();
  928.         if (need.isEmpty()) {
  929.             return new AsyncRevObjectQueue() {
  930.                 @Override
  931.                 public RevObject next() {
  932.                     return objItr.hasNext() ? objItr.next() : null;
  933.                 }

  934.                 @Override
  935.                 public boolean cancel(boolean mayInterruptIfRunning) {
  936.                     return true;
  937.                 }

  938.                 @Override
  939.                 public void release() {
  940.                     // In-memory only, no action required.
  941.                 }
  942.             };
  943.         }

  944.         final AsyncObjectLoaderQueue<T> lItr = reader.open(need, reportMissing);
  945.         return new AsyncRevObjectQueue() {
  946.             @Override
  947.             public RevObject next() throws MissingObjectException,
  948.                     IncorrectObjectTypeException, IOException {
  949.                 if (objItr.hasNext())
  950.                     return objItr.next();
  951.                 if (!lItr.next())
  952.                     return null;

  953.                 ObjectId id = lItr.getObjectId();
  954.                 ObjectLoader ldr = lItr.open();
  955.                 RevObject r = objects.get(id);
  956.                 if (r == null)
  957.                     r = parseNew(id, ldr);
  958.                 else if (r instanceof RevCommit) {
  959.                     byte[] raw = ldr.getCachedBytes();
  960.                     ((RevCommit) r).parseCanonical(RevWalk.this, raw);
  961.                 } else if (r instanceof RevTag) {
  962.                     byte[] raw = ldr.getCachedBytes();
  963.                     ((RevTag) r).parseCanonical(RevWalk.this, raw);
  964.                 } else
  965.                     r.flags |= PARSED;
  966.                 return r;
  967.             }

  968.             @Override
  969.             public boolean cancel(boolean mayInterruptIfRunning) {
  970.                 return lItr.cancel(mayInterruptIfRunning);
  971.             }

  972.             @Override
  973.             public void release() {
  974.                 lItr.release();
  975.             }
  976.         };
  977.     }

  978.     /**
  979.      * Ensure the object's critical headers have been parsed.
  980.      * <p>
  981.      * This method only returns successfully if the object exists and was parsed
  982.      * without error.
  983.      *
  984.      * @param obj
  985.      *            the object the caller needs to be parsed.
  986.      * @throws org.eclipse.jgit.errors.MissingObjectException
  987.      *             the supplied does not exist.
  988.      * @throws java.io.IOException
  989.      *             a pack file or loose object could not be read.
  990.      */
  991.     public void parseHeaders(RevObject obj)
  992.             throws MissingObjectException, IOException {
  993.         if ((obj.flags & PARSED) == 0)
  994.             obj.parseHeaders(this);
  995.     }

  996.     /**
  997.      * Ensure the object's full body content is available.
  998.      * <p>
  999.      * This method only returns successfully if the object exists and was parsed
  1000.      * without error.
  1001.      *
  1002.      * @param obj
  1003.      *            the object the caller needs to be parsed.
  1004.      * @throws org.eclipse.jgit.errors.MissingObjectException
  1005.      *             the supplied does not exist.
  1006.      * @throws java.io.IOException
  1007.      *             a pack file or loose object could not be read.
  1008.      */
  1009.     public void parseBody(RevObject obj)
  1010.             throws MissingObjectException, IOException {
  1011.         obj.parseBody(this);
  1012.     }

  1013.     /**
  1014.      * Peel back annotated tags until a non-tag object is found.
  1015.      *
  1016.      * @param obj
  1017.      *            the starting object.
  1018.      * @return If {@code obj} is not an annotated tag, {@code obj}. Otherwise
  1019.      *         the first non-tag object that {@code obj} references. The
  1020.      *         returned object's headers have been parsed.
  1021.      * @throws org.eclipse.jgit.errors.MissingObjectException
  1022.      *             a referenced object cannot be found.
  1023.      * @throws java.io.IOException
  1024.      *             a pack file or loose object could not be read.
  1025.      */
  1026.     public RevObject peel(RevObject obj) throws MissingObjectException,
  1027.             IOException {
  1028.         while (obj instanceof RevTag) {
  1029.             parseHeaders(obj);
  1030.             obj = ((RevTag) obj).getObject();
  1031.         }
  1032.         parseHeaders(obj);
  1033.         return obj;
  1034.     }

  1035.     /**
  1036.      * Create a new flag for application use during walking.
  1037.      * <p>
  1038.      * Applications are only assured to be able to create 24 unique flags on any
  1039.      * given revision walker instance. Any flags beyond 24 are offered only if
  1040.      * the implementation has extra free space within its internal storage.
  1041.      *
  1042.      * @param name
  1043.      *            description of the flag, primarily useful for debugging.
  1044.      * @return newly constructed flag instance.
  1045.      * @throws java.lang.IllegalArgumentException
  1046.      *             too many flags have been reserved on this revision walker.
  1047.      */
  1048.     public RevFlag newFlag(String name) {
  1049.         final int m = allocFlag();
  1050.         return new RevFlag(this, name, m);
  1051.     }

  1052.     int allocFlag() {
  1053.         if (freeFlags == 0)
  1054.             throw new IllegalArgumentException(MessageFormat.format(
  1055.                     JGitText.get().flagsAlreadyCreated,
  1056.                     Integer.valueOf(32 - RESERVED_FLAGS)));
  1057.         final int m = Integer.lowestOneBit(freeFlags);
  1058.         freeFlags &= ~m;
  1059.         return m;
  1060.     }

  1061.     /**
  1062.      * Automatically carry a flag from a child commit to its parents.
  1063.      * <p>
  1064.      * A carried flag is copied from the child commit onto its parents when the
  1065.      * child commit is popped from the lowest level of walk's internal graph.
  1066.      *
  1067.      * @param flag
  1068.      *            the flag to carry onto parents, if set on a descendant.
  1069.      */
  1070.     public void carry(RevFlag flag) {
  1071.         if ((freeFlags & flag.mask) != 0)
  1072.             throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name));
  1073.         if (flag.walker != this)
  1074.             throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name));
  1075.         carryFlags |= flag.mask;
  1076.     }

  1077.     /**
  1078.      * Automatically carry flags from a child commit to its parents.
  1079.      * <p>
  1080.      * A carried flag is copied from the child commit onto its parents when the
  1081.      * child commit is popped from the lowest level of walk's internal graph.
  1082.      *
  1083.      * @param set
  1084.      *            the flags to carry onto parents, if set on a descendant.
  1085.      */
  1086.     public void carry(Collection<RevFlag> set) {
  1087.         for (RevFlag flag : set)
  1088.             carry(flag);
  1089.     }

  1090.     /**
  1091.      * Preserve a RevFlag during all {@code reset} methods.
  1092.      * <p>
  1093.      * Calling {@code retainOnReset(flag)} avoids needing to pass the flag
  1094.      * during each {@code resetRetain()} invocation on this instance.
  1095.      * <p>
  1096.      * Clearing flags marked retainOnReset requires disposing of the flag with
  1097.      * {@code #disposeFlag(RevFlag)} or disposing of the entire RevWalk by
  1098.      * {@code #dispose()}.
  1099.      *
  1100.      * @param flag
  1101.      *            the flag to retain during all resets.
  1102.      * @since 3.6
  1103.      */
  1104.     public final void retainOnReset(RevFlag flag) {
  1105.         if ((freeFlags & flag.mask) != 0)
  1106.             throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name));
  1107.         if (flag.walker != this)
  1108.             throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name));
  1109.         retainOnReset |= flag.mask;
  1110.     }

  1111.     /**
  1112.      * Preserve a set of RevFlags during all {@code reset} methods.
  1113.      * <p>
  1114.      * Calling {@code retainOnReset(set)} avoids needing to pass the flags
  1115.      * during each {@code resetRetain()} invocation on this instance.
  1116.      * <p>
  1117.      * Clearing flags marked retainOnReset requires disposing of the flag with
  1118.      * {@code #disposeFlag(RevFlag)} or disposing of the entire RevWalk by
  1119.      * {@code #dispose()}.
  1120.      *
  1121.      * @param flags
  1122.      *            the flags to retain during all resets.
  1123.      * @since 3.6
  1124.      */
  1125.     public final void retainOnReset(Collection<RevFlag> flags) {
  1126.         for (RevFlag f : flags)
  1127.             retainOnReset(f);
  1128.     }

  1129.     /**
  1130.      * Allow a flag to be recycled for a different use.
  1131.      * <p>
  1132.      * Recycled flags always come back as a different Java object instance when
  1133.      * assigned again by {@link #newFlag(String)}.
  1134.      * <p>
  1135.      * If the flag was previously being carried, the carrying request is
  1136.      * removed. Disposing of a carried flag while a traversal is in progress has
  1137.      * an undefined behavior.
  1138.      *
  1139.      * @param flag
  1140.      *            the to recycle.
  1141.      */
  1142.     public void disposeFlag(RevFlag flag) {
  1143.         freeFlag(flag.mask);
  1144.     }

  1145.     void freeFlag(int mask) {
  1146.         retainOnReset &= ~mask;
  1147.         if (isNotStarted()) {
  1148.             freeFlags |= mask;
  1149.             carryFlags &= ~mask;
  1150.         } else {
  1151.             delayFreeFlags |= mask;
  1152.         }
  1153.     }

  1154.     private void finishDelayedFreeFlags() {
  1155.         if (delayFreeFlags != 0) {
  1156.             freeFlags |= delayFreeFlags;
  1157.             carryFlags &= ~delayFreeFlags;
  1158.             delayFreeFlags = 0;
  1159.         }
  1160.     }

  1161.     /**
  1162.      * Resets internal state and allows this instance to be used again.
  1163.      * <p>
  1164.      * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
  1165.      * instances are not invalidated. RevFlag instances are not invalidated, but
  1166.      * are removed from all RevObjects.
  1167.      */
  1168.     public final void reset() {
  1169.         reset(0);
  1170.     }

  1171.     /**
  1172.      * Resets internal state and allows this instance to be used again.
  1173.      * <p>
  1174.      * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
  1175.      * instances are not invalidated. RevFlag instances are not invalidated, but
  1176.      * are removed from all RevObjects.
  1177.      *
  1178.      * @param retainFlags
  1179.      *            application flags that should <b>not</b> be cleared from
  1180.      *            existing commit objects.
  1181.      */
  1182.     public final void resetRetain(RevFlagSet retainFlags) {
  1183.         reset(retainFlags.mask);
  1184.     }

  1185.     /**
  1186.      * Resets internal state and allows this instance to be used again.
  1187.      * <p>
  1188.      * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
  1189.      * instances are not invalidated. RevFlag instances are not invalidated, but
  1190.      * are removed from all RevObjects.
  1191.      * <p>
  1192.      * See {@link #retainOnReset(RevFlag)} for an alternative that does not
  1193.      * require passing the flags during each reset.
  1194.      *
  1195.      * @param retainFlags
  1196.      *            application flags that should <b>not</b> be cleared from
  1197.      *            existing commit objects.
  1198.      */
  1199.     public final void resetRetain(RevFlag... retainFlags) {
  1200.         int mask = 0;
  1201.         for (RevFlag flag : retainFlags)
  1202.             mask |= flag.mask;
  1203.         reset(mask);
  1204.     }

  1205.     /**
  1206.      * Resets internal state and allows this instance to be used again.
  1207.      * <p>
  1208.      * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
  1209.      * instances are not invalidated. RevFlag instances are not invalidated, but
  1210.      * are removed from all RevObjects. The value of {@code firstParent} is
  1211.      * retained.
  1212.      *
  1213.      * @param retainFlags
  1214.      *            application flags that should <b>not</b> be cleared from
  1215.      *            existing commit objects.
  1216.      */
  1217.     protected void reset(int retainFlags) {
  1218.         finishDelayedFreeFlags();
  1219.         retainFlags |= PARSED | retainOnReset;
  1220.         final int clearFlags = ~retainFlags;

  1221.         final FIFORevQueue q = new FIFORevQueue();
  1222.         for (RevCommit c : roots) {
  1223.             if ((c.flags & clearFlags) == 0)
  1224.                 continue;
  1225.             c.flags &= retainFlags;
  1226.             c.reset();
  1227.             q.add(c);
  1228.         }

  1229.         for (;;) {
  1230.             final RevCommit c = q.next();
  1231.             if (c == null)
  1232.                 break;
  1233.             if (c.parents == null)
  1234.                 continue;
  1235.             for (RevCommit p : c.parents) {
  1236.                 if ((p.flags & clearFlags) == 0)
  1237.                     continue;
  1238.                 p.flags &= retainFlags;
  1239.                 p.reset();
  1240.                 q.add(p);
  1241.             }
  1242.         }

  1243.         roots.clear();
  1244.         queue = new DateRevQueue(firstParent);
  1245.         pending = new StartGenerator(this);
  1246.     }

  1247.     /**
  1248.      * Dispose all internal state and invalidate all RevObject instances.
  1249.      * <p>
  1250.      * All RevObject (and thus RevCommit, etc.) instances previously acquired
  1251.      * from this RevWalk are invalidated by a dispose call. Applications must
  1252.      * not retain or use RevObject instances obtained prior to the dispose call.
  1253.      * All RevFlag instances are also invalidated, and must not be reused.
  1254.      */
  1255.     public void dispose() {
  1256.         reader.close();
  1257.         freeFlags = APP_FLAGS;
  1258.         delayFreeFlags = 0;
  1259.         retainOnReset = 0;
  1260.         carryFlags = UNINTERESTING;
  1261.         firstParent = false;
  1262.         objects.clear();
  1263.         roots.clear();
  1264.         queue = new DateRevQueue(firstParent);
  1265.         pending = new StartGenerator(this);
  1266.         shallowCommitsInitialized = false;
  1267.     }

  1268.     /**
  1269.      * Like {@link #next()}, but if a checked exception is thrown during the
  1270.      * walk it is rethrown as a {@link RevWalkException}.
  1271.      *
  1272.      * @throws RevWalkException if an {@link IOException} was thrown.
  1273.      * @return next most recent commit; null if traversal is over.
  1274.      */
  1275.     @Nullable
  1276.     private RevCommit nextForIterator() {
  1277.         try {
  1278.             return next();
  1279.         } catch (IOException e) {
  1280.             throw new RevWalkException(e);
  1281.         }
  1282.     }

  1283.     /**
  1284.      * {@inheritDoc}
  1285.      * <p>
  1286.      * Returns an Iterator over the commits of this walker.
  1287.      * <p>
  1288.      * The returned iterator is only useful for one walk. If this RevWalk gets
  1289.      * reset a new iterator must be obtained to walk over the new results.
  1290.      * <p>
  1291.      * Applications must not use both the Iterator and the {@link #next()} API
  1292.      * at the same time. Pick one API and use that for the entire walk.
  1293.      * <p>
  1294.      * If a checked exception is thrown during the walk (see {@link #next()}) it
  1295.      * is rethrown from the Iterator as a {@link RevWalkException}.
  1296.      *
  1297.      * @see RevWalkException
  1298.      */
  1299.     @Override
  1300.     public Iterator<RevCommit> iterator() {
  1301.         RevCommit first = nextForIterator();

  1302.         return new Iterator<RevCommit>() {
  1303.             RevCommit next = first;

  1304.             @Override
  1305.             public boolean hasNext() {
  1306.                 return next != null;
  1307.             }

  1308.             @Override
  1309.             public RevCommit next() {
  1310.                 RevCommit r = next;
  1311.                 next = nextForIterator();
  1312.                 return r;
  1313.             }

  1314.             @Override
  1315.             public void remove() {
  1316.                 throw new UnsupportedOperationException();
  1317.             }
  1318.         };
  1319.     }

  1320.     /**
  1321.      * Throws an exception if we have started producing output.
  1322.      */
  1323.     protected void assertNotStarted() {
  1324.         if (isNotStarted())
  1325.             return;
  1326.         throw new IllegalStateException(JGitText.get().outputHasAlreadyBeenStarted);
  1327.     }

  1328.     /**
  1329.      * Throws an exception if any commits have been marked as start.
  1330.      * <p>
  1331.      * If {@link #markStart(RevCommit)} has already been called,
  1332.      * {@link #reset()} can be called to satisfy this condition.
  1333.      *
  1334.      * @since 5.5
  1335.      */
  1336.     protected void assertNoCommitsMarkedStart() {
  1337.         if (roots.isEmpty())
  1338.             return;
  1339.         throw new IllegalStateException(
  1340.                 JGitText.get().commitsHaveAlreadyBeenMarkedAsStart);
  1341.     }

  1342.     private boolean isNotStarted() {
  1343.         return pending instanceof StartGenerator;
  1344.     }

  1345.     /**
  1346.      * Create and return an {@link org.eclipse.jgit.revwalk.ObjectWalk} using
  1347.      * the same objects.
  1348.      * <p>
  1349.      * Prior to using this method, the caller must reset this RevWalk to clean
  1350.      * any flags that were used during the last traversal.
  1351.      * <p>
  1352.      * The returned ObjectWalk uses the same ObjectReader, internal object pool,
  1353.      * and free RevFlags. Once the ObjectWalk is created, this RevWalk should
  1354.      * not be used anymore.
  1355.      *
  1356.      * @return a new walk, using the exact same object pool.
  1357.      */
  1358.     public ObjectWalk toObjectWalkWithSameObjects() {
  1359.         ObjectWalk ow = new ObjectWalk(reader);
  1360.         RevWalk rw = ow;
  1361.         rw.objects = objects;
  1362.         rw.freeFlags = freeFlags;
  1363.         return ow;
  1364.     }

  1365.     /**
  1366.      * Construct a new unparsed commit for the given object.
  1367.      *
  1368.      * @param id
  1369.      *            the object this walker requires a commit reference for.
  1370.      * @return a new unparsed reference for the object.
  1371.      */
  1372.     protected RevCommit createCommit(AnyObjectId id) {
  1373.         return new RevCommit(id);
  1374.     }

  1375.     void carryFlagsImpl(RevCommit c) {
  1376.         final int carry = c.flags & carryFlags;
  1377.         if (carry != 0)
  1378.             RevCommit.carryFlags(c, carry);
  1379.     }

  1380.     /**
  1381.      * Assume additional commits are shallow (have no parents).
  1382.      * <p>
  1383.      * This method is a No-op if the collection is empty.
  1384.      *
  1385.      * @param ids
  1386.      *            commits that should be treated as shallow commits, in addition
  1387.      *            to any commits already known to be shallow by the repository.
  1388.      * @since 3.3
  1389.      */
  1390.     public void assumeShallow(Collection<? extends ObjectId> ids) {
  1391.         for (ObjectId id : ids)
  1392.             lookupCommit(id).parents = RevCommit.NO_PARENTS;
  1393.     }

  1394.     /**
  1395.      * Reads the "shallow" file and applies it by setting the parents of shallow
  1396.      * commits to an empty array.
  1397.      * <p>
  1398.      * There is a sequencing problem if the first commit being parsed is a
  1399.      * shallow commit, since {@link RevCommit#parseCanonical(RevWalk, byte[])}
  1400.      * calls this method before its callers add the new commit to the
  1401.      * {@link RevWalk#objects} map. That means a call from this method to
  1402.      * {@link #lookupCommit(AnyObjectId)} fails to find that commit and creates
  1403.      * a new one, which is promptly discarded.
  1404.      * <p>
  1405.      * To avoid that, {@link RevCommit#parseCanonical(RevWalk, byte[])} passes
  1406.      * its commit to this method, so that this method can apply the shallow
  1407.      * state to it directly and avoid creating the duplicate commit object.
  1408.      *
  1409.      * @param rc
  1410.      *            the initial commit being parsed
  1411.      * @throws IOException
  1412.      *             if the shallow commits file can't be read
  1413.      */
  1414.     void initializeShallowCommits(RevCommit rc) throws IOException {
  1415.         if (shallowCommitsInitialized) {
  1416.             throw new IllegalStateException(
  1417.                     JGitText.get().shallowCommitsAlreadyInitialized);
  1418.         }

  1419.         shallowCommitsInitialized = true;

  1420.         if (reader == null) {
  1421.             return;
  1422.         }

  1423.         for (ObjectId id : reader.getShallowCommits()) {
  1424.             if (id.equals(rc.getId())) {
  1425.                 rc.parents = RevCommit.NO_PARENTS;
  1426.             } else {
  1427.                 lookupCommit(id).parents = RevCommit.NO_PARENTS;
  1428.             }
  1429.         }
  1430.     }
  1431. }