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