Remove System.out.println from RevWalkFilterTest
[egit/chris.git] / org.spearce.jgit / src / org / spearce / jgit / revwalk / RevWalk.java
blobe68b9fd58b7e54c7b2dedb5483cd3822031acb5e
1 /*
2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.revwalk;
41 import java.io.IOException;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.EnumSet;
45 import java.util.Iterator;
47 import org.spearce.jgit.errors.IncorrectObjectTypeException;
48 import org.spearce.jgit.errors.MissingObjectException;
49 import org.spearce.jgit.errors.RevWalkException;
50 import org.spearce.jgit.lib.AnyObjectId;
51 import org.spearce.jgit.lib.Constants;
52 import org.spearce.jgit.lib.MutableObjectId;
53 import org.spearce.jgit.lib.ObjectId;
54 import org.spearce.jgit.lib.ObjectIdSubclassMap;
55 import org.spearce.jgit.lib.ObjectLoader;
56 import org.spearce.jgit.lib.Repository;
57 import org.spearce.jgit.lib.WindowCursor;
58 import org.spearce.jgit.revwalk.filter.RevFilter;
59 import org.spearce.jgit.treewalk.filter.TreeFilter;
61 /**
62 * Walks a commit graph and produces the matching commits in order.
63 * <p>
64 * A RevWalk instance can only be used once to generate results. Running a
65 * second time requires creating a new RevWalk instance, or invoking
66 * {@link #reset()} before starting again. Resetting an existing instance may be
67 * faster for some applications as commit body parsing can be avoided on the
68 * later invocations.
69 * <p>
70 * RevWalk instances are not thread-safe. Applications must either restrict
71 * usage of a RevWalk instance to a single thread, or implement their own
72 * synchronization at a higher level.
73 * <p>
74 * Multiple simultaneous RevWalk instances per {@link Repository} are permitted,
75 * even from concurrent threads. Equality of {@link RevCommit}s from two
76 * different RevWalk instances is never true, even if their {@link ObjectId}s
77 * are equal (and thus they describe the same commit).
78 * <p>
79 * The offered iterator is over the list of RevCommits described by the
80 * configuration of this instance. Applications should restrict themselves to
81 * using either the provided Iterator or {@link #next()}, but never use both on
82 * the same RevWalk at the same time. The Iterator may buffer RevCommits, while
83 * {@link #next()} does not.
85 public class RevWalk implements Iterable<RevCommit> {
86 /**
87 * Set on objects whose important header data has been loaded.
88 * <p>
89 * For a RevCommit this indicates we have pulled apart the tree and parent
90 * references from the raw bytes available in the repository and translated
91 * those to our own local RevTree and RevCommit instances. The raw buffer is
92 * also available for message and other header filtering.
93 * <p>
94 * For a RevTag this indicates we have pulled part the tag references to
95 * find out who the tag refers to, and what that object's type is.
97 static final int PARSED = 1 << 0;
99 /**
100 * Set on RevCommit instances added to our {@link #pending} queue.
101 * <p>
102 * We use this flag to avoid adding the same commit instance twice to our
103 * queue, especially if we reached it by more than one path.
105 static final int SEEN = 1 << 1;
108 * Set on RevCommit instances the caller does not want output.
109 * <p>
110 * We flag commits as uninteresting if the caller does not want commits
111 * reachable from a commit given to {@link #markUninteresting(RevCommit)}.
112 * This flag is always carried into the commit's parents and is a key part
113 * of the "rev-list B --not A" feature; A is marked UNINTERESTING.
115 static final int UNINTERESTING = 1 << 2;
118 * Set on a RevCommit that can collapse out of the history.
119 * <p>
120 * If the {@link #treeFilter} concluded that this commit matches his
121 * parents' for all of the paths that the filter is interested in then we
122 * mark the commit REWRITE. Later we can rewrite the parents of a REWRITE
123 * child to remove chains of REWRITE commits before we produce the child to
124 * the application.
126 * @see RewriteGenerator
128 static final int REWRITE = 1 << 3;
131 * Temporary mark for use within generators or filters.
132 * <p>
133 * This mark is only for local use within a single scope. If someone sets
134 * the mark they must unset it before any other code can see the mark.
136 static final int TEMP_MARK = 1 << 4;
139 * Temporary mark for use within {@link TopoSortGenerator}.
140 * <p>
141 * This mark indicates the commit could not produce when it wanted to, as at
142 * least one child was behind it. Commits with this flag are delayed until
143 * all children have been output first.
145 static final int TOPO_DELAY = 1 << 5;
147 /** Number of flag bits we keep internal for our own use. See above flags. */
148 static final int RESERVED_FLAGS = 6;
150 private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1);
152 final Repository db;
154 final WindowCursor curs;
156 final MutableObjectId idBuffer;
158 private final ObjectIdSubclassMap<RevObject> objects;
160 private int freeFlags = APP_FLAGS;
162 private int delayFreeFlags;
164 int carryFlags = UNINTERESTING;
166 private final ArrayList<RevCommit> roots;
168 AbstractRevQueue queue;
170 Generator pending;
172 private final EnumSet<RevSort> sorting;
174 private RevFilter filter;
176 private TreeFilter treeFilter;
178 private boolean retainBody;
181 * Create a new revision walker for a given repository.
183 * @param repo
184 * the repository the walker will obtain data from.
186 public RevWalk(final Repository repo) {
187 db = repo;
188 curs = new WindowCursor();
189 idBuffer = new MutableObjectId();
190 objects = new ObjectIdSubclassMap<RevObject>();
191 roots = new ArrayList<RevCommit>();
192 queue = new DateRevQueue();
193 pending = new StartGenerator(this);
194 sorting = EnumSet.of(RevSort.NONE);
195 filter = RevFilter.ALL;
196 treeFilter = TreeFilter.ALL;
197 retainBody = true;
201 * Get the repository this walker loads objects from.
203 * @return the repository this walker was created to read.
205 public Repository getRepository() {
206 return db;
210 * Mark a commit to start graph traversal from.
211 * <p>
212 * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain
213 * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as
214 * this method requires the commit to be parsed before it can be added as a
215 * root for the traversal.
216 * <p>
217 * The method will automatically parse an unparsed commit, but error
218 * handling may be more difficult for the application to explain why a
219 * RevCommit is not actually a commit. The object pool of this walker would
220 * also be 'poisoned' by the non-commit RevCommit.
222 * @param c
223 * the commit to start traversing from. The commit passed must be
224 * from this same revision walker.
225 * @throws MissingObjectException
226 * the commit supplied is not available from the object
227 * database. This usually indicates the supplied commit is
228 * invalid, but the reference was constructed during an earlier
229 * invocation to {@link #lookupCommit(AnyObjectId)}.
230 * @throws IncorrectObjectTypeException
231 * the object was not parsed yet and it was discovered during
232 * parsing that it is not actually a commit. This usually
233 * indicates the caller supplied a non-commit SHA-1 to
234 * {@link #lookupCommit(AnyObjectId)}.
235 * @throws IOException
236 * a pack file or loose object could not be read.
238 public void markStart(final RevCommit c) throws MissingObjectException,
239 IncorrectObjectTypeException, IOException {
240 if ((c.flags & SEEN) != 0)
241 return;
242 if ((c.flags & PARSED) == 0)
243 c.parseHeaders(this);
244 c.flags |= SEEN;
245 roots.add(c);
246 queue.add(c);
250 * Mark commits to start graph traversal from.
252 * @param list
253 * commits to start traversing from. The commits passed must be
254 * from this same revision walker.
255 * @throws MissingObjectException
256 * one of the commits supplied is not available from the object
257 * database. This usually indicates the supplied commit is
258 * invalid, but the reference was constructed during an earlier
259 * invocation to {@link #lookupCommit(AnyObjectId)}.
260 * @throws IncorrectObjectTypeException
261 * the object was not parsed yet and it was discovered during
262 * parsing that it is not actually a commit. This usually
263 * indicates the caller supplied a non-commit SHA-1 to
264 * {@link #lookupCommit(AnyObjectId)}.
265 * @throws IOException
266 * a pack file or loose object could not be read.
268 public void markStart(final Collection<RevCommit> list)
269 throws MissingObjectException, IncorrectObjectTypeException,
270 IOException {
271 for (final RevCommit c : list)
272 markStart(c);
276 * Mark a commit to not produce in the output.
277 * <p>
278 * Uninteresting commits denote not just themselves but also their entire
279 * ancestry chain, back until the merge base of an uninteresting commit and
280 * an otherwise interesting commit.
281 * <p>
282 * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain
283 * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as
284 * this method requires the commit to be parsed before it can be added as a
285 * root for the traversal.
286 * <p>
287 * The method will automatically parse an unparsed commit, but error
288 * handling may be more difficult for the application to explain why a
289 * RevCommit is not actually a commit. The object pool of this walker would
290 * also be 'poisoned' by the non-commit RevCommit.
292 * @param c
293 * the commit to start traversing from. The commit passed must be
294 * from this same revision walker.
295 * @throws MissingObjectException
296 * the commit supplied is not available from the object
297 * database. This usually indicates the supplied commit is
298 * invalid, but the reference was constructed during an earlier
299 * invocation to {@link #lookupCommit(AnyObjectId)}.
300 * @throws IncorrectObjectTypeException
301 * the object was not parsed yet and it was discovered during
302 * parsing that it is not actually a commit. This usually
303 * indicates the caller supplied a non-commit SHA-1 to
304 * {@link #lookupCommit(AnyObjectId)}.
305 * @throws IOException
306 * a pack file or loose object could not be read.
308 public void markUninteresting(final RevCommit c)
309 throws MissingObjectException, IncorrectObjectTypeException,
310 IOException {
311 c.flags |= UNINTERESTING;
312 carryFlagsImpl(c);
313 markStart(c);
317 * Determine if a commit is reachable from another commit.
318 * <p>
319 * A commit <code>base</code> is an ancestor of <code>tip</code> if we
320 * can find a path of commits that leads from <code>tip</code> and ends at
321 * <code>base</code>.
322 * <p>
323 * This utility function resets the walker, inserts the two supplied
324 * commits, and then executes a walk until an answer can be obtained.
325 * Currently allocated RevFlags that have been added to RevCommit instances
326 * will be retained through the reset.
328 * @param base
329 * commit the caller thinks is reachable from <code>tip</code>.
330 * @param tip
331 * commit to start iteration from, and which is most likely a
332 * descendant (child) of <code>base</code>.
333 * @return true if there is a path directly from <code>tip</code> to
334 * <code>base</code> (and thus <code>base</code> is fully merged
335 * into <code>tip</code>); false otherwise.
336 * @throws MissingObjectException
337 * one or or more of the next commit's parents are not available
338 * from the object database, but were thought to be candidates
339 * for traversal. This usually indicates a broken link.
340 * @throws IncorrectObjectTypeException
341 * one or or more of the next commit's parents are not actually
342 * commit objects.
343 * @throws IOException
344 * a pack file or loose object could not be read.
346 public boolean isMergedInto(final RevCommit base, final RevCommit tip)
347 throws MissingObjectException, IncorrectObjectTypeException,
348 IOException {
349 final RevFilter oldRF = filter;
350 final TreeFilter oldTF = treeFilter;
351 try {
352 finishDelayedFreeFlags();
353 reset(~freeFlags & APP_FLAGS);
354 filter = RevFilter.MERGE_BASE;
355 treeFilter = TreeFilter.ALL;
356 markStart(tip);
357 markStart(base);
358 return next() == base;
359 } finally {
360 filter = oldRF;
361 treeFilter = oldTF;
366 * Pop the next most recent commit.
368 * @return next most recent commit; null if traversal is over.
369 * @throws MissingObjectException
370 * one or or more of the next commit's parents are not available
371 * from the object database, but were thought to be candidates
372 * for traversal. This usually indicates a broken link.
373 * @throws IncorrectObjectTypeException
374 * one or or more of the next commit's parents are not actually
375 * commit objects.
376 * @throws IOException
377 * a pack file or loose object could not be read.
379 public RevCommit next() throws MissingObjectException,
380 IncorrectObjectTypeException, IOException {
381 return pending.next();
385 * Obtain the sort types applied to the commits returned.
387 * @return the sorting strategies employed. At least one strategy is always
388 * used, but that strategy may be {@link RevSort#NONE}.
390 public EnumSet<RevSort> getRevSort() {
391 return sorting.clone();
395 * Check whether the provided sorting strategy is enabled.
397 * @param sort
398 * a sorting strategy to look for.
399 * @return true if this strategy is enabled, false otherwise
401 public boolean hasRevSort(RevSort sort) {
402 return sorting.contains(sort);
406 * Select a single sorting strategy for the returned commits.
407 * <p>
408 * Disables all sorting strategies, then enables only the single strategy
409 * supplied by the caller.
411 * @param s
412 * a sorting strategy to enable.
414 public void sort(final RevSort s) {
415 assertNotStarted();
416 sorting.clear();
417 sorting.add(s);
421 * Add or remove a sorting strategy for the returned commits.
422 * <p>
423 * Multiple strategies can be applied at once, in which case some strategies
424 * may take precedence over others. As an example, {@link RevSort#TOPO} must
425 * take precedence over {@link RevSort#COMMIT_TIME_DESC}, otherwise it
426 * cannot enforce its ordering.
428 * @param s
429 * a sorting strategy to enable or disable.
430 * @param use
431 * true if this strategy should be used, false if it should be
432 * removed.
434 public void sort(final RevSort s, final boolean use) {
435 assertNotStarted();
436 if (use)
437 sorting.add(s);
438 else
439 sorting.remove(s);
441 if (sorting.size() > 1)
442 sorting.remove(RevSort.NONE);
443 else if (sorting.size() == 0)
444 sorting.add(RevSort.NONE);
448 * Get the currently configured commit filter.
450 * @return the current filter. Never null as a filter is always needed.
452 public RevFilter getRevFilter() {
453 return filter;
457 * Set the commit filter for this walker.
458 * <p>
459 * Multiple filters may be combined by constructing an arbitrary tree of
460 * <code>AndRevFilter</code> or <code>OrRevFilter</code> instances to
461 * describe the boolean expression required by the application. Custom
462 * filter implementations may also be constructed by applications.
463 * <p>
464 * Note that filters are not thread-safe and may not be shared by concurrent
465 * RevWalk instances. Every RevWalk must be supplied its own unique filter,
466 * unless the filter implementation specifically states it is (and always
467 * will be) thread-safe. Callers may use {@link RevFilter#clone()} to create
468 * a unique filter tree for this RevWalk instance.
470 * @param newFilter
471 * the new filter. If null the special {@link RevFilter#ALL}
472 * filter will be used instead, as it matches every commit.
473 * @see org.spearce.jgit.revwalk.filter.AndRevFilter
474 * @see org.spearce.jgit.revwalk.filter.OrRevFilter
476 public void setRevFilter(final RevFilter newFilter) {
477 assertNotStarted();
478 filter = newFilter != null ? newFilter : RevFilter.ALL;
482 * Get the tree filter used to simplify commits by modified paths.
484 * @return the current filter. Never null as a filter is always needed. If
485 * no filter is being applied {@link TreeFilter#ALL} is returned.
487 public TreeFilter getTreeFilter() {
488 return treeFilter;
492 * Set the tree filter used to simplify commits by modified paths.
493 * <p>
494 * If null or {@link TreeFilter#ALL} the path limiter is removed. Commits
495 * will not be simplified.
496 * <p>
497 * If non-null and not {@link TreeFilter#ALL} then the tree filter will be
498 * installed and commits will have their ancestry simplified to hide commits
499 * that do not contain tree entries matched by the filter.
500 * <p>
501 * Usually callers should be inserting a filter graph including
502 * {@link TreeFilter#ANY_DIFF} along with one or more
503 * {@link org.spearce.jgit.treewalk.filter.PathFilter} instances.
505 * @param newFilter
506 * new filter. If null the special {@link TreeFilter#ALL} filter
507 * will be used instead, as it matches everything.
508 * @see org.spearce.jgit.treewalk.filter.PathFilter
510 public void setTreeFilter(final TreeFilter newFilter) {
511 assertNotStarted();
512 treeFilter = newFilter != null ? newFilter : TreeFilter.ALL;
516 * Should the body of a commit or tag be retained after parsing its headers?
517 * <p>
518 * Usually the body is always retained, but some application code might not
519 * care and would prefer to discard the body of a commit as early as
520 * possible, to reduce memory usage.
522 * @return true if the body should be retained; false it is discarded.
524 public boolean isRetainBody() {
525 return retainBody;
529 * Set whether or not the body of a commit or tag is retained.
530 * <p>
531 * If a body of a commit or tag is not retained, the application must
532 * call {@link #parseBody(RevObject)} before the body can be safely
533 * accessed through the type specific access methods.
535 * @param retain true to retain bodies; false to discard them early.
537 public void setRetainBody(final boolean retain) {
538 retainBody = retain;
542 * Locate a reference to a blob without loading it.
543 * <p>
544 * The blob may or may not exist in the repository. It is impossible to tell
545 * from this method's return value.
547 * @param id
548 * name of the blob object.
549 * @return reference to the blob object. Never null.
551 public RevBlob lookupBlob(final AnyObjectId id) {
552 RevBlob c = (RevBlob) objects.get(id);
553 if (c == null) {
554 c = new RevBlob(id);
555 objects.add(c);
557 return c;
561 * Locate a reference to a tree without loading it.
562 * <p>
563 * The tree may or may not exist in the repository. It is impossible to tell
564 * from this method's return value.
566 * @param id
567 * name of the tree object.
568 * @return reference to the tree object. Never null.
570 public RevTree lookupTree(final AnyObjectId id) {
571 RevTree c = (RevTree) objects.get(id);
572 if (c == null) {
573 c = new RevTree(id);
574 objects.add(c);
576 return c;
580 * Locate a reference to a commit without loading it.
581 * <p>
582 * The commit may or may not exist in the repository. It is impossible to
583 * tell from this method's return value.
585 * @param id
586 * name of the commit object.
587 * @return reference to the commit object. Never null.
589 public RevCommit lookupCommit(final AnyObjectId id) {
590 RevCommit c = (RevCommit) objects.get(id);
591 if (c == null) {
592 c = createCommit(id);
593 objects.add(c);
595 return c;
599 * Locate a reference to any object without loading it.
600 * <p>
601 * The object may or may not exist in the repository. It is impossible to
602 * tell from this method's return value.
604 * @param id
605 * name of the object.
606 * @param type
607 * type of the object. Must be a valid Git object type.
608 * @return reference to the object. Never null.
610 public RevObject lookupAny(final AnyObjectId id, final int type) {
611 RevObject r = objects.get(id);
612 if (r == null) {
613 switch (type) {
614 case Constants.OBJ_COMMIT:
615 r = createCommit(id);
616 break;
617 case Constants.OBJ_TREE:
618 r = new RevTree(id);
619 break;
620 case Constants.OBJ_BLOB:
621 r = new RevBlob(id);
622 break;
623 case Constants.OBJ_TAG:
624 r = new RevTag(id);
625 break;
626 default:
627 throw new IllegalArgumentException("invalid git type: " + type);
629 objects.add(r);
631 return r;
635 * Locate a reference to a commit and immediately parse its content.
636 * <p>
637 * Unlike {@link #lookupCommit(AnyObjectId)} this method only returns
638 * successfully if the commit object exists, is verified to be a commit, and
639 * was parsed without error.
641 * @param id
642 * name of the commit object.
643 * @return reference to the commit object. Never null.
644 * @throws MissingObjectException
645 * the supplied commit does not exist.
646 * @throws IncorrectObjectTypeException
647 * the supplied id is not a commit or an annotated tag.
648 * @throws IOException
649 * a pack file or loose object could not be read.
651 public RevCommit parseCommit(final AnyObjectId id)
652 throws MissingObjectException, IncorrectObjectTypeException,
653 IOException {
654 RevObject c = parseAny(id);
655 while (c instanceof RevTag) {
656 c = ((RevTag) c).getObject();
657 parseHeaders(c);
659 if (!(c instanceof RevCommit))
660 throw new IncorrectObjectTypeException(id.toObjectId(),
661 Constants.TYPE_COMMIT);
662 return (RevCommit) c;
666 * Locate a reference to a tree.
667 * <p>
668 * This method only returns successfully if the tree object exists, is
669 * verified to be a tree.
671 * @param id
672 * name of the tree object, or a commit or annotated tag that may
673 * reference a tree.
674 * @return reference to the tree object. Never null.
675 * @throws MissingObjectException
676 * the supplied tree does not exist.
677 * @throws IncorrectObjectTypeException
678 * the supplied id is not a tree, a commit or an annotated tag.
679 * @throws IOException
680 * a pack file or loose object could not be read.
682 public RevTree parseTree(final AnyObjectId id)
683 throws MissingObjectException, IncorrectObjectTypeException,
684 IOException {
685 RevObject c = parseAny(id);
686 while (c instanceof RevTag) {
687 c = ((RevTag) c).getObject();
688 parseHeaders(c);
691 final RevTree t;
692 if (c instanceof RevCommit)
693 t = ((RevCommit) c).getTree();
694 else if (!(c instanceof RevTree))
695 throw new IncorrectObjectTypeException(id.toObjectId(),
696 Constants.TYPE_TREE);
697 else
698 t = (RevTree) c;
699 parseHeaders(t);
700 return t;
704 * Locate a reference to any object and immediately parse its headers.
705 * <p>
706 * This method only returns successfully if the object exists and was parsed
707 * without error. Parsing an object can be expensive as the type must be
708 * determined. For blobs this may mean the blob content was unpacked
709 * unnecessarily, and thrown away.
711 * @param id
712 * name of the object.
713 * @return reference to the object. Never null.
714 * @throws MissingObjectException
715 * the supplied does not exist.
716 * @throws IOException
717 * a pack file or loose object could not be read.
719 public RevObject parseAny(final AnyObjectId id)
720 throws MissingObjectException, IOException {
721 RevObject r = objects.get(id);
722 if (r == null) {
723 final ObjectLoader ldr = db.openObject(curs, id);
724 if (ldr == null)
725 throw new MissingObjectException(id.toObjectId(), "unknown");
726 final byte[] data = ldr.getCachedBytes();
727 final int type = ldr.getType();
728 switch (type) {
729 case Constants.OBJ_COMMIT: {
730 final RevCommit c = createCommit(id);
731 c.parseCanonical(this, data);
732 r = c;
733 break;
735 case Constants.OBJ_TREE: {
736 r = new RevTree(id);
737 r.flags |= PARSED;
738 break;
740 case Constants.OBJ_BLOB: {
741 r = new RevBlob(id);
742 r.flags |= PARSED;
743 break;
745 case Constants.OBJ_TAG: {
746 final RevTag t = new RevTag(id);
747 t.parseCanonical(this, data);
748 r = t;
749 break;
751 default:
752 throw new IllegalArgumentException("Bad object type: " + type);
754 objects.add(r);
755 } else
756 parseHeaders(r);
757 return r;
761 * Ensure the object's critical headers have been parsed.
762 * <p>
763 * This method only returns successfully if the object exists and was parsed
764 * without error.
766 * @param obj
767 * the object the caller needs to be parsed.
768 * @throws MissingObjectException
769 * the supplied does not exist.
770 * @throws IOException
771 * a pack file or loose object could not be read.
773 public void parseHeaders(final RevObject obj)
774 throws MissingObjectException, IOException {
775 if ((obj.flags & PARSED) == 0)
776 obj.parseHeaders(this);
780 * Ensure the object's fully body content is available.
781 * <p>
782 * This method only returns successfully if the object exists and was parsed
783 * without error.
785 * @param obj
786 * the object the caller needs to be parsed.
787 * @throws MissingObjectException
788 * the supplied does not exist.
789 * @throws IOException
790 * a pack file or loose object could not be read.
792 public void parseBody(final RevObject obj)
793 throws MissingObjectException, IOException {
794 obj.parseBody(this);
798 * Create a new flag for application use during walking.
799 * <p>
800 * Applications are only assured to be able to create 24 unique flags on any
801 * given revision walker instance. Any flags beyond 24 are offered only if
802 * the implementation has extra free space within its internal storage.
804 * @param name
805 * description of the flag, primarily useful for debugging.
806 * @return newly constructed flag instance.
807 * @throws IllegalArgumentException
808 * too many flags have been reserved on this revision walker.
810 public RevFlag newFlag(final String name) {
811 final int m = allocFlag();
812 return new RevFlag(this, name, m);
815 int allocFlag() {
816 if (freeFlags == 0)
817 throw new IllegalArgumentException(32 - RESERVED_FLAGS
818 + " flags already created.");
819 final int m = Integer.lowestOneBit(freeFlags);
820 freeFlags &= ~m;
821 return m;
825 * Automatically carry a flag from a child commit to its parents.
826 * <p>
827 * A carried flag is copied from the child commit onto its parents when the
828 * child commit is popped from the lowest level of walk's internal graph.
830 * @param flag
831 * the flag to carry onto parents, if set on a descendant.
833 public void carry(final RevFlag flag) {
834 if ((freeFlags & flag.mask) != 0)
835 throw new IllegalArgumentException(flag.name + " is disposed.");
836 if (flag.walker != this)
837 throw new IllegalArgumentException(flag.name + " not from this.");
838 carryFlags |= flag.mask;
842 * Automatically carry flags from a child commit to its parents.
843 * <p>
844 * A carried flag is copied from the child commit onto its parents when the
845 * child commit is popped from the lowest level of walk's internal graph.
847 * @param set
848 * the flags to carry onto parents, if set on a descendant.
850 public void carry(final Collection<RevFlag> set) {
851 for (final RevFlag flag : set)
852 carry(flag);
856 * Allow a flag to be recycled for a different use.
857 * <p>
858 * Recycled flags always come back as a different Java object instance when
859 * assigned again by {@link #newFlag(String)}.
860 * <p>
861 * If the flag was previously being carried, the carrying request is
862 * removed. Disposing of a carried flag while a traversal is in progress has
863 * an undefined behavior.
865 * @param flag
866 * the to recycle.
868 public void disposeFlag(final RevFlag flag) {
869 freeFlag(flag.mask);
872 void freeFlag(final int mask) {
873 if (isNotStarted()) {
874 freeFlags |= mask;
875 carryFlags &= ~mask;
876 } else {
877 delayFreeFlags |= mask;
881 private void finishDelayedFreeFlags() {
882 if (delayFreeFlags != 0) {
883 freeFlags |= delayFreeFlags;
884 carryFlags &= ~delayFreeFlags;
885 delayFreeFlags = 0;
890 * Resets internal state and allows this instance to be used again.
891 * <p>
892 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
893 * instances are not invalidated. RevFlag instances are not invalidated, but
894 * are removed from all RevObjects.
896 public final void reset() {
897 reset(0);
901 * Resets internal state and allows this instance to be used again.
902 * <p>
903 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
904 * instances are not invalidated. RevFlag instances are not invalidated, but
905 * are removed from all RevObjects.
907 * @param retainFlags
908 * application flags that should <b>not</b> be cleared from
909 * existing commit objects.
911 public final void resetRetain(final RevFlagSet retainFlags) {
912 reset(retainFlags.mask);
916 * Resets internal state and allows this instance to be used again.
917 * <p>
918 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
919 * instances are not invalidated. RevFlag instances are not invalidated, but
920 * are removed from all RevObjects.
922 * @param retainFlags
923 * application flags that should <b>not</b> be cleared from
924 * existing commit objects.
926 public final void resetRetain(final RevFlag... retainFlags) {
927 int mask = 0;
928 for (final RevFlag flag : retainFlags)
929 mask |= flag.mask;
930 reset(mask);
934 * Resets internal state and allows this instance to be used again.
935 * <p>
936 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
937 * instances are not invalidated. RevFlag instances are not invalidated, but
938 * are removed from all RevObjects.
940 * @param retainFlags
941 * application flags that should <b>not</b> be cleared from
942 * existing commit objects.
944 protected void reset(int retainFlags) {
945 finishDelayedFreeFlags();
946 retainFlags |= PARSED;
947 final int clearFlags = ~retainFlags;
949 final FIFORevQueue q = new FIFORevQueue();
950 for (final RevCommit c : roots) {
951 if ((c.flags & clearFlags) == 0)
952 continue;
953 c.flags &= retainFlags;
954 c.reset();
955 q.add(c);
958 for (;;) {
959 final RevCommit c = q.next();
960 if (c == null)
961 break;
962 if (c.parents == null)
963 continue;
964 for (final RevCommit p : c.parents) {
965 if ((p.flags & clearFlags) == 0)
966 continue;
967 p.flags &= retainFlags;
968 p.reset();
969 q.add(p);
973 curs.release();
974 roots.clear();
975 queue = new DateRevQueue();
976 pending = new StartGenerator(this);
980 * Dispose all internal state and invalidate all RevObject instances.
981 * <p>
982 * All RevObject (and thus RevCommit, etc.) instances previously acquired
983 * from this RevWalk are invalidated by a dispose call. Applications must
984 * not retain or use RevObject instances obtained prior to the dispose call.
985 * All RevFlag instances are also invalidated, and must not be reused.
987 public void dispose() {
988 freeFlags = APP_FLAGS;
989 delayFreeFlags = 0;
990 carryFlags = UNINTERESTING;
991 objects.clear();
992 curs.release();
993 roots.clear();
994 queue = new DateRevQueue();
995 pending = new StartGenerator(this);
999 * Returns an Iterator over the commits of this walker.
1000 * <p>
1001 * The returned iterator is only useful for one walk. If this RevWalk gets
1002 * reset a new iterator must be obtained to walk over the new results.
1003 * <p>
1004 * Applications must not use both the Iterator and the {@link #next()} API
1005 * at the same time. Pick one API and use that for the entire walk.
1006 * <p>
1007 * If a checked exception is thrown during the walk (see {@link #next()})
1008 * it is rethrown from the Iterator as a {@link RevWalkException}.
1010 * @return an iterator over this walker's commits.
1011 * @see RevWalkException
1013 public Iterator<RevCommit> iterator() {
1014 final RevCommit first;
1015 try {
1016 first = RevWalk.this.next();
1017 } catch (MissingObjectException e) {
1018 throw new RevWalkException(e);
1019 } catch (IncorrectObjectTypeException e) {
1020 throw new RevWalkException(e);
1021 } catch (IOException e) {
1022 throw new RevWalkException(e);
1025 return new Iterator<RevCommit>() {
1026 RevCommit next = first;
1028 public boolean hasNext() {
1029 return next != null;
1032 public RevCommit next() {
1033 try {
1034 final RevCommit r = next;
1035 next = RevWalk.this.next();
1036 return r;
1037 } catch (MissingObjectException e) {
1038 throw new RevWalkException(e);
1039 } catch (IncorrectObjectTypeException e) {
1040 throw new RevWalkException(e);
1041 } catch (IOException e) {
1042 throw new RevWalkException(e);
1046 public void remove() {
1047 throw new UnsupportedOperationException();
1052 /** Throws an exception if we have started producing output. */
1053 protected void assertNotStarted() {
1054 if (isNotStarted())
1055 return;
1056 throw new IllegalStateException("Output has already been started.");
1059 private boolean isNotStarted() {
1060 return pending instanceof StartGenerator;
1064 * Construct a new unparsed commit for the given object.
1066 * @param id
1067 * the object this walker requires a commit reference for.
1068 * @return a new unparsed reference for the object.
1070 protected RevCommit createCommit(final AnyObjectId id) {
1071 return new RevCommit(id);
1074 void carryFlagsImpl(final RevCommit c) {
1075 final int carry = c.flags & carryFlags;
1076 if (carry != 0)
1077 RevCommit.carryFlags(c, carry);