Remove getId from ObjectLoader API as its unnecessary overhead
[egit/qmx.git] / org.spearce.jgit / src / org / spearce / jgit / revwalk / RevWalk.java
blobdfb34d9cf887bae9ba7382905ab87bbe8a5d3a1a
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;
179 * Create a new revision walker for a given repository.
181 * @param repo
182 * the repository the walker will obtain data from.
184 public RevWalk(final Repository repo) {
185 db = repo;
186 curs = new WindowCursor();
187 idBuffer = new MutableObjectId();
188 objects = new ObjectIdSubclassMap<RevObject>();
189 roots = new ArrayList<RevCommit>();
190 queue = new FIFORevQueue();
191 pending = new StartGenerator(this);
192 sorting = EnumSet.of(RevSort.NONE);
193 filter = RevFilter.ALL;
194 treeFilter = TreeFilter.ALL;
198 * Get the repository this walker loads objects from.
200 * @return the repository this walker was created to read.
202 public Repository getRepository() {
203 return db;
207 * Mark a commit to start graph traversal from.
208 * <p>
209 * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain
210 * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as
211 * this method requires the commit to be parsed before it can be added as a
212 * root for the traversal.
213 * <p>
214 * The method will automatically parse an unparsed commit, but error
215 * handling may be more difficult for the application to explain why a
216 * RevCommit is not actually a commit. The object pool of this walker would
217 * also be 'poisoned' by the non-commit RevCommit.
219 * @param c
220 * the commit to start traversing from. The commit passed must be
221 * from this same revision walker.
222 * @throws MissingObjectException
223 * the commit supplied is not available from the object
224 * database. This usually indicates the supplied commit is
225 * invalid, but the reference was constructed during an earlier
226 * invocation to {@link #lookupCommit(AnyObjectId)}.
227 * @throws IncorrectObjectTypeException
228 * the object was not parsed yet and it was discovered during
229 * parsing that it is not actually a commit. This usually
230 * indicates the caller supplied a non-commit SHA-1 to
231 * {@link #lookupCommit(AnyObjectId)}.
232 * @throws IOException
233 * a pack file or loose object could not be read.
235 public void markStart(final RevCommit c) throws MissingObjectException,
236 IncorrectObjectTypeException, IOException {
237 if ((c.flags & SEEN) != 0)
238 return;
239 if ((c.flags & PARSED) == 0)
240 c.parse(this);
241 c.flags |= SEEN;
242 roots.add(c);
243 queue.add(c);
247 * Mark commits to start graph traversal from.
249 * @param list
250 * commits to start traversing from. The commits passed must be
251 * from this same revision walker.
252 * @throws MissingObjectException
253 * one of the commits supplied is not available from the object
254 * database. This usually indicates the supplied commit is
255 * invalid, but the reference was constructed during an earlier
256 * invocation to {@link #lookupCommit(AnyObjectId)}.
257 * @throws IncorrectObjectTypeException
258 * the object was not parsed yet and it was discovered during
259 * parsing that it is not actually a commit. This usually
260 * indicates the caller supplied a non-commit SHA-1 to
261 * {@link #lookupCommit(AnyObjectId)}.
262 * @throws IOException
263 * a pack file or loose object could not be read.
265 public void markStart(final Collection<RevCommit> list)
266 throws MissingObjectException, IncorrectObjectTypeException,
267 IOException {
268 for (final RevCommit c : list)
269 markStart(c);
273 * Mark a commit to not produce in the output.
274 * <p>
275 * Uninteresting commits denote not just themselves but also their entire
276 * ancestry chain, back until the merge base of an uninteresting commit and
277 * an otherwise interesting commit.
278 * <p>
279 * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain
280 * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as
281 * this method requires the commit to be parsed before it can be added as a
282 * root for the traversal.
283 * <p>
284 * The method will automatically parse an unparsed commit, but error
285 * handling may be more difficult for the application to explain why a
286 * RevCommit is not actually a commit. The object pool of this walker would
287 * also be 'poisoned' by the non-commit RevCommit.
289 * @param c
290 * the commit to start traversing from. The commit passed must be
291 * from this same revision walker.
292 * @throws MissingObjectException
293 * the commit supplied is not available from the object
294 * database. This usually indicates the supplied commit is
295 * invalid, but the reference was constructed during an earlier
296 * invocation to {@link #lookupCommit(AnyObjectId)}.
297 * @throws IncorrectObjectTypeException
298 * the object was not parsed yet and it was discovered during
299 * parsing that it is not actually a commit. This usually
300 * indicates the caller supplied a non-commit SHA-1 to
301 * {@link #lookupCommit(AnyObjectId)}.
302 * @throws IOException
303 * a pack file or loose object could not be read.
305 public void markUninteresting(final RevCommit c)
306 throws MissingObjectException, IncorrectObjectTypeException,
307 IOException {
308 c.flags |= UNINTERESTING;
309 carryFlagsImpl(c);
310 markStart(c);
314 * Determine if a commit is reachable from another commit.
315 * <p>
316 * A commit <code>base</code> is an ancestor of <code>tip</code> if we
317 * can find a path of commits that leads from <code>tip</code> and ends at
318 * <code>base</code>.
319 * <p>
320 * This utility function resets the walker, inserts the two supplied
321 * commits, and then executes a walk until an answer can be obtained.
322 * Currently allocated RevFlags that have been added to RevCommit instances
323 * will be retained through the reset.
325 * @param base
326 * commit the caller thinks is reachable from <code>tip</code>.
327 * @param tip
328 * commit to start iteration from, and which is most likely a
329 * descendant (child) of <code>base</code>.
330 * @return true if there is a path directly from <code>tip</code> to
331 * <code>base</code> (and thus <code>base</code> is fully merged
332 * into <code>tip</code>); false otherwise.
333 * @throws MissingObjectException
334 * one or or more of the next commit's parents are not available
335 * from the object database, but were thought to be candidates
336 * for traversal. This usually indicates a broken link.
337 * @throws IncorrectObjectTypeException
338 * one or or more of the next commit's parents are not actually
339 * commit objects.
340 * @throws IOException
341 * a pack file or loose object could not be read.
343 public boolean isMergedInto(final RevCommit base, final RevCommit tip)
344 throws MissingObjectException, IncorrectObjectTypeException,
345 IOException {
346 final RevFilter oldRF = filter;
347 final TreeFilter oldTF = treeFilter;
348 try {
349 finishDelayedFreeFlags();
350 reset(~freeFlags & APP_FLAGS);
351 filter = RevFilter.MERGE_BASE;
352 treeFilter = TreeFilter.ALL;
353 markStart(tip);
354 markStart(base);
355 return next() == base;
356 } finally {
357 filter = oldRF;
358 treeFilter = oldTF;
363 * Pop the next most recent commit.
365 * @return next most recent commit; null if traversal is over.
366 * @throws MissingObjectException
367 * one or or more of the next commit's parents are not available
368 * from the object database, but were thought to be candidates
369 * for traversal. This usually indicates a broken link.
370 * @throws IncorrectObjectTypeException
371 * one or or more of the next commit's parents are not actually
372 * commit objects.
373 * @throws IOException
374 * a pack file or loose object could not be read.
376 public RevCommit next() throws MissingObjectException,
377 IncorrectObjectTypeException, IOException {
378 return pending.next();
382 * Obtain the sort types applied to the commits returned.
384 * @return the sorting strategies employed. At least one strategy is always
385 * used, but that strategy may be {@link RevSort#NONE}.
387 public EnumSet<RevSort> getRevSort() {
388 return sorting.clone();
392 * Check whether the provided sorting strategy is enabled.
394 * @param sort
395 * a sorting strategy to look for.
396 * @return true if this strategy is enabled, false otherwise
398 public boolean hasRevSort(RevSort sort) {
399 return sorting.contains(sort);
403 * Select a single sorting strategy for the returned commits.
404 * <p>
405 * Disables all sorting strategies, then enables only the single strategy
406 * supplied by the caller.
408 * @param s
409 * a sorting strategy to enable.
411 public void sort(final RevSort s) {
412 assertNotStarted();
413 sorting.clear();
414 sorting.add(s);
418 * Add or remove a sorting strategy for the returned commits.
419 * <p>
420 * Multiple strategies can be applied at once, in which case some strategies
421 * may take precedence over others. As an example, {@link RevSort#TOPO} must
422 * take precedence over {@link RevSort#COMMIT_TIME_DESC}, otherwise it
423 * cannot enforce its ordering.
425 * @param s
426 * a sorting strategy to enable or disable.
427 * @param use
428 * true if this strategy should be used, false if it should be
429 * removed.
431 public void sort(final RevSort s, final boolean use) {
432 assertNotStarted();
433 if (use)
434 sorting.add(s);
435 else
436 sorting.remove(s);
438 if (sorting.size() > 1)
439 sorting.remove(RevSort.NONE);
440 else if (sorting.size() == 0)
441 sorting.add(RevSort.NONE);
445 * Get the currently configured commit filter.
447 * @return the current filter. Never null as a filter is always needed.
449 public RevFilter getRevFilter() {
450 return filter;
454 * Set the commit filter for this walker.
455 * <p>
456 * Multiple filters may be combined by constructing an arbitrary tree of
457 * <code>AndRevFilter</code> or <code>OrRevFilter</code> instances to
458 * describe the boolean expression required by the application. Custom
459 * filter implementations may also be constructed by applications.
460 * <p>
461 * Note that filters are not thread-safe and may not be shared by concurrent
462 * RevWalk instances. Every RevWalk must be supplied its own unique filter,
463 * unless the filter implementation specifically states it is (and always
464 * will be) thread-safe. Callers may use {@link RevFilter#clone()} to create
465 * a unique filter tree for this RevWalk instance.
467 * @param newFilter
468 * the new filter. If null the special {@link RevFilter#ALL}
469 * filter will be used instead, as it matches every commit.
470 * @see org.spearce.jgit.revwalk.filter.AndRevFilter
471 * @see org.spearce.jgit.revwalk.filter.OrRevFilter
473 public void setRevFilter(final RevFilter newFilter) {
474 assertNotStarted();
475 filter = newFilter != null ? newFilter : RevFilter.ALL;
479 * Get the tree filter used to simplify commits by modified paths.
481 * @return the current filter. Never null as a filter is always needed. If
482 * no filter is being applied {@link TreeFilter#ALL} is returned.
484 public TreeFilter getTreeFilter() {
485 return treeFilter;
489 * Set the tree filter used to simplify commits by modified paths.
490 * <p>
491 * If null or {@link TreeFilter#ALL} the path limiter is removed. Commits
492 * will not be simplified.
493 * <p>
494 * If non-null and not {@link TreeFilter#ALL} then the tree filter will be
495 * installed and commits will have their ancestry simplified to hide commits
496 * that do not contain tree entries matched by the filter.
497 * <p>
498 * Usually callers should be inserting a filter graph including
499 * {@link TreeFilter#ANY_DIFF} along with one or more
500 * {@link org.spearce.jgit.treewalk.filter.PathFilter} instances.
502 * @param newFilter
503 * new filter. If null the special {@link TreeFilter#ALL} filter
504 * will be used instead, as it matches everything.
505 * @see org.spearce.jgit.treewalk.filter.PathFilter
507 public void setTreeFilter(final TreeFilter newFilter) {
508 assertNotStarted();
509 treeFilter = newFilter != null ? newFilter : TreeFilter.ALL;
513 * Locate a reference to a tree without loading it.
514 * <p>
515 * The tree may or may not exist in the repository. It is impossible to tell
516 * from this method's return value.
518 * @param id
519 * name of the tree object.
520 * @return reference to the tree object. Never null.
522 public RevTree lookupTree(final AnyObjectId id) {
523 RevTree c = (RevTree) objects.get(id);
524 if (c == null) {
525 c = new RevTree(id);
526 objects.add(c);
528 return c;
532 * Locate a reference to a commit without loading it.
533 * <p>
534 * The commit may or may not exist in the repository. It is impossible to
535 * tell from this method's return value.
537 * @param id
538 * name of the commit object.
539 * @return reference to the commit object. Never null.
541 public RevCommit lookupCommit(final AnyObjectId id) {
542 RevCommit c = (RevCommit) objects.get(id);
543 if (c == null) {
544 c = createCommit(id);
545 objects.add(c);
547 return c;
551 * Locate a reference to any object without loading it.
552 * <p>
553 * The object may or may not exist in the repository. It is impossible to
554 * tell from this method's return value.
556 * @param id
557 * name of the object.
558 * @param type
559 * type of the object. Must be a valid Git object type.
560 * @return reference to the object. Never null.
562 public RevObject lookupAny(final AnyObjectId id, final int type) {
563 RevObject r = objects.get(id);
564 if (r == null) {
565 switch (type) {
566 case Constants.OBJ_COMMIT:
567 r = createCommit(id);
568 break;
569 case Constants.OBJ_TREE:
570 r = new RevTree(id);
571 break;
572 case Constants.OBJ_BLOB:
573 r = new RevBlob(id);
574 break;
575 case Constants.OBJ_TAG:
576 r = new RevTag(id);
577 break;
578 default:
579 throw new IllegalArgumentException("invalid git type: " + type);
581 objects.add(r);
583 return r;
587 * Locate a reference to a commit and immediately parse its content.
588 * <p>
589 * Unlike {@link #lookupCommit(AnyObjectId)} this method only returns
590 * successfully if the commit object exists, is verified to be a commit, and
591 * was parsed without error.
593 * @param id
594 * name of the commit object.
595 * @return reference to the commit object. Never null.
596 * @throws MissingObjectException
597 * the supplied commit does not exist.
598 * @throws IncorrectObjectTypeException
599 * the supplied id is not a commit or an annotated tag.
600 * @throws IOException
601 * a pack file or loose object could not be read.
603 public RevCommit parseCommit(final AnyObjectId id)
604 throws MissingObjectException, IncorrectObjectTypeException,
605 IOException {
606 RevObject c = parseAny(id);
607 while (c instanceof RevTag) {
608 c = ((RevTag) c).getObject();
609 parse(c);
611 if (!(c instanceof RevCommit))
612 throw new IncorrectObjectTypeException(id.toObjectId(),
613 Constants.TYPE_COMMIT);
614 return (RevCommit) c;
618 * Locate a reference to a tree.
619 * <p>
620 * This method only returns successfully if the tree object exists, is
621 * verified to be a tree, and was parsed without error.
623 * @param id
624 * name of the tree object, or a commit or annotated tag that may
625 * reference a tree.
626 * @return reference to the tree object. Never null.
627 * @throws MissingObjectException
628 * the supplied tree does not exist.
629 * @throws IncorrectObjectTypeException
630 * the supplied id is not a tree, a commit or an annotated tag.
631 * @throws IOException
632 * a pack file or loose object could not be read.
634 public RevTree parseTree(final AnyObjectId id)
635 throws MissingObjectException, IncorrectObjectTypeException,
636 IOException {
637 RevObject c = parseAny(id);
638 while (c instanceof RevTag) {
639 c = ((RevTag) c).getObject();
640 parse(c);
643 final RevTree t;
644 if (c instanceof RevCommit)
645 t = ((RevCommit) c).getTree();
646 else if (!(c instanceof RevTree))
647 throw new IncorrectObjectTypeException(id.toObjectId(),
648 Constants.TYPE_TREE);
649 else
650 t = (RevTree) c;
652 if ((t.flags & PARSED) != 0)
653 return t;
654 final ObjectLoader ldr = db.openObject(curs, t);
655 if (ldr == null)
656 throw new MissingObjectException(t, Constants.TYPE_TREE);
657 if (ldr.getType() != Constants.OBJ_TREE)
658 throw new IncorrectObjectTypeException(t, Constants.TYPE_TREE);
659 t.flags |= PARSED;
660 return t;
664 * Locate a reference to any object and immediately parse its content.
665 * <p>
666 * This method only returns successfully if the object exists and was parsed
667 * without error. Parsing an object can be expensive as the type must be
668 * determined. For blobs this may mean the blob content was unpacked
669 * unnecessarily, and thrown away.
671 * @param id
672 * name of the object.
673 * @return reference to the object. Never null.
674 * @throws MissingObjectException
675 * the supplied does not exist.
676 * @throws IOException
677 * a pack file or loose object could not be read.
679 public RevObject parseAny(final AnyObjectId id)
680 throws MissingObjectException, IOException {
681 RevObject r = objects.get(id);
682 if (r == null) {
683 final ObjectLoader ldr = db.openObject(curs, id);
684 if (ldr == null)
685 throw new MissingObjectException(id.toObjectId(), "unknown");
686 final byte[] data = ldr.getCachedBytes();
687 final int type = ldr.getType();
688 switch (type) {
689 case Constants.OBJ_COMMIT: {
690 final RevCommit c = createCommit(id);
691 c.parseCanonical(this, data);
692 r = c;
693 break;
695 case Constants.OBJ_TREE: {
696 r = new RevTree(id);
697 r.flags |= PARSED;
698 break;
700 case Constants.OBJ_BLOB: {
701 r = new RevBlob(id);
702 r.flags |= PARSED;
703 break;
705 case Constants.OBJ_TAG: {
706 final RevTag t = new RevTag(id);
707 t.parseCanonical(this, data);
708 r = t;
709 break;
711 default:
712 throw new IllegalArgumentException("Bad object type: " + type);
714 objects.add(r);
715 } else if ((r.flags & PARSED) == 0)
716 r.parse(this);
717 return r;
721 * Ensure the object's content has been parsed.
722 * <p>
723 * This method only returns successfully if the object exists and was parsed
724 * without error.
726 * @param obj
727 * the object the caller needs to be parsed.
728 * @throws MissingObjectException
729 * the supplied does not exist.
730 * @throws IOException
731 * a pack file or loose object could not be read.
733 public void parse(final RevObject obj) throws MissingObjectException,
734 IOException {
735 if ((obj.flags & PARSED) != 0)
736 return;
737 obj.parse(this);
741 * Create a new flag for application use during walking.
742 * <p>
743 * Applications are only assured to be able to create 24 unique flags on any
744 * given revision walker instance. Any flags beyond 24 are offered only if
745 * the implementation has extra free space within its internal storage.
747 * @param name
748 * description of the flag, primarily useful for debugging.
749 * @return newly constructed flag instance.
750 * @throws IllegalArgumentException
751 * too many flags have been reserved on this revision walker.
753 public RevFlag newFlag(final String name) {
754 final int m = allocFlag();
755 return new RevFlag(this, name, m);
758 int allocFlag() {
759 if (freeFlags == 0)
760 throw new IllegalArgumentException(32 - RESERVED_FLAGS
761 + " flags already created.");
762 final int m = Integer.lowestOneBit(freeFlags);
763 freeFlags &= ~m;
764 return m;
768 * Automatically carry a flag from a child commit to its parents.
769 * <p>
770 * A carried flag is copied from the child commit onto its parents when the
771 * child commit is popped from the lowest level of walk's internal graph.
773 * @param flag
774 * the flag to carry onto parents, if set on a descendant.
776 public void carry(final RevFlag flag) {
777 if ((freeFlags & flag.mask) != 0)
778 throw new IllegalArgumentException(flag.name + " is disposed.");
779 if (flag.walker != this)
780 throw new IllegalArgumentException(flag.name + " not from this.");
781 carryFlags |= flag.mask;
785 * Automatically carry flags from a child commit to its parents.
786 * <p>
787 * A carried flag is copied from the child commit onto its parents when the
788 * child commit is popped from the lowest level of walk's internal graph.
790 * @param set
791 * the flags to carry onto parents, if set on a descendant.
793 public void carry(final Collection<RevFlag> set) {
794 for (final RevFlag flag : set)
795 carry(flag);
799 * Allow a flag to be recycled for a different use.
800 * <p>
801 * Recycled flags always come back as a different Java object instance when
802 * assigned again by {@link #newFlag(String)}.
803 * <p>
804 * If the flag was previously being carried, the carrying request is
805 * removed. Disposing of a carried flag while a traversal is in progress has
806 * an undefined behavior.
808 * @param flag
809 * the to recycle.
811 public void disposeFlag(final RevFlag flag) {
812 freeFlag(flag.mask);
815 void freeFlag(final int mask) {
816 if (isNotStarted()) {
817 freeFlags |= mask;
818 carryFlags &= ~mask;
819 } else {
820 delayFreeFlags |= mask;
824 private void finishDelayedFreeFlags() {
825 if (delayFreeFlags != 0) {
826 freeFlags |= delayFreeFlags;
827 carryFlags &= ~delayFreeFlags;
828 delayFreeFlags = 0;
833 * Resets internal state and allows this instance to be used again.
834 * <p>
835 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
836 * instances are not invalidated. RevFlag instances are not invalidated, but
837 * are removed from all RevObjects.
839 public final void reset() {
840 reset(0);
844 * Resets internal state and allows this instance to be used again.
845 * <p>
846 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
847 * instances are not invalidated. RevFlag instances are not invalidated, but
848 * are removed from all RevObjects.
850 * @param retainFlags
851 * application flags that should <b>not</b> be cleared from
852 * existing commit objects.
854 public final void resetRetain(final RevFlagSet retainFlags) {
855 reset(retainFlags.mask);
859 * Resets internal state and allows this instance to be used again.
860 * <p>
861 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
862 * instances are not invalidated. RevFlag instances are not invalidated, but
863 * are removed from all RevObjects.
865 * @param retainFlags
866 * application flags that should <b>not</b> be cleared from
867 * existing commit objects.
869 public final void resetRetain(final RevFlag... retainFlags) {
870 int mask = 0;
871 for (final RevFlag flag : retainFlags)
872 mask |= flag.mask;
873 reset(mask);
877 * Resets internal state and allows this instance to be used again.
878 * <p>
879 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit)
880 * instances are not invalidated. RevFlag instances are not invalidated, but
881 * are removed from all RevObjects.
883 * @param retainFlags
884 * application flags that should <b>not</b> be cleared from
885 * existing commit objects.
887 protected void reset(int retainFlags) {
888 finishDelayedFreeFlags();
889 retainFlags |= PARSED;
890 final int clearFlags = ~retainFlags;
892 final FIFORevQueue q = new FIFORevQueue();
893 for (final RevCommit c : roots) {
894 if ((c.flags & clearFlags) == 0)
895 continue;
896 c.flags &= retainFlags;
897 c.reset();
898 q.add(c);
901 for (;;) {
902 final RevCommit c = q.next();
903 if (c == null)
904 break;
905 if (c.parents == null)
906 continue;
907 for (final RevCommit p : c.parents) {
908 if ((p.flags & clearFlags) == 0)
909 continue;
910 p.flags &= retainFlags;
911 p.reset();
912 q.add(p);
916 curs.release();
917 roots.clear();
918 queue = new FIFORevQueue();
919 pending = new StartGenerator(this);
923 * Dispose all internal state and invalidate all RevObject instances.
924 * <p>
925 * All RevObject (and thus RevCommit, etc.) instances previously acquired
926 * from this RevWalk are invalidated by a dispose call. Applications must
927 * not retain or use RevObject instances obtained prior to the dispose call.
928 * All RevFlag instances are also invalidated, and must not be reused.
930 public void dispose() {
931 freeFlags = APP_FLAGS;
932 delayFreeFlags = 0;
933 carryFlags = UNINTERESTING;
934 objects.clear();
935 curs.release();
936 roots.clear();
937 queue = new FIFORevQueue();
938 pending = new StartGenerator(this);
942 * Returns an Iterator over the commits of this walker.
943 * <p>
944 * The returned iterator is only useful for one walk. If this RevWalk gets
945 * reset a new iterator must be obtained to walk over the new results.
946 * <p>
947 * Applications must not use both the Iterator and the {@link #next()} API
948 * at the same time. Pick one API and use that for the entire walk.
949 * <p>
950 * If a checked exception is thrown during the walk (see {@link #next()})
951 * it is rethrown from the Iterator as a {@link RevWalkException}.
953 * @return an iterator over this walker's commits.
954 * @see RevWalkException
956 public Iterator<RevCommit> iterator() {
957 final RevCommit first;
958 try {
959 first = RevWalk.this.next();
960 } catch (MissingObjectException e) {
961 throw new RevWalkException(e);
962 } catch (IncorrectObjectTypeException e) {
963 throw new RevWalkException(e);
964 } catch (IOException e) {
965 throw new RevWalkException(e);
968 return new Iterator<RevCommit>() {
969 RevCommit next = first;
971 public boolean hasNext() {
972 return next != null;
975 public RevCommit next() {
976 try {
977 final RevCommit r = next;
978 next = RevWalk.this.next();
979 return r;
980 } catch (MissingObjectException e) {
981 throw new RevWalkException(e);
982 } catch (IncorrectObjectTypeException e) {
983 throw new RevWalkException(e);
984 } catch (IOException e) {
985 throw new RevWalkException(e);
989 public void remove() {
990 throw new UnsupportedOperationException();
995 /** Throws an exception if we have started producing output. */
996 protected void assertNotStarted() {
997 if (isNotStarted())
998 return;
999 throw new IllegalStateException("Output has already been started.");
1002 private boolean isNotStarted() {
1003 return pending instanceof StartGenerator;
1007 * Construct a new unparsed commit for the given object.
1009 * @param id
1010 * the object this walker requires a commit reference for.
1011 * @return a new unparsed reference for the object.
1013 protected RevCommit createCommit(final AnyObjectId id) {
1014 return new RevCommit(id);
1017 void carryFlagsImpl(final RevCommit c) throws MissingObjectException,
1018 IncorrectObjectTypeException, IOException {
1019 final int carry = c.flags & carryFlags;
1020 if (carry != 0)
1021 RevCommit.carryFlags(c, carry);