View Javadoc
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  
13  package org.eclipse.jgit.revwalk;
14  
15  import java.io.IOException;
16  import java.text.MessageFormat;
17  import java.util.ArrayList;
18  import java.util.Collection;
19  import java.util.EnumSet;
20  import java.util.Iterator;
21  import java.util.List;
22  
23  import org.eclipse.jgit.annotations.NonNull;
24  import org.eclipse.jgit.annotations.Nullable;
25  import org.eclipse.jgit.errors.CorruptObjectException;
26  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
27  import org.eclipse.jgit.errors.LargeObjectException;
28  import org.eclipse.jgit.errors.MissingObjectException;
29  import org.eclipse.jgit.errors.RevWalkException;
30  import org.eclipse.jgit.internal.JGitText;
31  import org.eclipse.jgit.lib.AnyObjectId;
32  import org.eclipse.jgit.lib.AsyncObjectLoaderQueue;
33  import org.eclipse.jgit.lib.Constants;
34  import org.eclipse.jgit.lib.MutableObjectId;
35  import org.eclipse.jgit.lib.ObjectId;
36  import org.eclipse.jgit.lib.ObjectIdOwnerMap;
37  import org.eclipse.jgit.lib.ObjectLoader;
38  import org.eclipse.jgit.lib.ObjectReader;
39  import org.eclipse.jgit.lib.Repository;
40  import org.eclipse.jgit.revwalk.filter.RevFilter;
41  import org.eclipse.jgit.treewalk.filter.TreeFilter;
42  import org.eclipse.jgit.util.References;
43  
44  /**
45   * Walks a commit graph and produces the matching commits in order.
46   * <p>
47   * A RevWalk instance can only be used once to generate results. Running a
48   * second time requires creating a new RevWalk instance, or invoking
49   * {@link #reset()} before starting again. Resetting an existing instance may be
50   * faster for some applications as commit body parsing can be avoided on the
51   * later invocations.
52   * <p>
53   * RevWalk instances are not thread-safe. Applications must either restrict
54   * usage of a RevWalk instance to a single thread, or implement their own
55   * synchronization at a higher level.
56   * <p>
57   * Multiple simultaneous RevWalk instances per
58   * {@link org.eclipse.jgit.lib.Repository} are permitted, even from concurrent
59   * threads. Equality of {@link org.eclipse.jgit.revwalk.RevCommit}s from two
60   * different RevWalk instances is never true, even if their
61   * {@link org.eclipse.jgit.lib.ObjectId}s are equal (and thus they describe the
62   * same commit).
63   * <p>
64   * The offered iterator is over the list of RevCommits described by the
65   * configuration of this instance. Applications should restrict themselves to
66   * using either the provided Iterator or {@link #next()}, but never use both on
67   * the same RevWalk at the same time. The Iterator may buffer RevCommits, while
68   * {@link #next()} does not.
69   */
70  public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
71  	private static final int MB = 1 << 20;
72  
73  	/**
74  	 * Set on objects whose important header data has been loaded.
75  	 * <p>
76  	 * For a RevCommit this indicates we have pulled apart the tree and parent
77  	 * references from the raw bytes available in the repository and translated
78  	 * those to our own local RevTree and RevCommit instances. The raw buffer is
79  	 * also available for message and other header filtering.
80  	 * <p>
81  	 * For a RevTag this indicates we have pulled part the tag references to
82  	 * find out who the tag refers to, and what that object's type is.
83  	 */
84  	static final int PARSED = 1 << 0;
85  
86  	/**
87  	 * Set on RevCommit instances added to our {@link #pending} queue.
88  	 * <p>
89  	 * We use this flag to avoid adding the same commit instance twice to our
90  	 * queue, especially if we reached it by more than one path.
91  	 */
92  	static final int SEEN = 1 << 1;
93  
94  	/**
95  	 * Set on RevCommit instances the caller does not want output.
96  	 * <p>
97  	 * We flag commits as uninteresting if the caller does not want commits
98  	 * reachable from a commit given to {@link #markUninteresting(RevCommit)}.
99  	 * This flag is always carried into the commit's parents and is a key part
100 	 * of the "rev-list B --not A" feature; A is marked UNINTERESTING.
101 	 */
102 	static final int UNINTERESTING = 1 << 2;
103 
104 	/**
105 	 * Set on a RevCommit that can collapse out of the history.
106 	 * <p>
107 	 * If the {@link #treeFilter} concluded that this commit matches his
108 	 * parents' for all of the paths that the filter is interested in then we
109 	 * mark the commit REWRITE. Later we can rewrite the parents of a REWRITE
110 	 * child to remove chains of REWRITE commits before we produce the child to
111 	 * the application.
112 	 *
113 	 * @see RewriteGenerator
114 	 */
115 	static final int REWRITE = 1 << 3;
116 
117 	/**
118 	 * Temporary mark for use within generators or filters.
119 	 * <p>
120 	 * This mark is only for local use within a single scope. If someone sets
121 	 * the mark they must unset it before any other code can see the mark.
122 	 */
123 	static final int TEMP_MARK = 1 << 4;
124 
125 	/**
126 	 * Temporary mark for use within {@link TopoSortGenerator}.
127 	 * <p>
128 	 * This mark indicates the commit could not produce when it wanted to, as at
129 	 * least one child was behind it. Commits with this flag are delayed until
130 	 * all children have been output first.
131 	 */
132 	static final int TOPO_DELAY = 1 << 5;
133 
134 	/**
135 	 * Temporary mark for use within {@link TopoNonIntermixSortGenerator}.
136 	 * <p>
137 	 * This mark indicates the commit has been queued for emission in
138 	 * {@link TopoSortGenerator} and can be produced. This mark is removed when
139 	 * the commit has been produced.
140 	 */
141 	static final int TOPO_QUEUED = 1 << 6;
142 
143 	/** Number of flag bits we keep internal for our own use. See above flags. */
144 	static final int RESERVED_FLAGS = 7;
145 
146 	private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1);
147 
148 	final ObjectReader reader;
149 
150 	private final boolean closeReader;
151 
152 	final MutableObjectId idBuffer;
153 
154 	ObjectIdOwnerMap<RevObject> objects;
155 
156 	int freeFlags = APP_FLAGS;
157 
158 	private int delayFreeFlags;
159 
160 	private int retainOnReset;
161 
162 	int carryFlags = UNINTERESTING;
163 
164 	final ArrayList<RevCommit> roots;
165 
166 	AbstractRevQueue queue;
167 
168 	Generator pending;
169 
170 	private final EnumSet<RevSort> sorting;
171 
172 	private RevFilter filter;
173 
174 	private TreeFilter treeFilter;
175 
176 	private boolean retainBody = true;
177 
178 	private boolean rewriteParents = true;
179 
180 	private boolean firstParent;
181 
182 	boolean shallowCommitsInitialized;
183 
184 	/**
185 	 * Create a new revision walker for a given repository.
186 	 *
187 	 * @param repo
188 	 *            the repository the walker will obtain data from. An
189 	 *            ObjectReader will be created by the walker, and will be closed
190 	 *            when the walker is closed.
191 	 */
192 	public RevWalk(Repository repo) {
193 		this(repo.newObjectReader(), true);
194 	}
195 
196 	/**
197 	 * Create a new revision walker for a given repository.
198 	 * <p>
199 	 *
200 	 * @param or
201 	 *            the reader the walker will obtain data from. The reader is not
202 	 *            closed when the walker is closed (but is closed by {@link
203 	 *            #dispose()}.
204 	 */
205 	public RevWalk(ObjectReader or) {
206 		this(or, false);
207 	}
208 
209 	private RevWalk(ObjectReader or, boolean closeReader) {
210 		reader = or;
211 		idBuffer = new MutableObjectId();
212 		objects = new ObjectIdOwnerMap<>();
213 		roots = new ArrayList<>();
214 		queue = new DateRevQueue(false);
215 		pending = new StartGenerator(this);
216 		sorting = EnumSet.of(RevSort.NONE);
217 		filter = RevFilter.ALL;
218 		treeFilter = TreeFilter.ALL;
219 		this.closeReader = closeReader;
220 	}
221 
222 	/**
223 	 * Get the reader this walker is using to load objects.
224 	 *
225 	 * @return the reader this walker is using to load objects.
226 	 */
227 	public ObjectReader getObjectReader() {
228 		return reader;
229 	}
230 
231 	/**
232 	 * Get a reachability checker for commits over this revwalk.
233 	 *
234 	 * @return the most efficient reachability checker for this repository.
235 	 * @throws IOException
236 	 *             if it cannot open any of the underlying indices.
237 	 *
238 	 * @since 5.4
239 	 */
240 	public ReachabilityChecker createReachabilityChecker() throws IOException {
241 		if (reader.getBitmapIndex() != null) {
242 			return new BitmappedReachabilityChecker(this);
243 		}
244 
245 		return new PedestrianReachabilityChecker(true, this);
246 	}
247 
248 	/**
249 	 * {@inheritDoc}
250 	 * <p>
251 	 * Release any resources used by this walker's reader.
252 	 * <p>
253 	 * A walker that has been released can be used again, but may need to be
254 	 * released after the subsequent usage.
255 	 *
256 	 * @since 4.0
257 	 */
258 	@Override
259 	public void close() {
260 		if (closeReader) {
261 			reader.close();
262 		}
263 	}
264 
265 	/**
266 	 * Mark a commit to start graph traversal from.
267 	 * <p>
268 	 * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain
269 	 * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as
270 	 * this method requires the commit to be parsed before it can be added as a
271 	 * root for the traversal.
272 	 * <p>
273 	 * The method will automatically parse an unparsed commit, but error
274 	 * handling may be more difficult for the application to explain why a
275 	 * RevCommit is not actually a commit. The object pool of this walker would
276 	 * also be 'poisoned' by the non-commit RevCommit.
277 	 *
278 	 * @param c
279 	 *            the commit to start traversing from. The commit passed must be
280 	 *            from this same revision walker.
281 	 * @throws org.eclipse.jgit.errors.MissingObjectException
282 	 *             the commit supplied is not available from the object
283 	 *             database. This usually indicates the supplied commit is
284 	 *             invalid, but the reference was constructed during an earlier
285 	 *             invocation to {@link #lookupCommit(AnyObjectId)}.
286 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
287 	 *             the object was not parsed yet and it was discovered during
288 	 *             parsing that it is not actually a commit. This usually
289 	 *             indicates the caller supplied a non-commit SHA-1 to
290 	 *             {@link #lookupCommit(AnyObjectId)}.
291 	 * @throws java.io.IOException
292 	 *             a pack file or loose object could not be read.
293 	 */
294 	public void markStart(RevCommit c) throws MissingObjectException,
295 			IncorrectObjectTypeException, IOException {
296 		if ((c.flags & SEEN) != 0)
297 			return;
298 		if ((c.flags & PARSED) == 0)
299 			c.parseHeaders(this);
300 		c.flags |= SEEN;
301 		roots.add(c);
302 		queue.add(c);
303 	}
304 
305 	/**
306 	 * Mark commits to start graph traversal from.
307 	 *
308 	 * @param list
309 	 *            commits to start traversing from. The commits passed must be
310 	 *            from this same revision walker.
311 	 * @throws org.eclipse.jgit.errors.MissingObjectException
312 	 *             one of the commits 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 markStart(Collection<RevCommit> list)
325 			throws MissingObjectException, IncorrectObjectTypeException,
326 			IOException {
327 		for (RevCommit c : list)
328 			markStart(c);
329 	}
330 
331 	/**
332 	 * Mark a commit to not produce in the output.
333 	 * <p>
334 	 * Uninteresting commits denote not just themselves but also their entire
335 	 * ancestry chain, back until the merge base of an uninteresting commit and
336 	 * an otherwise interesting commit.
337 	 * <p>
338 	 * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain
339 	 * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as
340 	 * this method requires the commit to be parsed before it can be added as a
341 	 * root for the traversal.
342 	 * <p>
343 	 * The method will automatically parse an unparsed commit, but error
344 	 * handling may be more difficult for the application to explain why a
345 	 * RevCommit is not actually a commit. The object pool of this walker would
346 	 * also be 'poisoned' by the non-commit RevCommit.
347 	 *
348 	 * @param c
349 	 *            the commit to start traversing from. The commit passed must be
350 	 *            from this same revision walker.
351 	 * @throws org.eclipse.jgit.errors.MissingObjectException
352 	 *             the commit supplied is not available from the object
353 	 *             database. This usually indicates the supplied commit is
354 	 *             invalid, but the reference was constructed during an earlier
355 	 *             invocation to {@link #lookupCommit(AnyObjectId)}.
356 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
357 	 *             the object was not parsed yet and it was discovered during
358 	 *             parsing that it is not actually a commit. This usually
359 	 *             indicates the caller supplied a non-commit SHA-1 to
360 	 *             {@link #lookupCommit(AnyObjectId)}.
361 	 * @throws java.io.IOException
362 	 *             a pack file or loose object could not be read.
363 	 */
364 	public void markUninteresting(RevCommit c)
365 			throws MissingObjectException, IncorrectObjectTypeException,
366 			IOException {
367 		c.flags |= UNINTERESTING;
368 		carryFlagsImpl(c);
369 		markStart(c);
370 	}
371 
372 	/**
373 	 * Determine if a commit is reachable from another commit.
374 	 * <p>
375 	 * A commit <code>base</code> is an ancestor of <code>tip</code> if we
376 	 * can find a path of commits that leads from <code>tip</code> and ends at
377 	 * <code>base</code>.
378 	 * <p>
379 	 * This utility function resets the walker, inserts the two supplied
380 	 * commits, and then executes a walk until an answer can be obtained.
381 	 * Currently allocated RevFlags that have been added to RevCommit instances
382 	 * will be retained through the reset.
383 	 *
384 	 * @param base
385 	 *            commit the caller thinks is reachable from <code>tip</code>.
386 	 * @param tip
387 	 *            commit to start iteration from, and which is most likely a
388 	 *            descendant (child) of <code>base</code>.
389 	 * @return true if there is a path directly from <code>tip</code> to
390 	 *         <code>base</code> (and thus <code>base</code> is fully merged
391 	 *         into <code>tip</code>); false otherwise.
392 	 * @throws org.eclipse.jgit.errors.MissingObjectException
393 	 *             one or more of the next commit's parents are not available
394 	 *             from the object database, but were thought to be candidates
395 	 *             for traversal. This usually indicates a broken link.
396 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
397 	 *             one or more of the next commit's parents are not actually
398 	 *             commit objects.
399 	 * @throws java.io.IOException
400 	 *             a pack file or loose object could not be read.
401 	 */
402 	public boolean isMergedInto(RevCommit"../../../../org/eclipse/jgit/revwalk/RevCommit.html#RevCommit">RevCommit base, RevCommit tip)
403 			throws MissingObjectException, IncorrectObjectTypeException,
404 			IOException {
405 		final RevFilter oldRF = filter;
406 		final TreeFilter oldTF = treeFilter;
407 		try {
408 			finishDelayedFreeFlags();
409 			reset(~freeFlags & APP_FLAGS);
410 			filter = RevFilter.MERGE_BASE;
411 			treeFilter = TreeFilter.ALL;
412 			markStart(tip);
413 			markStart(base);
414 			RevCommit mergeBase;
415 			while ((mergeBase = next()) != null) {
416 				if (References.isSameObject(mergeBase, base)) {
417 					return true;
418 				}
419 			}
420 			return false;
421 		} finally {
422 			filter = oldRF;
423 			treeFilter = oldTF;
424 		}
425 	}
426 
427 	/**
428 	 * Pop the next most recent commit.
429 	 *
430 	 * @return next most recent commit; null if traversal is over.
431 	 * @throws org.eclipse.jgit.errors.MissingObjectException
432 	 *             one or more of the next commit's parents are not available
433 	 *             from the object database, but were thought to be candidates
434 	 *             for traversal. This usually indicates a broken link.
435 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
436 	 *             one or more of the next commit's parents are not actually
437 	 *             commit objects.
438 	 * @throws java.io.IOException
439 	 *             a pack file or loose object could not be read.
440 	 */
441 	public RevCommit next() throws MissingObjectException,
442 			IncorrectObjectTypeException, IOException {
443 		return pending.next();
444 	}
445 
446 	/**
447 	 * Obtain the sort types applied to the commits returned.
448 	 *
449 	 * @return the sorting strategies employed. At least one strategy is always
450 	 *         used, but that strategy may be
451 	 *         {@link org.eclipse.jgit.revwalk.RevSort#NONE}.
452 	 */
453 	public EnumSet<RevSort> getRevSort() {
454 		return sorting.clone();
455 	}
456 
457 	/**
458 	 * Check whether the provided sorting strategy is enabled.
459 	 *
460 	 * @param sort
461 	 *            a sorting strategy to look for.
462 	 * @return true if this strategy is enabled, false otherwise
463 	 */
464 	public boolean hasRevSort(RevSort sort) {
465 		return sorting.contains(sort);
466 	}
467 
468 	/**
469 	 * Select a single sorting strategy for the returned commits.
470 	 * <p>
471 	 * Disables all sorting strategies, then enables only the single strategy
472 	 * supplied by the caller.
473 	 *
474 	 * @param s
475 	 *            a sorting strategy to enable.
476 	 */
477 	public void sort(RevSort s) {
478 		assertNotStarted();
479 		sorting.clear();
480 		sorting.add(s);
481 	}
482 
483 	/**
484 	 * Add or remove a sorting strategy for the returned commits.
485 	 * <p>
486 	 * Multiple strategies can be applied at once, in which case some strategies
487 	 * may take precedence over others. As an example,
488 	 * {@link org.eclipse.jgit.revwalk.RevSort#TOPO} must take precedence over
489 	 * {@link org.eclipse.jgit.revwalk.RevSort#COMMIT_TIME_DESC}, otherwise it
490 	 * cannot enforce its ordering.
491 	 *
492 	 * @param s
493 	 *            a sorting strategy to enable or disable.
494 	 * @param use
495 	 *            true if this strategy should be used, false if it should be
496 	 *            removed.
497 	 */
498 	public void sort(RevSort s, boolean use) {
499 		assertNotStarted();
500 		if (use)
501 			sorting.add(s);
502 		else
503 			sorting.remove(s);
504 
505 		if (sorting.size() > 1)
506 			sorting.remove(RevSort.NONE);
507 		else if (sorting.isEmpty())
508 			sorting.add(RevSort.NONE);
509 	}
510 
511 	/**
512 	 * Get the currently configured commit filter.
513 	 *
514 	 * @return the current filter. Never null as a filter is always needed.
515 	 */
516 	@NonNull
517 	public RevFilter getRevFilter() {
518 		return filter;
519 	}
520 
521 	/**
522 	 * Set the commit filter for this walker.
523 	 * <p>
524 	 * Multiple filters may be combined by constructing an arbitrary tree of
525 	 * <code>AndRevFilter</code> or <code>OrRevFilter</code> instances to
526 	 * describe the boolean expression required by the application. Custom
527 	 * filter implementations may also be constructed by applications.
528 	 * <p>
529 	 * Note that filters are not thread-safe and may not be shared by concurrent
530 	 * RevWalk instances. Every RevWalk must be supplied its own unique filter,
531 	 * unless the filter implementation specifically states it is (and always
532 	 * will be) thread-safe. Callers may use
533 	 * {@link org.eclipse.jgit.revwalk.filter.RevFilter#clone()} to create a
534 	 * unique filter tree for this RevWalk instance.
535 	 *
536 	 * @param newFilter
537 	 *            the new filter. If null the special
538 	 *            {@link org.eclipse.jgit.revwalk.filter.RevFilter#ALL} filter
539 	 *            will be used instead, as it matches every commit.
540 	 * @see org.eclipse.jgit.revwalk.filter.AndRevFilter
541 	 * @see org.eclipse.jgit.revwalk.filter.OrRevFilter
542 	 */
543 	public void setRevFilter(RevFilter newFilter) {
544 		assertNotStarted();
545 		filter = newFilter != null ? newFilter : RevFilter.ALL;
546 	}
547 
548 	/**
549 	 * Get the tree filter used to simplify commits by modified paths.
550 	 *
551 	 * @return the current filter. Never null as a filter is always needed. If
552 	 *         no filter is being applied
553 	 *         {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} is
554 	 *         returned.
555 	 */
556 	@NonNull
557 	public TreeFilter getTreeFilter() {
558 		return treeFilter;
559 	}
560 
561 	/**
562 	 * Set the tree filter used to simplify commits by modified paths.
563 	 * <p>
564 	 * If null or {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} the
565 	 * path limiter is removed. Commits will not be simplified.
566 	 * <p>
567 	 * If non-null and not
568 	 * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} then the tree
569 	 * filter will be installed. Commits will have their ancestry simplified to
570 	 * hide commits that do not contain tree entries matched by the filter,
571 	 * unless {@code setRewriteParents(false)} is called.
572 	 * <p>
573 	 * Usually callers should be inserting a filter graph including
574 	 * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ANY_DIFF} along with
575 	 * one or more {@link org.eclipse.jgit.treewalk.filter.PathFilter}
576 	 * instances.
577 	 *
578 	 * @param newFilter
579 	 *            new filter. If null the special
580 	 *            {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} filter
581 	 *            will be used instead, as it matches everything.
582 	 * @see org.eclipse.jgit.treewalk.filter.PathFilter
583 	 */
584 	public void setTreeFilter(TreeFilter newFilter) {
585 		assertNotStarted();
586 		treeFilter = newFilter != null ? newFilter : TreeFilter.ALL;
587 	}
588 
589 	/**
590 	 * Set whether to rewrite parent pointers when filtering by modified paths.
591 	 * <p>
592 	 * By default, when {@link #setTreeFilter(TreeFilter)} is called with non-
593 	 * null and non-{@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL}
594 	 * filter, commits will have their ancestry simplified and parents rewritten
595 	 * to hide commits that do not match the filter.
596 	 * <p>
597 	 * This behavior can be bypassed by passing false to this method.
598 	 *
599 	 * @param rewrite
600 	 *            whether to rewrite parents; defaults to true.
601 	 * @since 3.4
602 	 */
603 	public void setRewriteParents(boolean rewrite) {
604 		rewriteParents = rewrite;
605 	}
606 
607 	boolean getRewriteParents() {
608 		return rewriteParents;
609 	}
610 
611 	/**
612 	 * Should the body of a commit or tag be retained after parsing its headers?
613 	 * <p>
614 	 * Usually the body is always retained, but some application code might not
615 	 * care and would prefer to discard the body of a commit as early as
616 	 * possible, to reduce memory usage.
617 	 * <p>
618 	 * True by default on {@link org.eclipse.jgit.revwalk.RevWalk} and false by
619 	 * default for {@link org.eclipse.jgit.revwalk.ObjectWalk}.
620 	 *
621 	 * @return true if the body should be retained; false it is discarded.
622 	 */
623 	public boolean isRetainBody() {
624 		return retainBody;
625 	}
626 
627 	/**
628 	 * Set whether or not the body of a commit or tag is retained.
629 	 * <p>
630 	 * If a body of a commit or tag is not retained, the application must call
631 	 * {@link #parseBody(RevObject)} before the body can be safely accessed
632 	 * through the type specific access methods.
633 	 * <p>
634 	 * True by default on {@link org.eclipse.jgit.revwalk.RevWalk} and false by
635 	 * default for {@link org.eclipse.jgit.revwalk.ObjectWalk}.
636 	 *
637 	 * @param retain
638 	 *            true to retain bodies; false to discard them early.
639 	 */
640 	public void setRetainBody(boolean retain) {
641 		retainBody = retain;
642 	}
643 
644 	/**
645 	 * @return whether only first-parent links should be followed when walking.
646 	 *
647 	 * @since 5.5
648 	 */
649 	public boolean isFirstParent() {
650 		return firstParent;
651 	}
652 
653 	/**
654 	 * Set whether or not only first parent links should be followed.
655 	 * <p>
656 	 * If set, second- and higher-parent links are not traversed at all.
657 	 * <p>
658 	 * This must be called prior to {@link #markStart(RevCommit)}.
659 	 *
660 	 * @param enable
661 	 *            true to walk only first-parent links.
662 	 *
663 	 * @since 5.5
664 	 */
665 	public void setFirstParent(boolean enable) {
666 		assertNotStarted();
667 		assertNoCommitsMarkedStart();
668 		firstParent = enable;
669 		queue = new DateRevQueue(firstParent);
670 		pending = new StartGenerator(this);
671 	}
672 
673 	/**
674 	 * Locate a reference to a blob without loading it.
675 	 * <p>
676 	 * The blob may or may not exist in the repository. It is impossible to tell
677 	 * from this method's return value.
678 	 *
679 	 * @param id
680 	 *            name of the blob object.
681 	 * @return reference to the blob object. Never null.
682 	 */
683 	@NonNull
684 	public RevBlob lookupBlob(AnyObjectId id) {
685 		RevBlob c = (RevBlob) objects.get(id);
686 		if (c == null) {
687 			c = new RevBlob(id);
688 			objects.add(c);
689 		}
690 		return c;
691 	}
692 
693 	/**
694 	 * Locate a reference to a tree without loading it.
695 	 * <p>
696 	 * The tree may or may not exist in the repository. It is impossible to tell
697 	 * from this method's return value.
698 	 *
699 	 * @param id
700 	 *            name of the tree object.
701 	 * @return reference to the tree object. Never null.
702 	 */
703 	@NonNull
704 	public RevTree lookupTree(AnyObjectId id) {
705 		RevTree c = (RevTree) objects.get(id);
706 		if (c == null) {
707 			c = new RevTree(id);
708 			objects.add(c);
709 		}
710 		return c;
711 	}
712 
713 	/**
714 	 * Locate a reference to a commit without loading it.
715 	 * <p>
716 	 * The commit may or may not exist in the repository. It is impossible to
717 	 * tell from this method's return value.
718 	 * <p>
719 	 * See {@link #parseHeaders(RevObject)} and {@link #parseBody(RevObject)}
720 	 * for loading contents.
721 	 *
722 	 * @param id
723 	 *            name of the commit object.
724 	 * @return reference to the commit object. Never null.
725 	 */
726 	@NonNull
727 	public RevCommit lookupCommit(AnyObjectId id) {
728 		RevCommit c = (RevCommit) objects.get(id);
729 		if (c == null) {
730 			c = createCommit(id);
731 			objects.add(c);
732 		}
733 		return c;
734 	}
735 
736 	/**
737 	 * Locate a reference to a tag without loading it.
738 	 * <p>
739 	 * The tag may or may not exist in the repository. It is impossible to tell
740 	 * from this method's return value.
741 	 *
742 	 * @param id
743 	 *            name of the tag object.
744 	 * @return reference to the tag object. Never null.
745 	 */
746 	@NonNull
747 	public RevTag lookupTag(AnyObjectId id) {
748 		RevTag c = (RevTag) objects.get(id);
749 		if (c == null) {
750 			c = new RevTag(id);
751 			objects.add(c);
752 		}
753 		return c;
754 	}
755 
756 	/**
757 	 * Locate a reference to any object without loading it.
758 	 * <p>
759 	 * The object may or may not exist in the repository. It is impossible to
760 	 * tell from this method's return value.
761 	 *
762 	 * @param id
763 	 *            name of the object.
764 	 * @param type
765 	 *            type of the object. Must be a valid Git object type.
766 	 * @return reference to the object. Never null.
767 	 */
768 	@NonNull
769 	public RevObject lookupAny(AnyObjectId id, int type) {
770 		RevObject r = objects.get(id);
771 		if (r == null) {
772 			switch (type) {
773 			case Constants.OBJ_COMMIT:
774 				r = createCommit(id);
775 				break;
776 			case Constants.OBJ_TREE:
777 				r = new RevTree(id);
778 				break;
779 			case Constants.OBJ_BLOB:
780 				r = new RevBlob(id);
781 				break;
782 			case Constants.OBJ_TAG:
783 				r = new RevTag(id);
784 				break;
785 			default:
786 				throw new IllegalArgumentException(MessageFormat.format(
787 						JGitText.get().invalidGitType, Integer.valueOf(type)));
788 			}
789 			objects.add(r);
790 		}
791 		return r;
792 	}
793 
794 	/**
795 	 * Locate an object that was previously allocated in this walk.
796 	 *
797 	 * @param id
798 	 *            name of the object.
799 	 * @return reference to the object if it has been previously located;
800 	 *         otherwise null.
801 	 */
802 	public RevObject lookupOrNull(AnyObjectId id) {
803 		return objects.get(id);
804 	}
805 
806 	/**
807 	 * Locate a reference to a commit and immediately parse its content.
808 	 * <p>
809 	 * Unlike {@link #lookupCommit(AnyObjectId)} this method only returns
810 	 * successfully if the commit object exists, is verified to be a commit, and
811 	 * was parsed without error.
812 	 *
813 	 * @param id
814 	 *            name of the commit object.
815 	 * @return reference to the commit object. Never null.
816 	 * @throws org.eclipse.jgit.errors.MissingObjectException
817 	 *             the supplied commit does not exist.
818 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
819 	 *             the supplied id is not a commit or an annotated tag.
820 	 * @throws java.io.IOException
821 	 *             a pack file or loose object could not be read.
822 	 */
823 	@NonNull
824 	public RevCommit parseCommit(AnyObjectId id)
825 			throws MissingObjectException, IncorrectObjectTypeException,
826 			IOException {
827 		RevObject c = peel(parseAny(id));
828 		if (!(c instanceof RevCommit))
829 			throw new IncorrectObjectTypeException(id.toObjectId(),
830 					Constants.TYPE_COMMIT);
831 		return (RevCommit) c;
832 	}
833 
834 	/**
835 	 * Locate a reference to a tree.
836 	 * <p>
837 	 * This method only returns successfully if the tree object exists, is
838 	 * verified to be a tree.
839 	 *
840 	 * @param id
841 	 *            name of the tree object, or a commit or annotated tag that may
842 	 *            reference a tree.
843 	 * @return reference to the tree object. Never null.
844 	 * @throws org.eclipse.jgit.errors.MissingObjectException
845 	 *             the supplied tree does not exist.
846 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
847 	 *             the supplied id is not a tree, a commit or an annotated tag.
848 	 * @throws java.io.IOException
849 	 *             a pack file or loose object could not be read.
850 	 */
851 	@NonNull
852 	public RevTree parseTree(AnyObjectId id)
853 			throws MissingObjectException, IncorrectObjectTypeException,
854 			IOException {
855 		RevObject c = peel(parseAny(id));
856 
857 		final RevTree t;
858 		if (c instanceof RevCommit)
859 			t = ((RevCommit) c).getTree();
860 		else if (!(c instanceof RevTree))
861 			throw new IncorrectObjectTypeException(id.toObjectId(),
862 					Constants.TYPE_TREE);
863 		else
864 			t = (RevTree) c;
865 		parseHeaders(t);
866 		return t;
867 	}
868 
869 	/**
870 	 * Locate a reference to an annotated tag and immediately parse its content.
871 	 * <p>
872 	 * Unlike {@link #lookupTag(AnyObjectId)} this method only returns
873 	 * successfully if the tag object exists, is verified to be a tag, and was
874 	 * parsed without error.
875 	 *
876 	 * @param id
877 	 *            name of the tag object.
878 	 * @return reference to the tag object. Never null.
879 	 * @throws org.eclipse.jgit.errors.MissingObjectException
880 	 *             the supplied tag does not exist.
881 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
882 	 *             the supplied id is not a tag or an annotated tag.
883 	 * @throws java.io.IOException
884 	 *             a pack file or loose object could not be read.
885 	 */
886 	@NonNull
887 	public RevTag parseTag(AnyObjectId id) throws MissingObjectException,
888 			IncorrectObjectTypeException, IOException {
889 		RevObject c = parseAny(id);
890 		if (!(c instanceof RevTag))
891 			throw new IncorrectObjectTypeException(id.toObjectId(),
892 					Constants.TYPE_TAG);
893 		return (RevTag) c;
894 	}
895 
896 	/**
897 	 * Locate a reference to any object and immediately parse its headers.
898 	 * <p>
899 	 * This method only returns successfully if the object exists and was parsed
900 	 * without error. Parsing an object can be expensive as the type must be
901 	 * determined. For blobs this may mean the blob content was unpacked
902 	 * unnecessarily, and thrown away.
903 	 *
904 	 * @param id
905 	 *            name of the object.
906 	 * @return reference to the object. Never null.
907 	 * @throws org.eclipse.jgit.errors.MissingObjectException
908 	 *             the supplied does not exist.
909 	 * @throws java.io.IOException
910 	 *             a pack file or loose object could not be read.
911 	 */
912 	@NonNull
913 	public RevObject parseAny(AnyObjectId id)
914 			throws MissingObjectException, IOException {
915 		RevObject r = objects.get(id);
916 		if (r == null)
917 			r = parseNew(id, reader.open(id));
918 		else
919 			parseHeaders(r);
920 		return r;
921 	}
922 
923 	private RevObject parseNew(AnyObjectId id, ObjectLoader ldr)
924 			throws LargeObjectException, CorruptObjectException,
925 			MissingObjectException, IOException {
926 		RevObject r;
927 		int type = ldr.getType();
928 		switch (type) {
929 		case Constants.OBJ_COMMIT: {
930 			final RevCommit c = createCommit(id);
931 			c.parseCanonical(this, getCachedBytes(c, ldr));
932 			r = c;
933 			break;
934 		}
935 		case Constants.OBJ_TREE: {
936 			r = new RevTree(id);
937 			r.flags |= PARSED;
938 			break;
939 		}
940 		case Constants.OBJ_BLOB: {
941 			r = new RevBlob(id);
942 			r.flags |= PARSED;
943 			break;
944 		}
945 		case Constants.OBJ_TAG: {
946 			final RevTagk/RevTag.html#RevTag">RevTag t = new RevTag(id);
947 			t.parseCanonical(this, getCachedBytes(t, ldr));
948 			r = t;
949 			break;
950 		}
951 		default:
952 			throw new IllegalArgumentException(MessageFormat.format(
953 					JGitText.get().badObjectType, Integer.valueOf(type)));
954 		}
955 		objects.add(r);
956 		return r;
957 	}
958 
959 	byte[] getCachedBytes(RevObject obj) throws LargeObjectException,
960 			MissingObjectException, IncorrectObjectTypeException, IOException {
961 		return getCachedBytes(obj, reader.open(obj, obj.getType()));
962 	}
963 
964 	byte[] getCachedBytes(RevObject obj, ObjectLoader ldr)
965 			throws LargeObjectException, MissingObjectException, IOException {
966 		try {
967 			return ldr.getCachedBytes(5 * MB);
968 		} catch (LargeObjectException tooBig) {
969 			tooBig.setObjectId(obj);
970 			throw tooBig;
971 		}
972 	}
973 
974 	/**
975 	 * Asynchronous object parsing.
976 	 *
977 	 * @param objectIds
978 	 *            objects to open from the object store. The supplied collection
979 	 *            must not be modified until the queue has finished.
980 	 * @param reportMissing
981 	 *            if true missing objects are reported by calling failure with a
982 	 *            MissingObjectException. This may be more expensive for the
983 	 *            implementation to guarantee. If false the implementation may
984 	 *            choose to report MissingObjectException, or silently skip over
985 	 *            the object with no warning.
986 	 * @return queue to read the objects from.
987 	 */
988 	public <T extends ObjectId> AsyncRevObjectQueue parseAny(
989 			Iterable<T> objectIds, boolean reportMissing) {
990 		List<T> need = new ArrayList<>();
991 		List<RevObject> have = new ArrayList<>();
992 		for (T id : objectIds) {
993 			RevObject r = objects.get(id);
994 			if (r != null && (r.flags & PARSED) != 0)
995 				have.add(r);
996 			else
997 				need.add(id);
998 		}
999 
1000 		final Iterator<RevObject> objItr = have.iterator();
1001 		if (need.isEmpty()) {
1002 			return new AsyncRevObjectQueue() {
1003 				@Override
1004 				public RevObject next() {
1005 					return objItr.hasNext() ? objItr.next() : null;
1006 				}
1007 
1008 				@Override
1009 				public boolean cancel(boolean mayInterruptIfRunning) {
1010 					return true;
1011 				}
1012 
1013 				@Override
1014 				public void release() {
1015 					// In-memory only, no action required.
1016 				}
1017 			};
1018 		}
1019 
1020 		final AsyncObjectLoaderQueue<T> lItr = reader.open(need, reportMissing);
1021 		return new AsyncRevObjectQueue() {
1022 			@Override
1023 			public RevObject next() throws MissingObjectException,
1024 					IncorrectObjectTypeException, IOException {
1025 				if (objItr.hasNext())
1026 					return objItr.next();
1027 				if (!lItr.next())
1028 					return null;
1029 
1030 				ObjectId id = lItr.getObjectId();
1031 				ObjectLoader ldr = lItr.open();
1032 				RevObject r = objects.get(id);
1033 				if (r == null)
1034 					r = parseNew(id, ldr);
1035 				else if (r instanceof RevCommit) {
1036 					byte[] raw = ldr.getCachedBytes();
1037 					((RevCommit) r).parseCanonical(RevWalk.this, raw);
1038 				} else if (r instanceof RevTag) {
1039 					byte[] raw = ldr.getCachedBytes();
1040 					((RevTag) r).parseCanonical(RevWalk.this, raw);
1041 				} else
1042 					r.flags |= PARSED;
1043 				return r;
1044 			}
1045 
1046 			@Override
1047 			public boolean cancel(boolean mayInterruptIfRunning) {
1048 				return lItr.cancel(mayInterruptIfRunning);
1049 			}
1050 
1051 			@Override
1052 			public void release() {
1053 				lItr.release();
1054 			}
1055 		};
1056 	}
1057 
1058 	/**
1059 	 * Ensure the object's critical headers have been parsed.
1060 	 * <p>
1061 	 * This method only returns successfully if the object exists and was parsed
1062 	 * without error.
1063 	 *
1064 	 * @param obj
1065 	 *            the object the caller needs to be parsed.
1066 	 * @throws org.eclipse.jgit.errors.MissingObjectException
1067 	 *             the supplied does not exist.
1068 	 * @throws java.io.IOException
1069 	 *             a pack file or loose object could not be read.
1070 	 */
1071 	public void parseHeaders(RevObject obj)
1072 			throws MissingObjectException, IOException {
1073 		if ((obj.flags & PARSED) == 0)
1074 			obj.parseHeaders(this);
1075 	}
1076 
1077 	/**
1078 	 * Ensure the object's full body content is available.
1079 	 * <p>
1080 	 * This method only returns successfully if the object exists and was parsed
1081 	 * without error.
1082 	 *
1083 	 * @param obj
1084 	 *            the object the caller needs to be parsed.
1085 	 * @throws org.eclipse.jgit.errors.MissingObjectException
1086 	 *             the supplied does not exist.
1087 	 * @throws java.io.IOException
1088 	 *             a pack file or loose object could not be read.
1089 	 */
1090 	public void parseBody(RevObject obj)
1091 			throws MissingObjectException, IOException {
1092 		obj.parseBody(this);
1093 	}
1094 
1095 	/**
1096 	 * Peel back annotated tags until a non-tag object is found.
1097 	 *
1098 	 * @param obj
1099 	 *            the starting object.
1100 	 * @return If {@code obj} is not an annotated tag, {@code obj}. Otherwise
1101 	 *         the first non-tag object that {@code obj} references. The
1102 	 *         returned object's headers have been parsed.
1103 	 * @throws org.eclipse.jgit.errors.MissingObjectException
1104 	 *             a referenced object cannot be found.
1105 	 * @throws java.io.IOException
1106 	 *             a pack file or loose object could not be read.
1107 	 */
1108 	public RevObject="../../../../org/eclipse/jgit/revwalk/RevObject.html#RevObject">RevObject peel(RevObject obj) throws MissingObjectException,
1109 			IOException {
1110 		while (obj instanceof RevTag) {
1111 			parseHeaders(obj);
1112 			obj = ((RevTag) obj).getObject();
1113 		}
1114 		parseHeaders(obj);
1115 		return obj;
1116 	}
1117 
1118 	/**
1119 	 * Create a new flag for application use during walking.
1120 	 * <p>
1121 	 * Applications are only assured to be able to create 24 unique flags on any
1122 	 * given revision walker instance. Any flags beyond 24 are offered only if
1123 	 * the implementation has extra free space within its internal storage.
1124 	 *
1125 	 * @param name
1126 	 *            description of the flag, primarily useful for debugging.
1127 	 * @return newly constructed flag instance.
1128 	 * @throws java.lang.IllegalArgumentException
1129 	 *             too many flags have been reserved on this revision walker.
1130 	 */
1131 	public RevFlag newFlag(String name) {
1132 		final int m = allocFlag();
1133 		return new RevFlag(this, name, m);
1134 	}
1135 
1136 	int allocFlag() {
1137 		if (freeFlags == 0)
1138 			throw new IllegalArgumentException(MessageFormat.format(
1139 					JGitText.get().flagsAlreadyCreated,
1140 					Integer.valueOf(32 - RESERVED_FLAGS)));
1141 		final int m = Integer.lowestOneBit(freeFlags);
1142 		freeFlags &= ~m;
1143 		return m;
1144 	}
1145 
1146 	/**
1147 	 * Automatically carry a flag from a child commit to its parents.
1148 	 * <p>
1149 	 * A carried flag is copied from the child commit onto its parents when the
1150 	 * child commit is popped from the lowest level of walk's internal graph.
1151 	 *
1152 	 * @param flag
1153 	 *            the flag to carry onto parents, if set on a descendant.
1154 	 */
1155 	public void carry(RevFlag flag) {
1156 		if ((freeFlags & flag.mask) != 0)
1157 			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name));
1158 		if (flag.walker != this)
1159 			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name));
1160 		carryFlags |= flag.mask;
1161 	}
1162 
1163 	/**
1164 	 * Automatically carry flags from a child commit to its parents.
1165 	 * <p>
1166 	 * A carried flag is copied from the child commit onto its parents when the
1167 	 * child commit is popped from the lowest level of walk's internal graph.
1168 	 *
1169 	 * @param set
1170 	 *            the flags to carry onto parents, if set on a descendant.
1171 	 */
1172 	public void carry(Collection<RevFlag> set) {
1173 		for (RevFlag flag : set)
1174 			carry(flag);
1175 	}
1176 
1177 	/**
1178 	 * Preserve a RevFlag during all {@code reset} methods.
1179 	 * <p>
1180 	 * Calling {@code retainOnReset(flag)} avoids needing to pass the flag
1181 	 * during each {@code resetRetain()} invocation on this instance.
1182 	 * <p>
1183 	 * Clearing flags marked retainOnReset requires disposing of the flag with
1184 	 * {@code #disposeFlag(RevFlag)} or disposing of the entire RevWalk by
1185 	 * {@code #dispose()}.
1186 	 *
1187 	 * @param flag
1188 	 *            the flag to retain during all resets.
1189 	 * @since 3.6
1190 	 */
1191 	public final void retainOnReset(RevFlag flag) {
1192 		if ((freeFlags & flag.mask) != 0)
1193 			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name));
1194 		if (flag.walker != this)
1195 			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name));
1196 		retainOnReset |= flag.mask;
1197 	}
1198 
1199 	/**
1200 	 * Preserve a set of RevFlags during all {@code reset} methods.
1201 	 * <p>
1202 	 * Calling {@code retainOnReset(set)} avoids needing to pass the flags
1203 	 * during each {@code resetRetain()} invocation on this instance.
1204 	 * <p>
1205 	 * Clearing flags marked retainOnReset requires disposing of the flag with
1206 	 * {@code #disposeFlag(RevFlag)} or disposing of the entire RevWalk by
1207 	 * {@code #dispose()}.
1208 	 *
1209 	 * @param flags
1210 	 *            the flags to retain during all resets.
1211 	 * @since 3.6
1212 	 */
1213 	public final void retainOnReset(Collection<RevFlag> flags) {
1214 		for (RevFlag f : flags)
1215 			retainOnReset(f);
1216 	}
1217 
1218 	/**
1219 	 * Allow a flag to be recycled for a different use.
1220 	 * <p>
1221 	 * Recycled flags always come back as a different Java object instance when
1222 	 * assigned again by {@link #newFlag(String)}.
1223 	 * <p>
1224 	 * If the flag was previously being carried, the carrying request is
1225 	 * removed. Disposing of a carried flag while a traversal is in progress has
1226 	 * an undefined behavior.
1227 	 *
1228 	 * @param flag
1229 	 *            the to recycle.
1230 	 */
1231 	public void disposeFlag(RevFlag flag) {
1232 		freeFlag(flag.mask);
1233 	}
1234 
1235 	void freeFlag(int mask) {
1236 		retainOnReset &= ~mask;
1237 		if (isNotStarted()) {
1238 			freeFlags |= mask;
1239 			carryFlags &= ~mask;
1240 		} else {
1241 			delayFreeFlags |= mask;
1242 		}
1243 	}
1244 
1245 	private void finishDelayedFreeFlags() {
1246 		if (delayFreeFlags != 0) {
1247 			freeFlags |= delayFreeFlags;
1248 			carryFlags &= ~delayFreeFlags;
1249 			delayFreeFlags = 0;
1250 		}
1251 	}
1252 
1253 	/**
1254 	 * Resets internal state and allows this instance to be used again.
1255 	 * <p>
1256 	 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
1257 	 * instances are not invalidated. RevFlag instances are not invalidated, but
1258 	 * are removed from all RevObjects.
1259 	 */
1260 	public final void reset() {
1261 		reset(0);
1262 	}
1263 
1264 	/**
1265 	 * Resets internal state and allows this instance to be used again.
1266 	 * <p>
1267 	 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
1268 	 * instances are not invalidated. RevFlag instances are not invalidated, but
1269 	 * are removed from all RevObjects.
1270 	 *
1271 	 * @param retainFlags
1272 	 *            application flags that should <b>not</b> be cleared from
1273 	 *            existing commit objects.
1274 	 */
1275 	public final void resetRetain(RevFlagSet retainFlags) {
1276 		reset(retainFlags.mask);
1277 	}
1278 
1279 	/**
1280 	 * Resets internal state and allows this instance to be used again.
1281 	 * <p>
1282 	 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
1283 	 * instances are not invalidated. RevFlag instances are not invalidated, but
1284 	 * are removed from all RevObjects.
1285 	 * <p>
1286 	 * See {@link #retainOnReset(RevFlag)} for an alternative that does not
1287 	 * require passing the flags during each reset.
1288 	 *
1289 	 * @param retainFlags
1290 	 *            application flags that should <b>not</b> be cleared from
1291 	 *            existing commit objects.
1292 	 */
1293 	public final void resetRetain(RevFlag... retainFlags) {
1294 		int mask = 0;
1295 		for (RevFlag flag : retainFlags)
1296 			mask |= flag.mask;
1297 		reset(mask);
1298 	}
1299 
1300 	/**
1301 	 * Resets internal state and allows this instance to be used again.
1302 	 * <p>
1303 	 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
1304 	 * instances are not invalidated. RevFlag instances are not invalidated, but
1305 	 * are removed from all RevObjects. The value of {@code firstParent} is
1306 	 * retained.
1307 	 *
1308 	 * @param retainFlags
1309 	 *            application flags that should <b>not</b> be cleared from
1310 	 *            existing commit objects.
1311 	 */
1312 	protected void reset(int retainFlags) {
1313 		finishDelayedFreeFlags();
1314 		retainFlags |= PARSED | retainOnReset;
1315 		final int clearFlags = ~retainFlags;
1316 
1317 		final FIFORevQueueRevQueue.html#FIFORevQueue">FIFORevQueue q = new FIFORevQueue();
1318 		for (RevCommit c : roots) {
1319 			if ((c.flags & clearFlags) == 0)
1320 				continue;
1321 			c.flags &= retainFlags;
1322 			c.reset();
1323 			q.add(c);
1324 		}
1325 
1326 		for (;;) {
1327 			final RevCommit c = q.next();
1328 			if (c == null)
1329 				break;
1330 			if (c.parents == null)
1331 				continue;
1332 			for (RevCommit p : c.parents) {
1333 				if ((p.flags & clearFlags) == 0)
1334 					continue;
1335 				p.flags &= retainFlags;
1336 				p.reset();
1337 				q.add(p);
1338 			}
1339 		}
1340 
1341 		roots.clear();
1342 		queue = new DateRevQueue(firstParent);
1343 		pending = new StartGenerator(this);
1344 	}
1345 
1346 	/**
1347 	 * Dispose all internal state and invalidate all RevObject instances.
1348 	 * <p>
1349 	 * All RevObject (and thus RevCommit, etc.) instances previously acquired
1350 	 * from this RevWalk are invalidated by a dispose call. Applications must
1351 	 * not retain or use RevObject instances obtained prior to the dispose call.
1352 	 * All RevFlag instances are also invalidated, and must not be reused.
1353 	 */
1354 	public void dispose() {
1355 		reader.close();
1356 		freeFlags = APP_FLAGS;
1357 		delayFreeFlags = 0;
1358 		retainOnReset = 0;
1359 		carryFlags = UNINTERESTING;
1360 		firstParent = false;
1361 		objects.clear();
1362 		roots.clear();
1363 		queue = new DateRevQueue(firstParent);
1364 		pending = new StartGenerator(this);
1365 		shallowCommitsInitialized = false;
1366 	}
1367 
1368 	/**
1369 	 * Like {@link #next()}, but if a checked exception is thrown during the
1370 	 * walk it is rethrown as a {@link RevWalkException}.
1371 	 *
1372 	 * @throws RevWalkException if an {@link IOException} was thrown.
1373 	 * @return next most recent commit; null if traversal is over.
1374 	 */
1375 	@Nullable
1376 	private RevCommit nextForIterator() {
1377 		try {
1378 			return next();
1379 		} catch (IOException e) {
1380 			throw new RevWalkException(e);
1381 		}
1382 	}
1383 
1384 	/**
1385 	 * {@inheritDoc}
1386 	 * <p>
1387 	 * Returns an Iterator over the commits of this walker.
1388 	 * <p>
1389 	 * The returned iterator is only useful for one walk. If this RevWalk gets
1390 	 * reset a new iterator must be obtained to walk over the new results.
1391 	 * <p>
1392 	 * Applications must not use both the Iterator and the {@link #next()} API
1393 	 * at the same time. Pick one API and use that for the entire walk.
1394 	 * <p>
1395 	 * If a checked exception is thrown during the walk (see {@link #next()}) it
1396 	 * is rethrown from the Iterator as a {@link RevWalkException}.
1397 	 *
1398 	 * @see RevWalkException
1399 	 */
1400 	@Override
1401 	public Iterator<RevCommit> iterator() {
1402 		RevCommit first = nextForIterator();
1403 
1404 		return new Iterator<RevCommit>() {
1405 			RevCommit next = first;
1406 
1407 			@Override
1408 			public boolean hasNext() {
1409 				return next != null;
1410 			}
1411 
1412 			@Override
1413 			public RevCommit next() {
1414 				RevCommit r = next;
1415 				next = nextForIterator();
1416 				return r;
1417 			}
1418 
1419 			@Override
1420 			public void remove() {
1421 				throw new UnsupportedOperationException();
1422 			}
1423 		};
1424 	}
1425 
1426 	/**
1427 	 * Throws an exception if we have started producing output.
1428 	 */
1429 	protected void assertNotStarted() {
1430 		if (isNotStarted())
1431 			return;
1432 		throw new IllegalStateException(JGitText.get().outputHasAlreadyBeenStarted);
1433 	}
1434 
1435 	/**
1436 	 * Throws an exception if any commits have been marked as start.
1437 	 * <p>
1438 	 * If {@link #markStart(RevCommit)} has already been called,
1439 	 * {@link #reset()} can be called to satisfy this condition.
1440 	 *
1441 	 * @since 5.5
1442 	 */
1443 	protected void assertNoCommitsMarkedStart() {
1444 		if (roots.isEmpty())
1445 			return;
1446 		throw new IllegalStateException(
1447 				JGitText.get().commitsHaveAlreadyBeenMarkedAsStart);
1448 	}
1449 
1450 	private boolean isNotStarted() {
1451 		return pending instanceof StartGenerator;
1452 	}
1453 
1454 	/**
1455 	 * Create and return an {@link org.eclipse.jgit.revwalk.ObjectWalk} using
1456 	 * the same objects.
1457 	 * <p>
1458 	 * Prior to using this method, the caller must reset this RevWalk to clean
1459 	 * any flags that were used during the last traversal.
1460 	 * <p>
1461 	 * The returned ObjectWalk uses the same ObjectReader, internal object pool,
1462 	 * and free RevFlags. Once the ObjectWalk is created, this RevWalk should
1463 	 * not be used anymore.
1464 	 *
1465 	 * @return a new walk, using the exact same object pool.
1466 	 */
1467 	public ObjectWalk toObjectWalkWithSameObjects() {
1468 		ObjectWalk ow = new ObjectWalk(reader);
1469 		RevWalk rw = ow;
1470 		rw.objects = objects;
1471 		rw.freeFlags = freeFlags;
1472 		return ow;
1473 	}
1474 
1475 	/**
1476 	 * Construct a new unparsed commit for the given object.
1477 	 *
1478 	 * @param id
1479 	 *            the object this walker requires a commit reference for.
1480 	 * @return a new unparsed reference for the object.
1481 	 */
1482 	protected RevCommit createCommit(AnyObjectId id) {
1483 		return new RevCommit(id);
1484 	}
1485 
1486 	void carryFlagsImpl(RevCommit c) {
1487 		final int carry = c.flags & carryFlags;
1488 		if (carry != 0)
1489 			RevCommit.carryFlags(c, carry);
1490 	}
1491 
1492 	/**
1493 	 * Assume additional commits are shallow (have no parents).
1494 	 * <p>
1495 	 * This method is a No-op if the collection is empty.
1496 	 *
1497 	 * @param ids
1498 	 *            commits that should be treated as shallow commits, in addition
1499 	 *            to any commits already known to be shallow by the repository.
1500 	 * @since 3.3
1501 	 */
1502 	public void assumeShallow(Collection<? extends ObjectId> ids) {
1503 		for (ObjectId id : ids)
1504 			lookupCommit(id).parents = RevCommit.NO_PARENTS;
1505 	}
1506 
1507 	/**
1508 	 * Reads the "shallow" file and applies it by setting the parents of shallow
1509 	 * commits to an empty array.
1510 	 * <p>
1511 	 * There is a sequencing problem if the first commit being parsed is a
1512 	 * shallow commit, since {@link RevCommit#parseCanonical(RevWalk, byte[])}
1513 	 * calls this method before its callers add the new commit to the
1514 	 * {@link RevWalk#objects} map. That means a call from this method to
1515 	 * {@link #lookupCommit(AnyObjectId)} fails to find that commit and creates
1516 	 * a new one, which is promptly discarded.
1517 	 * <p>
1518 	 * To avoid that, {@link RevCommit#parseCanonical(RevWalk, byte[])} passes
1519 	 * its commit to this method, so that this method can apply the shallow
1520 	 * state to it directly and avoid creating the duplicate commit object.
1521 	 *
1522 	 * @param rc
1523 	 *            the initial commit being parsed
1524 	 * @throws IOException
1525 	 *             if the shallow commits file can't be read
1526 	 */
1527 	void initializeShallowCommits(RevCommit rc) throws IOException {
1528 		if (shallowCommitsInitialized) {
1529 			throw new IllegalStateException(
1530 					JGitText.get().shallowCommitsAlreadyInitialized);
1531 		}
1532 
1533 		shallowCommitsInitialized = true;
1534 
1535 		if (reader == null) {
1536 			return;
1537 		}
1538 
1539 		for (ObjectId id : reader.getShallowCommits()) {
1540 			if (id.equals(rc.getId())) {
1541 				rc.parents = RevCommit.NO_PARENTS;
1542 			} else {
1543 				lookupCommit(id).parents = RevCommit.NO_PARENTS;
1544 			}
1545 		}
1546 	}
1547 }