View Javadoc
1   /*
2    * Copyright (C) 2008-2009, Google Inc.
3    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
4    *
5    * This program and the accompanying materials are made available under the
6    * terms of the Eclipse Distribution License v. 1.0 which is available at
7    * https://www.eclipse.org/org/documents/edl-v10.php.
8    *
9    * SPDX-License-Identifier: BSD-3-Clause
10   */
11  
12  package org.eclipse.jgit.treewalk;
13  
14  import static java.nio.charset.StandardCharsets.UTF_8;
15  
16  import java.io.IOException;
17  import java.util.HashMap;
18  import java.util.Map;
19  import java.util.Set;
20  import java.util.regex.Matcher;
21  
22  import org.eclipse.jgit.annotations.Nullable;
23  import org.eclipse.jgit.api.errors.JGitInternalException;
24  import org.eclipse.jgit.attributes.Attribute;
25  import org.eclipse.jgit.attributes.Attributes;
26  import org.eclipse.jgit.attributes.AttributesHandler;
27  import org.eclipse.jgit.attributes.AttributesNodeProvider;
28  import org.eclipse.jgit.attributes.AttributesProvider;
29  import org.eclipse.jgit.attributes.FilterCommandRegistry;
30  import org.eclipse.jgit.dircache.DirCacheBuildIterator;
31  import org.eclipse.jgit.dircache.DirCacheIterator;
32  import org.eclipse.jgit.errors.CorruptObjectException;
33  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
34  import org.eclipse.jgit.errors.MissingObjectException;
35  import org.eclipse.jgit.errors.StopWalkException;
36  import org.eclipse.jgit.lib.AnyObjectId;
37  import org.eclipse.jgit.lib.Config;
38  import org.eclipse.jgit.lib.ConfigConstants;
39  import org.eclipse.jgit.lib.Constants;
40  import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
41  import org.eclipse.jgit.lib.FileMode;
42  import org.eclipse.jgit.lib.MutableObjectId;
43  import org.eclipse.jgit.lib.ObjectId;
44  import org.eclipse.jgit.lib.ObjectReader;
45  import org.eclipse.jgit.lib.Repository;
46  import org.eclipse.jgit.revwalk.RevTree;
47  import org.eclipse.jgit.treewalk.filter.PathFilter;
48  import org.eclipse.jgit.treewalk.filter.TreeFilter;
49  import org.eclipse.jgit.util.QuotedString;
50  import org.eclipse.jgit.util.RawParseUtils;
51  import org.eclipse.jgit.util.io.EolStreamTypeUtil;
52  
53  /**
54   * Walks one or more {@link org.eclipse.jgit.treewalk.AbstractTreeIterator}s in
55   * parallel.
56   * <p>
57   * This class can perform n-way differences across as many trees as necessary.
58   * <p>
59   * Each tree added must have the same root as existing trees in the walk.
60   * <p>
61   * A TreeWalk instance can only be used once to generate results. Running a
62   * second time requires creating a new TreeWalk instance, or invoking
63   * {@link #reset()} and adding new trees before starting again. Resetting an
64   * existing instance may be faster for some applications as some internal
65   * buffers may be recycled.
66   * <p>
67   * TreeWalk instances are not thread-safe. Applications must either restrict
68   * usage of a TreeWalk instance to a single thread, or implement their own
69   * synchronization at a higher level.
70   * <p>
71   * Multiple simultaneous TreeWalk instances per
72   * {@link org.eclipse.jgit.lib.Repository} are permitted, even from concurrent
73   * threads.
74   */
75  public class TreeWalk implements AutoCloseable, AttributesProvider {
76  	private static final AbstractTreeIterator[] NO_TREES = {};
77  
78  	/**
79  	 * @since 4.2
80  	 */
81  	public enum OperationType {
82  		/**
83  		 * Represents a checkout operation (for example a checkout or reset
84  		 * operation).
85  		 */
86  		CHECKOUT_OP,
87  
88  		/**
89  		 * Represents a checkin operation (for example an add operation)
90  		 */
91  		CHECKIN_OP
92  	}
93  
94  	/**
95  	 *            Type of operation you want to retrieve the git attributes for.
96  	 */
97  	private OperationType operationType = OperationType.CHECKOUT_OP;
98  
99  	/**
100 	 * The filter command as defined in gitattributes. The keys are
101 	 * filterName+"."+filterCommandType. E.g. "lfs.clean"
102 	 */
103 	private Map<String, String> filterCommandsByNameDotType = new HashMap<>();
104 
105 	/**
106 	 * Set the operation type of this walk
107 	 *
108 	 * @param operationType
109 	 *            a {@link org.eclipse.jgit.treewalk.TreeWalk.OperationType}
110 	 *            object.
111 	 * @since 4.2
112 	 */
113 	public void setOperationType(OperationType operationType) {
114 		this.operationType = operationType;
115 	}
116 
117 	/**
118 	 * Open a tree walk and filter to exactly one path.
119 	 * <p>
120 	 * The returned tree walk is already positioned on the requested path, so
121 	 * the caller should not need to invoke {@link #next()} unless they are
122 	 * looking for a possible directory/file name conflict.
123 	 *
124 	 * @param reader
125 	 *            the reader the walker will obtain tree data from.
126 	 * @param path
127 	 *            single path to advance the tree walk instance into.
128 	 * @param trees
129 	 *            one or more trees to walk through, all with the same root.
130 	 * @return a new tree walk configured for exactly this one path; null if no
131 	 *         path was found in any of the trees.
132 	 * @throws java.io.IOException
133 	 *             reading a pack file or loose object failed.
134 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
135 	 *             an tree object could not be read as its data stream did not
136 	 *             appear to be a tree, or could not be inflated.
137 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
138 	 *             an object we expected to be a tree was not a tree.
139 	 * @throws org.eclipse.jgit.errors.MissingObjectException
140 	 *             a tree object was not found.
141 	 */
142 	public static TreeWalk forPath(final ObjectReader reader, final String path,
143 			final AnyObjectId... trees) throws MissingObjectException,
144 			IncorrectObjectTypeException, CorruptObjectException, IOException {
145 		return forPath(null, reader, path, trees);
146 	}
147 
148 	/**
149 	 * Open a tree walk and filter to exactly one path.
150 	 * <p>
151 	 * The returned tree walk is already positioned on the requested path, so
152 	 * the caller should not need to invoke {@link #next()} unless they are
153 	 * looking for a possible directory/file name conflict.
154 	 *
155 	 * @param repo
156 	 *            repository to read config data and
157 	 *            {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
158 	 *            from.
159 	 * @param reader
160 	 *            the reader the walker will obtain tree data from.
161 	 * @param path
162 	 *            single path to advance the tree walk instance into.
163 	 * @param trees
164 	 *            one or more trees to walk through, all with the same root.
165 	 * @return a new tree walk configured for exactly this one path; null if no
166 	 *         path was found in any of the trees.
167 	 * @throws java.io.IOException
168 	 *             reading a pack file or loose object failed.
169 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
170 	 *             an tree object could not be read as its data stream did not
171 	 *             appear to be a tree, or could not be inflated.
172 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
173 	 *             an object we expected to be a tree was not a tree.
174 	 * @throws org.eclipse.jgit.errors.MissingObjectException
175 	 *             a tree object was not found.
176 	 * @since 4.3
177 	 */
178 	public static TreeWalk forPath(final @Nullable Repository repo,
179 			final ObjectReader reader, final String path,
180 			final AnyObjectId... trees)
181 					throws MissingObjectException, IncorrectObjectTypeException,
182 					CorruptObjectException, IOException {
183 		TreeWalk tw = new TreeWalk(repo, reader);
184 		PathFilter f = PathFilter.create(path);
185 		tw.setFilter(f);
186 		tw.reset(trees);
187 		tw.setRecursive(false);
188 
189 		while (tw.next()) {
190 			if (f.isDone(tw)) {
191 				return tw;
192 			} else if (tw.isSubtree()) {
193 				tw.enterSubtree();
194 			}
195 		}
196 		return null;
197 	}
198 
199 	/**
200 	 * Open a tree walk and filter to exactly one path.
201 	 * <p>
202 	 * The returned tree walk is already positioned on the requested path, so
203 	 * the caller should not need to invoke {@link #next()} unless they are
204 	 * looking for a possible directory/file name conflict.
205 	 *
206 	 * @param db
207 	 *            repository to read tree object data from.
208 	 * @param path
209 	 *            single path to advance the tree walk instance into.
210 	 * @param trees
211 	 *            one or more trees to walk through, all with the same root.
212 	 * @return a new tree walk configured for exactly this one path; null if no
213 	 *         path was found in any of the trees.
214 	 * @throws java.io.IOException
215 	 *             reading a pack file or loose object failed.
216 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
217 	 *             an tree object could not be read as its data stream did not
218 	 *             appear to be a tree, or could not be inflated.
219 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
220 	 *             an object we expected to be a tree was not a tree.
221 	 * @throws org.eclipse.jgit.errors.MissingObjectException
222 	 *             a tree object was not found.
223 	 */
224 	public static TreeWalk forPath(final Repository db, final String path,
225 			final AnyObjectId... trees) throws MissingObjectException,
226 			IncorrectObjectTypeException, CorruptObjectException, IOException {
227 		try (ObjectReader reader = db.newObjectReader()) {
228 			return forPath(db, reader, path, trees);
229 		}
230 	}
231 
232 	/**
233 	 * Open a tree walk and filter to exactly one path.
234 	 * <p>
235 	 * The returned tree walk is already positioned on the requested path, so
236 	 * the caller should not need to invoke {@link #next()} unless they are
237 	 * looking for a possible directory/file name conflict.
238 	 *
239 	 * @param db
240 	 *            repository to read tree object data from.
241 	 * @param path
242 	 *            single path to advance the tree walk instance into.
243 	 * @param tree
244 	 *            the single tree to walk through.
245 	 * @return a new tree walk configured for exactly this one path; null if no
246 	 *         path was found in any of the trees.
247 	 * @throws java.io.IOException
248 	 *             reading a pack file or loose object failed.
249 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
250 	 *             an tree object could not be read as its data stream did not
251 	 *             appear to be a tree, or could not be inflated.
252 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
253 	 *             an object we expected to be a tree was not a tree.
254 	 * @throws org.eclipse.jgit.errors.MissingObjectException
255 	 *             a tree object was not found.
256 	 */
257 	public static TreeWalk forPath(final Repository db, final String path,
258 			final RevTree tree) throws MissingObjectException,
259 			IncorrectObjectTypeException, CorruptObjectException, IOException {
260 		return forPath(db, path, new ObjectId[] { tree });
261 	}
262 
263 	private final ObjectReader reader;
264 
265 	private final boolean closeReader;
266 
267 	private final MutableObjectIdml#MutableObjectId">MutableObjectId idBuffer = new MutableObjectId();
268 
269 	private TreeFilter filter;
270 
271 	AbstractTreeIterator[] trees;
272 
273 	private boolean recursive;
274 
275 	private boolean postOrderTraversal;
276 
277 	int depth;
278 
279 	private boolean advance;
280 
281 	private boolean postChildren;
282 
283 	private AttributesNodeProvider attributesNodeProvider;
284 
285 	AbstractTreeIterator currentHead;
286 
287 	/** Cached attribute for the current entry */
288 	private Attributes attrs = null;
289 
290 	/** Cached attributes handler */
291 	private AttributesHandler attributesHandler;
292 
293 	private Config config;
294 
295 	private Set<String> filterCommands;
296 
297 	/**
298 	 * Create a new tree walker for a given repository.
299 	 *
300 	 * @param repo
301 	 *            the repository the walker will obtain data from. An
302 	 *            ObjectReader will be created by the walker, and will be closed
303 	 *            when the walker is closed.
304 	 */
305 	public TreeWalk(Repository repo) {
306 		this(repo, repo.newObjectReader(), true);
307 	}
308 
309 	/**
310 	 * Create a new tree walker for a given repository.
311 	 *
312 	 * @param repo
313 	 *            the repository the walker will obtain data from. An
314 	 *            ObjectReader will be created by the walker, and will be closed
315 	 *            when the walker is closed.
316 	 * @param or
317 	 *            the reader the walker will obtain tree data from. The reader
318 	 *            is not closed when the walker is closed.
319 	 * @since 4.3
320 	 */
321 	public TreeWalk(@Nullable Repository repo, ObjectReader or) {
322 		this(repo, or, false);
323 	}
324 
325 	/**
326 	 * Create a new tree walker for a given repository.
327 	 *
328 	 * @param or
329 	 *            the reader the walker will obtain tree data from. The reader
330 	 *            is not closed when the walker is closed.
331 	 */
332 	public TreeWalk(ObjectReader or) {
333 		this(null, or, false);
334 	}
335 
336 	private TreeWalk(final @Nullable Repository repo, final ObjectReader or,
337 			final boolean closeReader) {
338 		if (repo != null) {
339 			config = repo.getConfig();
340 			attributesNodeProvider = repo.createAttributesNodeProvider();
341 			filterCommands = FilterCommandRegistry
342 					.getRegisteredFilterCommands();
343 		} else {
344 			config = null;
345 			attributesNodeProvider = null;
346 		}
347 		reader = or;
348 		filter = TreeFilter.ALL;
349 		trees = NO_TREES;
350 		this.closeReader = closeReader;
351 	}
352 
353 	/**
354 	 * Get the reader this walker is using to load objects.
355 	 *
356 	 * @return the reader this walker is using to load objects.
357 	 */
358 	public ObjectReader getObjectReader() {
359 		return reader;
360 	}
361 
362 	/**
363 	 * Get the operation type
364 	 *
365 	 * @return the {@link org.eclipse.jgit.treewalk.TreeWalk.OperationType}
366 	 * @since 4.3
367 	 */
368 	public OperationType getOperationType() {
369 		return operationType;
370 	}
371 
372 	/**
373 	 * {@inheritDoc}
374 	 * <p>
375 	 * Release any resources used by this walker's reader.
376 	 * <p>
377 	 * A walker that has been released can be used again, but may need to be
378 	 * released after the subsequent usage.
379 	 *
380 	 * @since 4.0
381 	 */
382 	@Override
383 	public void close() {
384 		if (closeReader) {
385 			reader.close();
386 		}
387 	}
388 
389 	/**
390 	 * Get the currently configured filter.
391 	 *
392 	 * @return the current filter. Never null as a filter is always needed.
393 	 */
394 	public TreeFilter getFilter() {
395 		return filter;
396 	}
397 
398 	/**
399 	 * Set the tree entry filter for this walker.
400 	 * <p>
401 	 * Multiple filters may be combined by constructing an arbitrary tree of
402 	 * <code>AndTreeFilter</code> or <code>OrTreeFilter</code> instances to
403 	 * describe the boolean expression required by the application. Custom
404 	 * filter implementations may also be constructed by applications.
405 	 * <p>
406 	 * Note that filters are not thread-safe and may not be shared by concurrent
407 	 * TreeWalk instances. Every TreeWalk must be supplied its own unique
408 	 * filter, unless the filter implementation specifically states it is (and
409 	 * always will be) thread-safe. Callers may use
410 	 * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#clone()} to create a
411 	 * unique filter tree for this TreeWalk instance.
412 	 *
413 	 * @param newFilter
414 	 *            the new filter. If null the special
415 	 *            {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} filter
416 	 *            will be used instead, as it matches every entry.
417 	 * @see org.eclipse.jgit.treewalk.filter.AndTreeFilter
418 	 * @see org.eclipse.jgit.treewalk.filter.OrTreeFilter
419 	 */
420 	public void setFilter(TreeFilter newFilter) {
421 		filter = newFilter != null ? newFilter : TreeFilter.ALL;
422 	}
423 
424 	/**
425 	 * Is this walker automatically entering into subtrees?
426 	 * <p>
427 	 * If the walker is recursive then the caller will not see a subtree node
428 	 * and instead will only receive file nodes in all relevant subtrees.
429 	 *
430 	 * @return true if automatically entering subtrees is enabled.
431 	 */
432 	public boolean isRecursive() {
433 		return recursive;
434 	}
435 
436 	/**
437 	 * Set the walker to enter (or not enter) subtrees automatically.
438 	 * <p>
439 	 * If recursive mode is enabled the walker will hide subtree nodes from the
440 	 * calling application and will produce only file level nodes. If a tree
441 	 * (directory) is deleted then all of the file level nodes will appear to be
442 	 * deleted, recursively, through as many levels as necessary to account for
443 	 * all entries.
444 	 *
445 	 * @param b
446 	 *            true to skip subtree nodes and only obtain files nodes.
447 	 */
448 	public void setRecursive(boolean b) {
449 		recursive = b;
450 	}
451 
452 	/**
453 	 * Does this walker return a tree entry after it exits the subtree?
454 	 * <p>
455 	 * If post order traversal is enabled then the walker will return a subtree
456 	 * after it has returned the last entry within that subtree. This may cause
457 	 * a subtree to be seen by the application twice if {@link #isRecursive()}
458 	 * is false, as the application will see it once, call
459 	 * {@link #enterSubtree()}, and then see it again as it leaves the subtree.
460 	 * <p>
461 	 * If an application does not enable {@link #isRecursive()} and it does not
462 	 * call {@link #enterSubtree()} then the tree is returned only once as none
463 	 * of the children were processed.
464 	 *
465 	 * @return true if subtrees are returned after entries within the subtree.
466 	 */
467 	public boolean isPostOrderTraversal() {
468 		return postOrderTraversal;
469 	}
470 
471 	/**
472 	 * Set the walker to return trees after their children.
473 	 *
474 	 * @param b
475 	 *            true to get trees after their children.
476 	 * @see #isPostOrderTraversal()
477 	 */
478 	public void setPostOrderTraversal(boolean b) {
479 		postOrderTraversal = b;
480 	}
481 
482 	/**
483 	 * Sets the {@link org.eclipse.jgit.attributes.AttributesNodeProvider} for
484 	 * this {@link org.eclipse.jgit.treewalk.TreeWalk}.
485 	 * <p>
486 	 * This is a requirement for a correct computation of the git attributes. If
487 	 * this {@link org.eclipse.jgit.treewalk.TreeWalk} has been built using
488 	 * {@link #TreeWalk(Repository)} constructor, the
489 	 * {@link org.eclipse.jgit.attributes.AttributesNodeProvider} has already
490 	 * been set. Indeed,the {@link org.eclipse.jgit.lib.Repository} can provide
491 	 * an {@link org.eclipse.jgit.attributes.AttributesNodeProvider} using
492 	 * {@link org.eclipse.jgit.lib.Repository#createAttributesNodeProvider()}
493 	 * method. Otherwise you should provide one.
494 	 * </p>
495 	 *
496 	 * @see Repository#createAttributesNodeProvider()
497 	 * @param provider
498 	 *            a {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
499 	 *            object.
500 	 * @since 4.2
501 	 */
502 	public void setAttributesNodeProvider(AttributesNodeProvider provider) {
503 		attributesNodeProvider = provider;
504 	}
505 
506 	/**
507 	 * Get the attributes node provider
508 	 *
509 	 * @return the {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
510 	 *         for this {@link org.eclipse.jgit.treewalk.TreeWalk}.
511 	 * @since 4.3
512 	 */
513 	public AttributesNodeProvider getAttributesNodeProvider() {
514 		return attributesNodeProvider;
515 	}
516 
517 	/**
518 	 * {@inheritDoc}
519 	 * <p>
520 	 * Retrieve the git attributes for the current entry.
521 	 *
522 	 * <h3>Git attribute computation</h3>
523 	 *
524 	 * <ul>
525 	 * <li>Get the attributes matching the current path entry from the info file
526 	 * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li>
527 	 * <li>Completes the list of attributes using the .gitattributes files
528 	 * located on the current path (the further the directory that contains
529 	 * .gitattributes is from the path in question, the lower its precedence).
530 	 * For a checkin operation, it will look first on the working tree (if any).
531 	 * If there is no attributes file, it will fallback on the index. For a
532 	 * checkout operation, it will first use the index entry and then fallback
533 	 * on the working tree if none.</li>
534 	 * <li>In the end, completes the list of matching attributes using the
535 	 * global attribute file define in the configuration (see
536 	 * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li>
537 	 *
538 	 * </ul>
539 	 *
540 	 *
541 	 * <h3>Iterator constraints</h3>
542 	 *
543 	 * <p>
544 	 * In order to have a correct list of attributes for the current entry, this
545 	 * {@link TreeWalk} requires to have at least one
546 	 * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An
547 	 * {@link AttributesNodeProvider} is used to retrieve the attributes from
548 	 * the info attributes file and the global attributes file. The
549 	 * {@link DirCacheIterator} is used to retrieve the .gitattributes files
550 	 * stored in the index. A {@link WorkingTreeIterator} can also be provided
551 	 * to access the local version of the .gitattributes files. If none is
552 	 * provided it will fallback on the {@link DirCacheIterator}.
553 	 * </p>
554 	 *
555 	 * @since 4.2
556 	 */
557 	@Override
558 	public Attributes getAttributes() {
559 		if (attrs != null)
560 			return attrs;
561 
562 		if (attributesNodeProvider == null) {
563 			// The work tree should have a AttributesNodeProvider to be able to
564 			// retrieve the info and global attributes node
565 			throw new IllegalStateException(
566 					"The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$
567 		}
568 
569 		try {
570 			// Lazy create the attributesHandler on the first access of
571 			// attributes. This requires the info, global and root
572 			// attributes nodes
573 			if (attributesHandler == null) {
574 				attributesHandler = new AttributesHandler(this);
575 			}
576 			attrs = attributesHandler.getAttributes();
577 			return attrs;
578 		} catch (IOException e) {
579 			throw new JGitInternalException("Error while parsing attributes", //$NON-NLS-1$
580 					e);
581 		}
582 	}
583 
584 	/**
585 	 * Get the EOL stream type of the current entry using the config and
586 	 * {@link #getAttributes()}.
587 	 *
588 	 * @param opType
589 	 *            the operationtype (checkin/checkout) which should be used
590 	 * @return the EOL stream type of the current entry using the config and
591 	 *         {@link #getAttributes()}. Note that this method may return null
592 	 *         if the {@link org.eclipse.jgit.treewalk.TreeWalk} is not based on
593 	 *         a working tree
594 	 * @since 4.10
595 	 */
596 	@Nullable
597 	public EolStreamType getEolStreamType(OperationType opType) {
598 		if (attributesNodeProvider == null || config == null)
599 			return null;
600 		return EolStreamTypeUtil.detectStreamType(
601 				opType != null ? opType : operationType,
602 					config.get(WorkingTreeOptions.KEY), getAttributes());
603 	}
604 
605 	/**
606 	 * Reset this walker so new tree iterators can be added to it.
607 	 */
608 	public void reset() {
609 		attrs = null;
610 		attributesHandler = null;
611 		trees = NO_TREES;
612 		advance = false;
613 		depth = 0;
614 	}
615 
616 	/**
617 	 * Reset this walker to run over a single existing tree.
618 	 *
619 	 * @param id
620 	 *            the tree we need to parse. The walker will execute over this
621 	 *            single tree if the reset is successful.
622 	 * @throws org.eclipse.jgit.errors.MissingObjectException
623 	 *             the given tree object does not exist in this repository.
624 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
625 	 *             the given object id does not denote a tree, but instead names
626 	 *             some other non-tree type of object. Note that commits are not
627 	 *             trees, even if they are sometimes called a "tree-ish".
628 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
629 	 *             the object claimed to be a tree, but its contents did not
630 	 *             appear to be a tree. The repository may have data corruption.
631 	 * @throws java.io.IOException
632 	 *             a loose object or pack file could not be read.
633 	 */
634 	public void reset(AnyObjectId id) throws MissingObjectException,
635 			IncorrectObjectTypeException, CorruptObjectException, IOException {
636 		if (trees.length == 1) {
637 			AbstractTreeIterator o = trees[0];
638 			while (o.parent != null)
639 				o = o.parent;
640 			if (o instanceof CanonicalTreeParser) {
641 				o.matches = null;
642 				o.matchShift = 0;
643 				((CanonicalTreeParser) o).reset(reader, id);
644 				trees[0] = o;
645 			} else {
646 				trees[0] = parserFor(id);
647 			}
648 		} else {
649 			trees = new AbstractTreeIterator[] { parserFor(id) };
650 		}
651 
652 		advance = false;
653 		depth = 0;
654 		attrs = null;
655 	}
656 
657 	/**
658 	 * Reset this walker to run over a set of existing trees.
659 	 *
660 	 * @param ids
661 	 *            the trees we need to parse. The walker will execute over this
662 	 *            many parallel trees if the reset is successful.
663 	 * @throws org.eclipse.jgit.errors.MissingObjectException
664 	 *             the given tree object does not exist in this repository.
665 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
666 	 *             the given object id does not denote a tree, but instead names
667 	 *             some other non-tree type of object. Note that commits are not
668 	 *             trees, even if they are sometimes called a "tree-ish".
669 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
670 	 *             the object claimed to be a tree, but its contents did not
671 	 *             appear to be a tree. The repository may have data corruption.
672 	 * @throws java.io.IOException
673 	 *             a loose object or pack file could not be read.
674 	 */
675 	public void reset(AnyObjectId... ids) throws MissingObjectException,
676 			IncorrectObjectTypeException, CorruptObjectException, IOException {
677 		final int oldLen = trees.length;
678 		final int newLen = ids.length;
679 		final AbstractTreeIterator[] r = newLen == oldLen ? trees
680 				: new AbstractTreeIterator[newLen];
681 		for (int i = 0; i < newLen; i++) {
682 			AbstractTreeIterator o;
683 
684 			if (i < oldLen) {
685 				o = trees[i];
686 				while (o.parent != null)
687 					o = o.parent;
688 				if (o instanceof CanonicalTreeParser && o.pathOffset == 0) {
689 					o.matches = null;
690 					o.matchShift = 0;
691 					((CanonicalTreeParser) o).reset(reader, ids[i]);
692 					r[i] = o;
693 					continue;
694 				}
695 			}
696 
697 			o = parserFor(ids[i]);
698 			r[i] = o;
699 		}
700 
701 		trees = r;
702 		advance = false;
703 		depth = 0;
704 		attrs = null;
705 	}
706 
707 	/**
708 	 * Add an already existing tree object for walking.
709 	 * <p>
710 	 * The position of this tree is returned to the caller, in case the caller
711 	 * has lost track of the order they added the trees into the walker.
712 	 * <p>
713 	 * The tree must have the same root as existing trees in the walk.
714 	 *
715 	 * @param id
716 	 *            identity of the tree object the caller wants walked.
717 	 * @return position of this tree within the walker.
718 	 * @throws org.eclipse.jgit.errors.MissingObjectException
719 	 *             the given tree object does not exist in this repository.
720 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
721 	 *             the given object id does not denote a tree, but instead names
722 	 *             some other non-tree type of object. Note that commits are not
723 	 *             trees, even if they are sometimes called a "tree-ish".
724 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
725 	 *             the object claimed to be a tree, but its contents did not
726 	 *             appear to be a tree. The repository may have data corruption.
727 	 * @throws java.io.IOException
728 	 *             a loose object or pack file could not be read.
729 	 */
730 	public int addTree(AnyObjectId id) throws MissingObjectException,
731 			IncorrectObjectTypeException, CorruptObjectException, IOException {
732 		return addTree(parserFor(id));
733 	}
734 
735 	/**
736 	 * Add an already created tree iterator for walking.
737 	 * <p>
738 	 * The position of this tree is returned to the caller, in case the caller
739 	 * has lost track of the order they added the trees into the walker.
740 	 * <p>
741 	 * The tree which the iterator operates on must have the same root as
742 	 * existing trees in the walk.
743 	 *
744 	 * @param p
745 	 *            an iterator to walk over. The iterator should be new, with no
746 	 *            parent, and should still be positioned before the first entry.
747 	 *            The tree which the iterator operates on must have the same
748 	 *            root as other trees in the walk.
749 	 * @return position of this tree within the walker.
750 	 */
751 	public int addTree(AbstractTreeIterator p) {
752 		int n = trees.length;
753 		AbstractTreeIterator[] newTrees = new AbstractTreeIterator[n + 1];
754 
755 		System.arraycopy(trees, 0, newTrees, 0, n);
756 		newTrees[n] = p;
757 		p.matches = null;
758 		p.matchShift = 0;
759 
760 		trees = newTrees;
761 		return n;
762 	}
763 
764 	/**
765 	 * Get the number of trees known to this walker.
766 	 *
767 	 * @return the total number of trees this walker is iterating over.
768 	 */
769 	public int getTreeCount() {
770 		return trees.length;
771 	}
772 
773 	/**
774 	 * Advance this walker to the next relevant entry.
775 	 *
776 	 * @return true if there is an entry available; false if all entries have
777 	 *         been walked and the walk of this set of tree iterators is over.
778 	 * @throws org.eclipse.jgit.errors.MissingObjectException
779 	 *             {@link #isRecursive()} was enabled, a subtree was found, but
780 	 *             the subtree object does not exist in this repository. The
781 	 *             repository may be missing objects.
782 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
783 	 *             {@link #isRecursive()} was enabled, a subtree was found, and
784 	 *             the subtree id does not denote a tree, but instead names some
785 	 *             other non-tree type of object. The repository may have data
786 	 *             corruption.
787 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
788 	 *             the contents of a tree did not appear to be a tree. The
789 	 *             repository may have data corruption.
790 	 * @throws java.io.IOException
791 	 *             a loose object or pack file could not be read.
792 	 */
793 	public boolean next() throws MissingObjectException,
794 			IncorrectObjectTypeException, CorruptObjectException, IOException {
795 		try {
796 			if (advance) {
797 				advance = false;
798 				postChildren = false;
799 				popEntriesEqual();
800 			}
801 
802 			for (;;) {
803 				attrs = null;
804 				final AbstractTreeIterator t = min();
805 				if (t.eof()) {
806 					if (depth > 0) {
807 						exitSubtree();
808 						if (postOrderTraversal) {
809 							advance = true;
810 							postChildren = true;
811 							return true;
812 						}
813 						popEntriesEqual();
814 						continue;
815 					}
816 					return false;
817 				}
818 
819 				currentHead = t;
820 				if (filter.matchFilter(this) == 1) {
821 					skipEntriesEqual();
822 					continue;
823 				}
824 
825 				if (recursive && FileMode.TREE.equals(t.mode)) {
826 					enterSubtree();
827 					continue;
828 				}
829 
830 				advance = true;
831 				return true;
832 			}
833 		} catch (StopWalkException stop) {
834 			stopWalk();
835 			return false;
836 		}
837 	}
838 
839 	/**
840 	 * Notify iterators the walk is aborting.
841 	 * <p>
842 	 * Primarily to notify {@link DirCacheBuildIterator} the walk is aborting so
843 	 * that it can copy any remaining entries.
844 	 *
845 	 * @throws IOException
846 	 *             if traversal of remaining entries throws an exception during
847 	 *             object access. This should never occur as remaining trees
848 	 *             should already be in memory, however the methods used to
849 	 *             finish traversal are declared to throw IOException.
850 	 */
851 	void stopWalk() throws IOException {
852 		for (AbstractTreeIterator t : trees) {
853 			t.stopWalk();
854 		}
855 	}
856 
857 	/**
858 	 * Obtain the tree iterator for the current entry.
859 	 * <p>
860 	 * Entering into (or exiting out of) a subtree causes the current tree
861 	 * iterator instance to be changed for the nth tree. This allows the tree
862 	 * iterators to manage only one list of items, with the diving handled by
863 	 * recursive trees.
864 	 *
865 	 * @param nth
866 	 *            tree to obtain the current iterator of.
867 	 * @param clazz
868 	 *            type of the tree iterator expected by the caller.
869 	 * @return r the current iterator of the requested type; null if the tree
870 	 *         has no entry to match the current path.
871 	 */
872 	@SuppressWarnings("unchecked")
873 	public <T extends AbstractTreeIterator> T getTree(final int nth,
874 			final Class<T> clazz) {
875 		final AbstractTreeIterator t = trees[nth];
876 		return t.matches == currentHead ? (T) t : null;
877 	}
878 
879 	/**
880 	 * Obtain the raw {@link org.eclipse.jgit.lib.FileMode} bits for the current
881 	 * entry.
882 	 * <p>
883 	 * Every added tree supplies mode bits, even if the tree does not contain
884 	 * the current entry. In the latter case
885 	 * {@link org.eclipse.jgit.lib.FileMode#MISSING}'s mode bits (0) are
886 	 * returned.
887 	 *
888 	 * @param nth
889 	 *            tree to obtain the mode bits from.
890 	 * @return mode bits for the current entry of the nth tree.
891 	 * @see FileMode#fromBits(int)
892 	 */
893 	public int getRawMode(int nth) {
894 		final AbstractTreeIterator t = trees[nth];
895 		return t.matches == currentHead ? t.mode : 0;
896 	}
897 
898 	/**
899 	 * Obtain the {@link org.eclipse.jgit.lib.FileMode} for the current entry.
900 	 * <p>
901 	 * Every added tree supplies a mode, even if the tree does not contain the
902 	 * current entry. In the latter case
903 	 * {@link org.eclipse.jgit.lib.FileMode#MISSING} is returned.
904 	 *
905 	 * @param nth
906 	 *            tree to obtain the mode from.
907 	 * @return mode for the current entry of the nth tree.
908 	 */
909 	public FileMode getFileMode(int nth) {
910 		return FileMode.fromBits(getRawMode(nth));
911 	}
912 
913 	/**
914 	 * Obtain the {@link org.eclipse.jgit.lib.FileMode} for the current entry on
915 	 * the currentHead tree
916 	 *
917 	 * @return mode for the current entry of the currentHead tree.
918 	 * @since 4.3
919 	 */
920 	public FileMode getFileMode() {
921 		return FileMode.fromBits(currentHead.mode);
922 	}
923 
924 	/**
925 	 * Obtain the ObjectId for the current entry.
926 	 * <p>
927 	 * Using this method to compare ObjectId values between trees of this walker
928 	 * is very inefficient. Applications should try to use
929 	 * {@link #idEqual(int, int)} or {@link #getObjectId(MutableObjectId, int)}
930 	 * whenever possible.
931 	 * <p>
932 	 * Every tree supplies an object id, even if the tree does not contain the
933 	 * current entry. In the latter case
934 	 * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} is returned.
935 	 *
936 	 * @param nth
937 	 *            tree to obtain the object identifier from.
938 	 * @return object identifier for the current tree entry.
939 	 * @see #getObjectId(MutableObjectId, int)
940 	 * @see #idEqual(int, int)
941 	 * @see #getObjectId(MutableObjectId, int)
942 	 * @see #idEqual(int, int)
943 	 */
944 	public ObjectId getObjectId(int nth) {
945 		final AbstractTreeIterator t = trees[nth];
946 		return t.matches == currentHead ? t.getEntryObjectId() : ObjectId
947 				.zeroId();
948 	}
949 
950 	/**
951 	 * Obtain the ObjectId for the current entry.
952 	 * <p>
953 	 * Every tree supplies an object id, even if the tree does not contain the
954 	 * current entry. In the latter case
955 	 * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} is supplied.
956 	 * <p>
957 	 * Applications should try to use {@link #idEqual(int, int)} when possible
958 	 * as it avoids conversion overheads.
959 	 *
960 	 * @param out
961 	 *            buffer to copy the object id into.
962 	 * @param nth
963 	 *            tree to obtain the object identifier from.
964 	 * @see #idEqual(int, int)
965 	 */
966 	public void getObjectId(MutableObjectId out, int nth) {
967 		final AbstractTreeIterator t = trees[nth];
968 		if (t.matches == currentHead)
969 			t.getEntryObjectId(out);
970 		else
971 			out.clear();
972 	}
973 
974 	/**
975 	 * Compare two tree's current ObjectId values for equality.
976 	 *
977 	 * @param nthA
978 	 *            first tree to compare the object id from.
979 	 * @param nthB
980 	 *            second tree to compare the object id from.
981 	 * @return result of
982 	 *         <code>getObjectId(nthA).equals(getObjectId(nthB))</code>.
983 	 * @see #getObjectId(int)
984 	 */
985 	public boolean idEqual(int nthA, int nthB) {
986 		final AbstractTreeIterator ch = currentHead;
987 		final AbstractTreeIterator a = trees[nthA];
988 		final AbstractTreeIterator b = trees[nthB];
989 		if (a.matches != ch && b.matches != ch) {
990 			// If neither tree matches the current path node then neither
991 			// tree has this entry. In such case the ObjectId is zero(),
992 			// and zero() is always equal to zero().
993 			//
994 			return true;
995 		}
996 		if (!a.hasId() || !b.hasId())
997 			return false;
998 		if (a.matches == ch && b.matches == ch)
999 			return a.idEqual(b);
1000 		return false;
1001 	}
1002 
1003 	/**
1004 	 * Get the current entry's name within its parent tree.
1005 	 * <p>
1006 	 * This method is not very efficient and is primarily meant for debugging
1007 	 * and final output generation. Applications should try to avoid calling it,
1008 	 * and if invoked do so only once per interesting entry, where the name is
1009 	 * absolutely required for correct function.
1010 	 *
1011 	 * @return name of the current entry within the parent tree (or directory).
1012 	 *         The name never includes a '/'.
1013 	 */
1014 	public String getNameString() {
1015 		final AbstractTreeIterator t = currentHead;
1016 		final int off = t.pathOffset;
1017 		final int end = t.pathLen;
1018 		return RawParseUtils.decode(UTF_8, t.path, off, end);
1019 	}
1020 
1021 	/**
1022 	 * Get the current entry's complete path.
1023 	 * <p>
1024 	 * This method is not very efficient and is primarily meant for debugging
1025 	 * and final output generation. Applications should try to avoid calling it,
1026 	 * and if invoked do so only once per interesting entry, where the name is
1027 	 * absolutely required for correct function.
1028 	 *
1029 	 * @return complete path of the current entry, from the root of the
1030 	 *         repository. If the current entry is in a subtree there will be at
1031 	 *         least one '/' in the returned string.
1032 	 */
1033 	public String getPathString() {
1034 		return pathOf(currentHead);
1035 	}
1036 
1037 	/**
1038 	 * Get the current entry's complete path as a UTF-8 byte array.
1039 	 *
1040 	 * @return complete path of the current entry, from the root of the
1041 	 *         repository. If the current entry is in a subtree there will be at
1042 	 *         least one '/' in the returned string.
1043 	 */
1044 	public byte[] getRawPath() {
1045 		final AbstractTreeIterator t = currentHead;
1046 		final int n = t.pathLen;
1047 		final byte[] r = new byte[n];
1048 		System.arraycopy(t.path, 0, r, 0, n);
1049 		return r;
1050 	}
1051 
1052 	/**
1053 	 * Get the path length of the current entry.
1054 	 *
1055 	 * @return The path length of the current entry.
1056 	 */
1057 	public int getPathLength() {
1058 		return currentHead.pathLen;
1059 	}
1060 
1061 	/**
1062 	 * Test if the supplied path matches the current entry's path.
1063 	 * <p>
1064 	 * This method detects if the supplied path is equal to, a subtree of, or
1065 	 * not similar at all to the current entry. It is faster to use this
1066 	 * method than to use {@link #getPathString()} to first create a String
1067 	 * object, then test <code>startsWith</code> or some other type of string
1068 	 * match function.
1069 	 * <p>
1070 	 * If the current entry is a subtree, then all paths within the subtree
1071 	 * are considered to match it.
1072 	 *
1073 	 * @param p
1074 	 *            path buffer to test. Callers should ensure the path does not
1075 	 *            end with '/' prior to invocation.
1076 	 * @param pLen
1077 	 *            number of bytes from <code>buf</code> to test.
1078 	 * @return -1 if the current path is a parent to p; 0 if p matches the current
1079 	 *         path; 1 if the current path is different and will never match
1080 	 *         again on this tree walk.
1081 	 * @since 4.7
1082 	 */
1083 	public int isPathMatch(byte[] p, int pLen) {
1084 		final AbstractTreeIterator t = currentHead;
1085 		final byte[] c = t.path;
1086 		final int cLen = t.pathLen;
1087 		int ci;
1088 
1089 		for (ci = 0; ci < cLen && ci < pLen; ci++) {
1090 			final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
1091 			if (c_value != 0) {
1092 				// Paths do not and will never match
1093 				return 1;
1094 			}
1095 		}
1096 
1097 		if (ci < cLen) {
1098 			// Ran out of pattern but we still had current data.
1099 			// If c[ci] == '/' then pattern matches the subtree.
1100 			// Otherwise it is a partial match == miss
1101 			return c[ci] == '/' ? 0 : 1;
1102 		}
1103 
1104 		if (ci < pLen) {
1105 			// Ran out of current, but we still have pattern data.
1106 			// If p[ci] == '/' then this subtree is a parent in the pattern,
1107 			// otherwise it's a miss.
1108 			return p[ci] == '/' && FileMode.TREE.equals(t.mode) ? -1 : 1;
1109 		}
1110 
1111 		// Both strings are identical.
1112 		return 0;
1113 	}
1114 
1115 	/**
1116 	 * Test if the supplied path matches the current entry's path.
1117 	 * <p>
1118 	 * This method tests that the supplied path is exactly equal to the current
1119 	 * entry or is one of its parent directories. It is faster to use this
1120 	 * method then to use {@link #getPathString()} to first create a String
1121 	 * object, then test <code>startsWith</code> or some other type of string
1122 	 * match function.
1123 	 * <p>
1124 	 * If the current entry is a subtree, then all paths within the subtree
1125 	 * are considered to match it.
1126 	 *
1127 	 * @param p
1128 	 *            path buffer to test. Callers should ensure the path does not
1129 	 *            end with '/' prior to invocation.
1130 	 * @param pLen
1131 	 *            number of bytes from <code>buf</code> to test.
1132 	 * @return &lt; 0 if p is before the current path; 0 if p matches the current
1133 	 *         path; 1 if the current path is past p and p will never match
1134 	 *         again on this tree walk.
1135 	 */
1136 	public int isPathPrefix(byte[] p, int pLen) {
1137 		final AbstractTreeIterator t = currentHead;
1138 		final byte[] c = t.path;
1139 		final int cLen = t.pathLen;
1140 		int ci;
1141 
1142 		for (ci = 0; ci < cLen && ci < pLen; ci++) {
1143 			final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
1144 			if (c_value != 0)
1145 				return c_value;
1146 		}
1147 
1148 		if (ci < cLen) {
1149 			// Ran out of pattern but we still had current data.
1150 			// If c[ci] == '/' then pattern matches the subtree.
1151 			// Otherwise we cannot be certain so we return -1.
1152 			//
1153 			return c[ci] == '/' ? 0 : -1;
1154 		}
1155 
1156 		if (ci < pLen) {
1157 			// Ran out of current, but we still have pattern data.
1158 			// If p[ci] == '/' then pattern matches this subtree,
1159 			// otherwise we cannot be certain so we return -1.
1160 			//
1161 			return p[ci] == '/' && FileMode.TREE.equals(t.mode) ? 0 : -1;
1162 		}
1163 
1164 		// Both strings are identical.
1165 		//
1166 		return 0;
1167 	}
1168 
1169 	/**
1170 	 * Test if the supplied path matches (being suffix of) the current entry's
1171 	 * path.
1172 	 * <p>
1173 	 * This method tests that the supplied path is exactly equal to the current
1174 	 * entry, or is relative to one of entry's parent directories. It is faster
1175 	 * to use this method then to use {@link #getPathString()} to first create
1176 	 * a String object, then test <code>endsWith</code> or some other type of
1177 	 * string match function.
1178 	 *
1179 	 * @param p
1180 	 *            path buffer to test.
1181 	 * @param pLen
1182 	 *            number of bytes from <code>buf</code> to test.
1183 	 * @return true if p is suffix of the current path;
1184 	 *         false if otherwise
1185 	 */
1186 	public boolean isPathSuffix(byte[] p, int pLen) {
1187 		final AbstractTreeIterator t = currentHead;
1188 		final byte[] c = t.path;
1189 		final int cLen = t.pathLen;
1190 
1191 		for (int i = 1; i <= pLen; i++) {
1192 			// Pattern longer than current path
1193 			if (i > cLen)
1194 				return false;
1195 			// Current path doesn't match pattern
1196 			if (c[cLen - i] != p[pLen - i])
1197 				return false;
1198 		}
1199 
1200 		// Whole pattern tested -> matches
1201 		return true;
1202 	}
1203 
1204 	/**
1205 	 * Get the current subtree depth of this walker.
1206 	 *
1207 	 * @return the current subtree depth of this walker.
1208 	 */
1209 	public int getDepth() {
1210 		return depth;
1211 	}
1212 
1213 	/**
1214 	 * Is the current entry a subtree?
1215 	 * <p>
1216 	 * This method is faster then testing the raw mode bits of all trees to see
1217 	 * if any of them are a subtree. If at least one is a subtree then this
1218 	 * method will return true.
1219 	 *
1220 	 * @return true if {@link #enterSubtree()} will work on the current node.
1221 	 */
1222 	public boolean isSubtree() {
1223 		return FileMode.TREE.equals(currentHead.mode);
1224 	}
1225 
1226 	/**
1227 	 * Is the current entry a subtree returned after its children?
1228 	 *
1229 	 * @return true if the current node is a tree that has been returned after
1230 	 *         its children were already processed.
1231 	 * @see #isPostOrderTraversal()
1232 	 */
1233 	public boolean isPostChildren() {
1234 		return postChildren && isSubtree();
1235 	}
1236 
1237 	/**
1238 	 * Enter into the current subtree.
1239 	 * <p>
1240 	 * If the current entry is a subtree this method arranges for its children
1241 	 * to be returned before the next sibling following the subtree is returned.
1242 	 *
1243 	 * @throws org.eclipse.jgit.errors.MissingObjectException
1244 	 *             a subtree was found, but the subtree object does not exist in
1245 	 *             this repository. The repository may be missing objects.
1246 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
1247 	 *             a subtree was found, and the subtree id does not denote a
1248 	 *             tree, but instead names some other non-tree type of object.
1249 	 *             The repository may have data corruption.
1250 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
1251 	 *             the contents of a tree did not appear to be a tree. The
1252 	 *             repository may have data corruption.
1253 	 * @throws java.io.IOException
1254 	 *             a loose object or pack file could not be read.
1255 	 */
1256 	public void enterSubtree() throws MissingObjectException,
1257 			IncorrectObjectTypeException, CorruptObjectException, IOException {
1258 		attrs = null;
1259 		final AbstractTreeIterator ch = currentHead;
1260 		final AbstractTreeIteratorrator.html#AbstractTreeIterator">AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
1261 		for (int i = 0; i < trees.length; i++) {
1262 			final AbstractTreeIterator t = trees[i];
1263 			final AbstractTreeIterator n;
1264 			// If we find a GITLINK when attempting to enter a subtree, then the
1265 			// GITLINK must exist as a TREE in the index, meaning the working tree
1266 			// entry should be treated as a TREE
1267 			if (t.matches == ch && !t.eof() &&
1268 					(FileMode.TREE.equals(t.mode)
1269 							|| (FileMode.GITLINK.equals(t.mode) && t.isWorkTree())))
1270 				n = t.createSubtreeIterator(reader, idBuffer);
1271 			else
1272 				n = t.createEmptyTreeIterator();
1273 			tmp[i] = n;
1274 		}
1275 		depth++;
1276 		advance = false;
1277 		System.arraycopy(tmp, 0, trees, 0, trees.length);
1278 	}
1279 
1280 	@SuppressWarnings("unused")
1281 	AbstractTreeIterator min() throws CorruptObjectException {
1282 		int i = 0;
1283 		AbstractTreeIterator minRef = trees[i];
1284 		while (minRef.eof() && ++i < trees.length)
1285 			minRef = trees[i];
1286 		if (minRef.eof())
1287 			return minRef;
1288 
1289 		minRef.matches = minRef;
1290 		while (++i < trees.length) {
1291 			final AbstractTreeIterator t = trees[i];
1292 			if (t.eof())
1293 				continue;
1294 			final int cmp = t.pathCompare(minRef);
1295 			if (cmp < 0) {
1296 				t.matches = t;
1297 				minRef = t;
1298 			} else if (cmp == 0) {
1299 				t.matches = minRef;
1300 			}
1301 		}
1302 
1303 		return minRef;
1304 	}
1305 
1306 	void popEntriesEqual() throws CorruptObjectException {
1307 		final AbstractTreeIterator ch = currentHead;
1308 		for (AbstractTreeIterator t : trees) {
1309 			if (t.matches == ch) {
1310 				t.next(1);
1311 				t.matches = null;
1312 			}
1313 		}
1314 	}
1315 
1316 	void skipEntriesEqual() throws CorruptObjectException {
1317 		final AbstractTreeIterator ch = currentHead;
1318 		for (AbstractTreeIterator t : trees) {
1319 			if (t.matches == ch) {
1320 				t.skip();
1321 				t.matches = null;
1322 			}
1323 		}
1324 	}
1325 
1326 	void exitSubtree() {
1327 		depth--;
1328 		for (int i = 0; i < trees.length; i++)
1329 			trees[i] = trees[i].parent;
1330 
1331 		AbstractTreeIterator minRef = null;
1332 		for (AbstractTreeIterator t : trees) {
1333 			if (t.matches != t)
1334 				continue;
1335 			if (minRef == null || t.pathCompare(minRef) < 0)
1336 				minRef = t;
1337 		}
1338 		currentHead = minRef;
1339 	}
1340 
1341 	private CanonicalTreeParser parserFor(AnyObjectId id)
1342 			throws IncorrectObjectTypeException, IOException {
1343 		final CanonicalTreeParserreeParser.html#CanonicalTreeParser">CanonicalTreeParser p = new CanonicalTreeParser();
1344 		p.reset(reader, id);
1345 		return p;
1346 	}
1347 
1348 	static String pathOf(AbstractTreeIterator t) {
1349 		return RawParseUtils.decode(UTF_8, t.path, 0, t.pathLen);
1350 	}
1351 
1352 	static String pathOf(byte[] buf, int pos, int end) {
1353 		return RawParseUtils.decode(UTF_8, buf, pos, end);
1354 	}
1355 
1356 	/**
1357 	 * Get the tree of that type.
1358 	 *
1359 	 * @param type
1360 	 *            of the tree to be queried
1361 	 * @return the tree of that type or null if none is present.
1362 	 * @since 4.3
1363 	 * @param <T>
1364 	 *            a tree type.
1365 	 */
1366 	public <T extends AbstractTreeIterator> T getTree(Class<T> type) {
1367 		for (AbstractTreeIterator tree : trees) {
1368 			if (type.isInstance(tree)) {
1369 				return type.cast(tree);
1370 			}
1371 		}
1372 		return null;
1373 	}
1374 
1375 	/**
1376 	 * Inspect config and attributes to return a filtercommand applicable for
1377 	 * the current path, but without expanding %f occurences
1378 	 *
1379 	 * @param filterCommandType
1380 	 *            which type of filterCommand should be executed. E.g. "clean",
1381 	 *            "smudge"
1382 	 * @return a filter command
1383 	 * @throws java.io.IOException
1384 	 * @since 4.2
1385 	 */
1386 	public String getFilterCommand(String filterCommandType)
1387 			throws IOException {
1388 		Attributes attributes = getAttributes();
1389 
1390 		Attribute f = attributes.get(Constants.ATTR_FILTER);
1391 		if (f == null) {
1392 			return null;
1393 		}
1394 		String filterValue = f.getValue();
1395 		if (filterValue == null) {
1396 			return null;
1397 		}
1398 
1399 		String filterCommand = getFilterCommandDefinition(filterValue,
1400 				filterCommandType);
1401 		if (filterCommand == null) {
1402 			return null;
1403 		}
1404 		return filterCommand.replaceAll("%f", //$NON-NLS-1$
1405 				Matcher.quoteReplacement(
1406 						QuotedString.BOURNE.quote((getPathString()))));
1407 	}
1408 
1409 	/**
1410 	 * Get the filter command how it is defined in gitconfig. The returned
1411 	 * string may contain "%f" which needs to be replaced by the current path
1412 	 * before executing the filter command. These filter definitions are cached
1413 	 * for better performance.
1414 	 *
1415 	 * @param filterDriverName
1416 	 *            The name of the filter driver as it is referenced in the
1417 	 *            gitattributes file. E.g. "lfs". For each filter driver there
1418 	 *            may be many commands defined in the .gitconfig
1419 	 * @param filterCommandType
1420 	 *            The type of the filter command for a specific filter driver.
1421 	 *            May be "clean" or "smudge".
1422 	 * @return the definition of the command to be executed for this filter
1423 	 *         driver and filter command
1424 	 */
1425 	private String getFilterCommandDefinition(String filterDriverName,
1426 			String filterCommandType) {
1427 		String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$
1428 		String filterCommand = filterCommandsByNameDotType.get(key);
1429 		if (filterCommand != null)
1430 			return filterCommand;
1431 		filterCommand = config.getString(ConfigConstants.CONFIG_FILTER_SECTION,
1432 				filterDriverName, filterCommandType);
1433 		boolean useBuiltin = config.getBoolean(
1434 				ConfigConstants.CONFIG_FILTER_SECTION,
1435 				filterDriverName, ConfigConstants.CONFIG_KEY_USEJGITBUILTIN, false);
1436 		if (useBuiltin) {
1437 			String builtinFilterCommand = Constants.BUILTIN_FILTER_PREFIX
1438 					+ filterDriverName + '/' + filterCommandType;
1439 			if (filterCommands != null
1440 					&& filterCommands.contains(builtinFilterCommand)) {
1441 				filterCommand = builtinFilterCommand;
1442 			}
1443 		}
1444 		if (filterCommand != null) {
1445 			filterCommandsByNameDotType.put(key, filterCommand);
1446 		}
1447 		return filterCommand;
1448 	}
1449 }