2 * Copyright (C) 2008-2009, Google Inc.
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * and other copyright owners as documented in the project's IP log.
6 * This program and the accompanying materials are made available
7 * under the terms of the Eclipse Distribution License v1.0 which
8 * accompanies this distribution, is reproduced below, and is
9 * available at http://www.eclipse.org/org/documents/edl-v10.php
11 * All rights reserved.
13 * Redistribution and use in source and binary forms, with or
14 * without modification, are permitted provided that the following
17 * - Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
20 * - Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials provided
23 * with the distribution.
25 * - Neither the name of the Eclipse Foundation, Inc. nor the
26 * names of its contributors may be used to endorse or promote
27 * products derived from this software without specific prior
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
31 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
32 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
39 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 package org
.eclipse
.jgit
.treewalk
;
47 import java
.io
.IOException
;
48 import java
.util
.Collections
;
50 import org
.eclipse
.jgit
.errors
.CorruptObjectException
;
51 import org
.eclipse
.jgit
.errors
.IncorrectObjectTypeException
;
52 import org
.eclipse
.jgit
.errors
.MissingObjectException
;
53 import org
.eclipse
.jgit
.errors
.StopWalkException
;
54 import org
.eclipse
.jgit
.lib
.AnyObjectId
;
55 import org
.eclipse
.jgit
.lib
.Constants
;
56 import org
.eclipse
.jgit
.lib
.FileMode
;
57 import org
.eclipse
.jgit
.lib
.MutableObjectId
;
58 import org
.eclipse
.jgit
.lib
.ObjectId
;
59 import org
.eclipse
.jgit
.lib
.Repository
;
60 import org
.eclipse
.jgit
.lib
.WindowCursor
;
61 import org
.eclipse
.jgit
.revwalk
.RevTree
;
62 import org
.eclipse
.jgit
.treewalk
.filter
.PathFilterGroup
;
63 import org
.eclipse
.jgit
.treewalk
.filter
.TreeFilter
;
64 import org
.eclipse
.jgit
.util
.RawParseUtils
;
67 * Walks one or more {@link AbstractTreeIterator}s in parallel.
69 * This class can perform n-way differences across as many trees as necessary.
71 * Each tree added must have the same root as existing trees in the walk.
73 * A TreeWalk instance can only be used once to generate results. Running a
74 * second time requires creating a new TreeWalk instance, or invoking
75 * {@link #reset()} and adding new trees before starting again. Resetting an
76 * existing instance may be faster for some applications as some internal
77 * buffers may be recycled.
79 * TreeWalk instances are not thread-safe. Applications must either restrict
80 * usage of a TreeWalk instance to a single thread, or implement their own
81 * synchronization at a higher level.
83 * Multiple simultaneous TreeWalk instances per {@link Repository} are
84 * permitted, even from concurrent threads.
86 public class TreeWalk
{
88 * Open a tree walk and filter to exactly one path.
90 * The returned tree walk is already positioned on the requested path, so
91 * the caller should not need to invoke {@link #next()} unless they are
92 * looking for a possible directory/file name conflict.
95 * repository to read tree object data from.
97 * single path to advance the tree walk instance into.
99 * one or more trees to walk through, all with the same root.
100 * @return a new tree walk configured for exactly this one path; null if no
101 * path was found in any of the trees.
102 * @throws IOException
103 * reading a pack file or loose object failed.
104 * @throws CorruptObjectException
105 * an tree object could not be read as its data stream did not
106 * appear to be a tree, or could not be inflated.
107 * @throws IncorrectObjectTypeException
108 * an object we expected to be a tree was not a tree.
109 * @throws MissingObjectException
110 * a tree object was not found.
112 public static TreeWalk
forPath(final Repository db
, final String path
,
113 final AnyObjectId
... trees
) throws MissingObjectException
,
114 IncorrectObjectTypeException
, CorruptObjectException
, IOException
{
115 final TreeWalk r
= new TreeWalk(db
);
116 r
.setFilter(PathFilterGroup
.createFromStrings(Collections
118 r
.setRecursive(r
.getFilter().shouldBeRecursive());
120 return r
.next() ? r
: null;
124 * Open a tree walk and filter to exactly one path.
126 * The returned tree walk is already positioned on the requested path, so
127 * the caller should not need to invoke {@link #next()} unless they are
128 * looking for a possible directory/file name conflict.
131 * repository to read tree object data from.
133 * single path to advance the tree walk instance into.
135 * the single tree to walk through.
136 * @return a new tree walk configured for exactly this one path; null if no
137 * path was found in any of the trees.
138 * @throws IOException
139 * reading a pack file or loose object failed.
140 * @throws CorruptObjectException
141 * an tree object could not be read as its data stream did not
142 * appear to be a tree, or could not be inflated.
143 * @throws IncorrectObjectTypeException
144 * an object we expected to be a tree was not a tree.
145 * @throws MissingObjectException
146 * a tree object was not found.
148 public static TreeWalk
forPath(final Repository db
, final String path
,
149 final RevTree tree
) throws MissingObjectException
,
150 IncorrectObjectTypeException
, CorruptObjectException
, IOException
{
151 return forPath(db
, path
, new ObjectId
[] { tree
});
154 private final Repository db
;
156 private final MutableObjectId idBuffer
= new MutableObjectId();
158 private final WindowCursor curs
= new WindowCursor();
160 private TreeFilter filter
;
162 AbstractTreeIterator
[] trees
;
164 private boolean recursive
;
166 private boolean postOrderTraversal
;
170 private boolean advance
;
172 private boolean postChildren
;
174 AbstractTreeIterator currentHead
;
177 * Create a new tree walker for a given repository.
180 * the repository the walker will obtain data from.
182 public TreeWalk(final Repository repo
) {
184 filter
= TreeFilter
.ALL
;
185 trees
= new AbstractTreeIterator
[] { new EmptyTreeIterator() };
189 * Get the repository this tree walker is reading from.
191 * @return the repository configured when the walker was created.
193 public Repository
getRepository() {
198 * Get the currently configured filter.
200 * @return the current filter. Never null as a filter is always needed.
202 public TreeFilter
getFilter() {
207 * Set the tree entry filter for this walker.
209 * Multiple filters may be combined by constructing an arbitrary tree of
210 * <code>AndTreeFilter</code> or <code>OrTreeFilter</code> instances to
211 * describe the boolean expression required by the application. Custom
212 * filter implementations may also be constructed by applications.
214 * Note that filters are not thread-safe and may not be shared by concurrent
215 * TreeWalk instances. Every TreeWalk must be supplied its own unique
216 * filter, unless the filter implementation specifically states it is (and
217 * always will be) thread-safe. Callers may use {@link TreeFilter#clone()}
218 * to create a unique filter tree for this TreeWalk instance.
221 * the new filter. If null the special {@link TreeFilter#ALL}
222 * filter will be used instead, as it matches every entry.
223 * @see org.eclipse.jgit.treewalk.filter.AndTreeFilter
224 * @see org.eclipse.jgit.treewalk.filter.OrTreeFilter
226 public void setFilter(final TreeFilter newFilter
) {
227 filter
= newFilter
!= null ? newFilter
: TreeFilter
.ALL
;
231 * Is this walker automatically entering into subtrees?
233 * If the walker is recursive then the caller will not see a subtree node
234 * and instead will only receive file nodes in all relevant subtrees.
236 * @return true if automatically entering subtrees is enabled.
238 public boolean isRecursive() {
243 * Set the walker to enter (or not enter) subtrees automatically.
245 * If recursive mode is enabled the walker will hide subtree nodes from the
246 * calling application and will produce only file level nodes. If a tree
247 * (directory) is deleted then all of the file level nodes will appear to be
248 * deleted, recursively, through as many levels as necessary to account for
252 * true to skip subtree nodes and only obtain files nodes.
254 public void setRecursive(final boolean b
) {
259 * Does this walker return a tree entry after it exits the subtree?
261 * If post order traversal is enabled then the walker will return a subtree
262 * after it has returned the last entry within that subtree. This may cause
263 * a subtree to be seen by the application twice if {@link #isRecursive()}
264 * is false, as the application will see it once, call
265 * {@link #enterSubtree()}, and then see it again as it leaves the subtree.
267 * If an application does not enable {@link #isRecursive()} and it does not
268 * call {@link #enterSubtree()} then the tree is returned only once as none
269 * of the children were processed.
271 * @return true if subtrees are returned after entries within the subtree.
273 public boolean isPostOrderTraversal() {
274 return postOrderTraversal
;
278 * Set the walker to return trees after their children.
281 * true to get trees after their children.
282 * @see #isPostOrderTraversal()
284 public void setPostOrderTraversal(final boolean b
) {
285 postOrderTraversal
= b
;
288 /** Reset this walker so new tree iterators can be added to it. */
289 public void reset() {
290 trees
= new AbstractTreeIterator
[0];
296 * Reset this walker to run over a single existing tree.
299 * the tree we need to parse. The walker will execute over this
300 * single tree if the reset is successful.
301 * @throws MissingObjectException
302 * the given tree object does not exist in this repository.
303 * @throws IncorrectObjectTypeException
304 * the given object id does not denote a tree, but instead names
305 * some other non-tree type of object. Note that commits are not
306 * trees, even if they are sometimes called a "tree-ish".
307 * @throws CorruptObjectException
308 * the object claimed to be a tree, but its contents did not
309 * appear to be a tree. The repository may have data corruption.
310 * @throws IOException
311 * a loose object or pack file could not be read.
313 public void reset(final AnyObjectId id
) throws MissingObjectException
,
314 IncorrectObjectTypeException
, CorruptObjectException
, IOException
{
315 if (trees
.length
== 1) {
316 AbstractTreeIterator o
= trees
[0];
317 while (o
.parent
!= null)
319 if (o
instanceof CanonicalTreeParser
) {
322 ((CanonicalTreeParser
) o
).reset(db
, id
, curs
);
325 trees
[0] = parserFor(id
);
328 trees
= new AbstractTreeIterator
[] { parserFor(id
) };
336 * Reset this walker to run over a set of existing trees.
339 * the trees we need to parse. The walker will execute over this
340 * many parallel trees if the reset is successful.
341 * @throws MissingObjectException
342 * the given tree object does not exist in this repository.
343 * @throws IncorrectObjectTypeException
344 * the given object id does not denote a tree, but instead names
345 * some other non-tree type of object. Note that commits are not
346 * trees, even if they are sometimes called a "tree-ish".
347 * @throws CorruptObjectException
348 * the object claimed to be a tree, but its contents did not
349 * appear to be a tree. The repository may have data corruption.
350 * @throws IOException
351 * a loose object or pack file could not be read.
353 public void reset(final AnyObjectId
[] ids
) throws MissingObjectException
,
354 IncorrectObjectTypeException
, CorruptObjectException
, IOException
{
355 final int oldLen
= trees
.length
;
356 final int newLen
= ids
.length
;
357 final AbstractTreeIterator
[] r
= newLen
== oldLen ? trees
358 : new AbstractTreeIterator
[newLen
];
359 for (int i
= 0; i
< newLen
; i
++) {
360 AbstractTreeIterator o
;
364 while (o
.parent
!= null)
366 if (o
instanceof CanonicalTreeParser
&& o
.pathOffset
== 0) {
369 ((CanonicalTreeParser
) o
).reset(db
, ids
[i
], curs
);
375 o
= parserFor(ids
[i
]);
385 * Add an already existing tree object for walking.
387 * The position of this tree is returned to the caller, in case the caller
388 * has lost track of the order they added the trees into the walker.
390 * The tree must have the same root as existing trees in the walk.
393 * identity of the tree object the caller wants walked.
394 * @return position of this tree within the walker.
395 * @throws MissingObjectException
396 * the given tree object does not exist in this repository.
397 * @throws IncorrectObjectTypeException
398 * the given object id does not denote a tree, but instead names
399 * some other non-tree type of object. Note that commits are not
400 * trees, even if they are sometimes called a "tree-ish".
401 * @throws CorruptObjectException
402 * the object claimed to be a tree, but its contents did not
403 * appear to be a tree. The repository may have data corruption.
404 * @throws IOException
405 * a loose object or pack file could not be read.
407 public int addTree(final ObjectId id
) throws MissingObjectException
,
408 IncorrectObjectTypeException
, CorruptObjectException
, IOException
{
409 return addTree(parserFor(id
));
413 * Add an already created tree iterator for walking.
415 * The position of this tree is returned to the caller, in case the caller
416 * has lost track of the order they added the trees into the walker.
418 * The tree which the iterator operates on must have the same root as
419 * existing trees in the walk.
422 * an iterator to walk over. The iterator should be new, with no
423 * parent, and should still be positioned before the first entry.
424 * The tree which the iterator operates on must have the same root
425 * as other trees in the walk.
427 * @return position of this tree within the walker.
428 * @throws CorruptObjectException
429 * the iterator was unable to obtain its first entry, due to
430 * possible data corruption within the backing data store.
432 public int addTree(final AbstractTreeIterator p
)
433 throws CorruptObjectException
{
434 final int n
= trees
.length
;
435 final AbstractTreeIterator
[] newTrees
= new AbstractTreeIterator
[n
+ 1];
437 System
.arraycopy(trees
, 0, newTrees
, 0, n
);
447 * Get the number of trees known to this walker.
449 * @return the total number of trees this walker is iterating over.
451 public int getTreeCount() {
456 * Advance this walker to the next relevant entry.
458 * @return true if there is an entry available; false if all entries have
459 * been walked and the walk of this set of tree iterators is over.
460 * @throws MissingObjectException
461 * {@link #isRecursive()} was enabled, a subtree was found, but
462 * the subtree object does not exist in this repository. The
463 * repository may be missing objects.
464 * @throws IncorrectObjectTypeException
465 * {@link #isRecursive()} was enabled, a subtree was found, and
466 * the subtree id does not denote a tree, but instead names some
467 * other non-tree type of object. The repository may have data
469 * @throws CorruptObjectException
470 * the contents of a tree did not appear to be a tree. The
471 * repository may have data corruption.
472 * @throws IOException
473 * a loose object or pack file could not be read.
475 public boolean next() throws MissingObjectException
,
476 IncorrectObjectTypeException
, CorruptObjectException
, IOException
{
480 postChildren
= false;
485 final AbstractTreeIterator t
= min();
489 if (postOrderTraversal
) {
501 if (!filter
.include(this)) {
506 if (recursive
&& FileMode
.TREE
.equals(t
.mode
)) {
514 } catch (StopWalkException stop
) {
515 for (final AbstractTreeIterator t
: trees
)
522 * Obtain the tree iterator for the current entry.
524 * Entering into (or exiting out of) a subtree causes the current tree
525 * iterator instance to be changed for the nth tree. This allows the tree
526 * iterators to manage only one list of items, with the diving handled by
530 * type of the tree iterator expected by the caller.
532 * tree to obtain the current iterator of.
534 * type of the tree iterator expected by the caller.
535 * @return r the current iterator of the requested type; null if the tree
536 * has no entry to match the current path.
538 public <T
extends AbstractTreeIterator
> T
getTree(final int nth
,
539 final Class
<T
> clazz
) {
540 final AbstractTreeIterator t
= trees
[nth
];
541 return t
.matches
== currentHead ?
(T
) t
: null;
545 * Obtain the raw {@link FileMode} bits for the current entry.
547 * Every added tree supplies mode bits, even if the tree does not contain
548 * the current entry. In the latter case {@link FileMode#MISSING}'s mode
549 * bits (0) are returned.
552 * tree to obtain the mode bits from.
553 * @return mode bits for the current entry of the nth tree.
554 * @see FileMode#fromBits(int)
556 public int getRawMode(final int nth
) {
557 final AbstractTreeIterator t
= trees
[nth
];
558 return t
.matches
== currentHead ? t
.mode
: 0;
562 * Obtain the {@link FileMode} for the current entry.
564 * Every added tree supplies a mode, even if the tree does not contain the
565 * current entry. In the latter case {@link FileMode#MISSING} is returned.
568 * tree to obtain the mode from.
569 * @return mode for the current entry of the nth tree.
571 public FileMode
getFileMode(final int nth
) {
572 return FileMode
.fromBits(getRawMode(nth
));
576 * Obtain the ObjectId for the current entry.
578 * Using this method to compare ObjectId values between trees of this walker
579 * is very inefficient. Applications should try to use
580 * {@link #idEqual(int, int)} or {@link #getObjectId(MutableObjectId, int)}
583 * Every tree supplies an object id, even if the tree does not contain the
584 * current entry. In the latter case {@link ObjectId#zeroId()} is returned.
587 * tree to obtain the object identifier from.
588 * @return object identifier for the current tree entry.
589 * @see #getObjectId(MutableObjectId, int)
590 * @see #idEqual(int, int)
592 public ObjectId
getObjectId(final int nth
) {
593 final AbstractTreeIterator t
= trees
[nth
];
594 return t
.matches
== currentHead ? t
.getEntryObjectId() : ObjectId
599 * Obtain the ObjectId for the current entry.
601 * Every tree supplies an object id, even if the tree does not contain the
602 * current entry. In the latter case {@link ObjectId#zeroId()} is supplied.
604 * Applications should try to use {@link #idEqual(int, int)} when possible
605 * as it avoids conversion overheads.
608 * buffer to copy the object id into.
610 * tree to obtain the object identifier from.
611 * @see #idEqual(int, int)
613 public void getObjectId(final MutableObjectId out
, final int nth
) {
614 final AbstractTreeIterator t
= trees
[nth
];
615 if (t
.matches
== currentHead
)
616 t
.getEntryObjectId(out
);
622 * Compare two tree's current ObjectId values for equality.
625 * first tree to compare the object id from.
627 * second tree to compare the object id from.
629 * <code>getObjectId(nthA).equals(getObjectId(nthB))</code>.
630 * @see #getObjectId(int)
632 public boolean idEqual(final int nthA
, final int nthB
) {
633 final AbstractTreeIterator ch
= currentHead
;
634 final AbstractTreeIterator a
= trees
[nthA
];
635 final AbstractTreeIterator b
= trees
[nthB
];
636 if (a
.matches
== ch
&& b
.matches
== ch
)
638 if (a
.matches
!= ch
&& b
.matches
!= ch
) {
639 // If neither tree matches the current path node then neither
640 // tree has this entry. In such case the ObjectId is zero(),
641 // and zero() is always equal to zero().
649 * Get the current entry's name within its parent tree.
651 * This method is not very efficient and is primarily meant for debugging
652 * and final output generation. Applications should try to avoid calling it,
653 * and if invoked do so only once per interesting entry, where the name is
654 * absolutely required for correct function.
656 * @return name of the current entry within the parent tree (or directory).
657 * The name never includes a '/'.
659 public String
getNameString() {
660 final AbstractTreeIterator t
= currentHead
;
661 final int off
= t
.pathOffset
;
662 final int end
= t
.pathLen
;
663 return RawParseUtils
.decode(Constants
.CHARSET
, t
.path
, off
, end
);
667 * Get the current entry's complete path.
669 * This method is not very efficient and is primarily meant for debugging
670 * and final output generation. Applications should try to avoid calling it,
671 * and if invoked do so only once per interesting entry, where the name is
672 * absolutely required for correct function.
674 * @return complete path of the current entry, from the root of the
675 * repository. If the current entry is in a subtree there will be at
676 * least one '/' in the returned string.
678 public String
getPathString() {
679 return pathOf(currentHead
);
683 * Get the current entry's complete path as a UTF-8 byte array.
685 * @return complete path of the current entry, from the root of the
686 * repository. If the current entry is in a subtree there will be at
687 * least one '/' in the returned string.
689 public byte[] getRawPath() {
690 final AbstractTreeIterator t
= currentHead
;
691 final int n
= t
.pathLen
;
692 final byte[] r
= new byte[n
];
693 System
.arraycopy(t
.path
, 0, r
, 0, n
);
698 * Test if the supplied path matches the current entry's path.
700 * This method tests that the supplied path is exactly equal to the current
701 * entry, or is one of its parent directories. It is faster to use this
702 * method then to use {@link #getPathString()} to first create a String
703 * object, then test <code>startsWith</code> or some other type of string
707 * path buffer to test. Callers should ensure the path does not
708 * end with '/' prior to invocation.
710 * number of bytes from <code>buf</code> to test.
711 * @return < 0 if p is before the current path; 0 if p matches the current
712 * path; 1 if the current path is past p and p will never match
713 * again on this tree walk.
715 public int isPathPrefix(final byte[] p
, final int pLen
) {
716 final AbstractTreeIterator t
= currentHead
;
717 final byte[] c
= t
.path
;
718 final int cLen
= t
.pathLen
;
721 for (ci
= 0; ci
< cLen
&& ci
< pLen
; ci
++) {
722 final int c_value
= (c
[ci
] & 0xff) - (p
[ci
] & 0xff);
728 // Ran out of pattern but we still had current data.
729 // If c[ci] == '/' then pattern matches the subtree.
730 // Otherwise we cannot be certain so we return -1.
732 return c
[ci
] == '/' ?
0 : -1;
736 // Ran out of current, but we still have pattern data.
737 // If p[ci] == '/' then pattern matches this subtree,
738 // otherwise we cannot be certain so we return -1.
740 return p
[ci
] == '/' ?
0 : -1;
743 // Both strings are identical.
749 * Test if the supplied path matches (being suffix of) the current entry's
752 * This method tests that the supplied path is exactly equal to the current
753 * entry, or is relative to one of entry's parent directories. It is faster
754 * to use this method then to use {@link #getPathString()} to first create
755 * a String object, then test <code>endsWith</code> or some other type of
756 * string match function.
759 * path buffer to test.
761 * number of bytes from <code>buf</code> to test.
762 * @return true if p is suffix of the current path;
765 public boolean isPathSuffix(final byte[] p
, final int pLen
) {
766 final AbstractTreeIterator t
= currentHead
;
767 final byte[] c
= t
.path
;
768 final int cLen
= t
.pathLen
;
771 for (ci
= 1; ci
< cLen
&& ci
< pLen
; ci
++) {
772 if (c
[cLen
-ci
] != p
[pLen
-ci
])
780 * Get the current subtree depth of this walker.
782 * @return the current subtree depth of this walker.
784 public int getDepth() {
789 * Is the current entry a subtree?
791 * This method is faster then testing the raw mode bits of all trees to see
792 * if any of them are a subtree. If at least one is a subtree then this
793 * method will return true.
795 * @return true if {@link #enterSubtree()} will work on the current node.
797 public boolean isSubtree() {
798 return FileMode
.TREE
.equals(currentHead
.mode
);
802 * Is the current entry a subtree returned after its children?
804 * @return true if the current node is a tree that has been returned after
805 * its children were already processed.
806 * @see #isPostOrderTraversal()
808 public boolean isPostChildren() {
809 return postChildren
&& isSubtree();
813 * Enter into the current subtree.
815 * If the current entry is a subtree this method arranges for its children
816 * to be returned before the next sibling following the subtree is returned.
818 * @throws MissingObjectException
819 * a subtree was found, but the subtree object does not exist in
820 * this repository. The repository may be missing objects.
821 * @throws IncorrectObjectTypeException
822 * a subtree was found, and the subtree id does not denote a
823 * tree, but instead names some other non-tree type of object.
824 * The repository may have data corruption.
825 * @throws CorruptObjectException
826 * the contents of a tree did not appear to be a tree. The
827 * repository may have data corruption.
828 * @throws IOException
829 * a loose object or pack file could not be read.
831 public void enterSubtree() throws MissingObjectException
,
832 IncorrectObjectTypeException
, CorruptObjectException
, IOException
{
833 final AbstractTreeIterator ch
= currentHead
;
834 final AbstractTreeIterator
[] tmp
= new AbstractTreeIterator
[trees
.length
];
835 for (int i
= 0; i
< trees
.length
; i
++) {
836 final AbstractTreeIterator t
= trees
[i
];
837 final AbstractTreeIterator n
;
838 if (t
.matches
== ch
&& !t
.eof() && FileMode
.TREE
.equals(t
.mode
))
839 n
= t
.createSubtreeIterator(db
, idBuffer
, curs
);
841 n
= t
.createEmptyTreeIterator();
846 System
.arraycopy(tmp
, 0, trees
, 0, trees
.length
);
849 AbstractTreeIterator
min() throws CorruptObjectException
{
851 AbstractTreeIterator minRef
= trees
[i
];
852 while (minRef
.eof() && ++i
< trees
.length
)
857 minRef
.matches
= minRef
;
858 while (++i
< trees
.length
) {
859 final AbstractTreeIterator t
= trees
[i
];
862 final int cmp
= t
.pathCompare(minRef
);
866 } else if (cmp
== 0) {
874 void popEntriesEqual() throws CorruptObjectException
{
875 final AbstractTreeIterator ch
= currentHead
;
876 for (int i
= 0; i
< trees
.length
; i
++) {
877 final AbstractTreeIterator t
= trees
[i
];
878 if (t
.matches
== ch
) {
885 void skipEntriesEqual() throws CorruptObjectException
{
886 final AbstractTreeIterator ch
= currentHead
;
887 for (int i
= 0; i
< trees
.length
; i
++) {
888 final AbstractTreeIterator t
= trees
[i
];
889 if (t
.matches
== ch
) {
896 private void exitSubtree() {
898 for (int i
= 0; i
< trees
.length
; i
++)
899 trees
[i
] = trees
[i
].parent
;
901 AbstractTreeIterator minRef
= null;
902 for (final AbstractTreeIterator t
: trees
) {
905 if (minRef
== null || t
.pathCompare(minRef
) < 0)
908 currentHead
= minRef
;
911 private CanonicalTreeParser
parserFor(final AnyObjectId id
)
912 throws IncorrectObjectTypeException
, IOException
{
913 final CanonicalTreeParser p
= new CanonicalTreeParser();
914 p
.reset(db
, id
, curs
);
918 static String
pathOf(final AbstractTreeIterator t
) {
919 return RawParseUtils
.decode(Constants
.CHARSET
, t
.path
, 0, t
.pathLen
);