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