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