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