View Javadoc
1   /*
2    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.revwalk;
45  
46  import static java.util.Objects.requireNonNull;
47  import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
48  import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
49  import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
50  
51  import java.io.IOException;
52  import java.text.MessageFormat;
53  import java.util.ArrayList;
54  import java.util.List;
55  
56  import org.eclipse.jgit.errors.CorruptObjectException;
57  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
58  import org.eclipse.jgit.errors.LargeObjectException;
59  import org.eclipse.jgit.errors.MissingObjectException;
60  import org.eclipse.jgit.internal.JGitText;
61  import org.eclipse.jgit.lib.AnyObjectId;
62  import org.eclipse.jgit.lib.ObjectReader;
63  import org.eclipse.jgit.lib.Repository;
64  import org.eclipse.jgit.revwalk.filter.ObjectFilter;
65  import org.eclipse.jgit.util.RawParseUtils;
66  
67  /**
68   * Specialized subclass of RevWalk to include trees, blobs and tags.
69   * <p>
70   * Unlike RevWalk this subclass is able to remember starting roots that include
71   * annotated tags, or arbitrary trees or blobs. Once commit generation is
72   * complete and all commits have been popped by the application, individual
73   * annotated tag, tree and blob objects can be popped through the additional
74   * method {@link #nextObject()}.
75   * <p>
76   * Tree and blob objects reachable from interesting commits are automatically
77   * scheduled for inclusion in the results of {@link #nextObject()}, returning
78   * each object exactly once. Objects are sorted and returned according to the
79   * the commits that reference them and the order they appear within a tree.
80   * Ordering can be affected by changing the
81   * {@link org.eclipse.jgit.revwalk.RevSort} used to order the commits that are
82   * returned first.
83   */
84  public class ObjectWalk extends RevWalk {
85  	private static final int ID_SZ = 20;
86  	private static final int TYPE_SHIFT = 12;
87  	private static final int TYPE_TREE = 0040000 >>> TYPE_SHIFT;
88  	private static final int TYPE_SYMLINK = 0120000 >>> TYPE_SHIFT;
89  	private static final int TYPE_FILE = 0100000 >>> TYPE_SHIFT;
90  	private static final int TYPE_GITLINK = 0160000 >>> TYPE_SHIFT;
91  
92  	/**
93  	 * Indicates a non-RevCommit is in {@link #pendingObjects}.
94  	 * <p>
95  	 * We can safely reuse {@link RevWalk#REWRITE} here for the same value as it
96  	 * is only set on RevCommit and {@link #pendingObjects} never has RevCommit
97  	 * instances inserted into it.
98  	 */
99  	private static final int IN_PENDING = RevWalk.REWRITE;
100 
101 	/**
102 	 * When walking over a tree and blob graph, objects are usually marked as
103 	 * seen as they are visited and this "seen" status is checked upon the next
104 	 * visit. If they are already "seen" then they are not processed (returned
105 	 * by {@link ObjectWalk#nextObject()}) again. However, this behavior can be
106 	 * overridden by supplying a different implementation of this class.
107 	 *
108 	 * @since 5.4
109 	 */
110 	public interface VisitationPolicy {
111 		/**
112 		 * Whenever the rev or object walk reaches a Git object, if that object
113 		 * already exists as a RevObject, this method is called to determine if
114 		 * that object should be visited.
115 		 *
116 		 * @param o
117 		 *            the object to check if it should be visited
118 		 * @return true if the object should be visited
119 		 */
120 		boolean shouldVisit(RevObject o);
121 
122 		/**
123 		 * Called when an object is visited.
124 		 *
125 		 * @param o
126 		 *            the object that was visited
127 		 */
128 		void visited(RevObject o);
129 	}
130 
131 	/**
132 	 * The default visitation policy: causes all objects to be visited exactly
133 	 * once.
134 	 *
135 	 * @since 5.4
136 	 */
137 	public static final VisitationPolicy SIMPLE_VISITATION_POLICY =
138 			new VisitationPolicy() {
139 		@Override
140 		public boolean shouldVisit(RevObject o) {
141 			return (o.flags & SEEN) == 0;
142 		}
143 
144 		@Override
145 		public void visited(RevObject o) {
146 			o.flags |= SEEN;
147 		}
148 	};
149 
150 	private List<RevObject> rootObjects;
151 
152 	private BlockObjQueue pendingObjects;
153 
154 	private ObjectFilter objectFilter;
155 
156 	private TreeVisit freeVisit;
157 
158 	private TreeVisit currVisit;
159 
160 	private byte[] pathBuf;
161 
162 	private int pathLen;
163 
164 	private boolean boundary;
165 
166 	private VisitationPolicy visitationPolicy = SIMPLE_VISITATION_POLICY;
167 
168 	/**
169 	 * Create a new revision and object walker for a given repository.
170 	 *
171 	 * @param repo
172 	 *            the repository the walker will obtain data from.
173 	 */
174 	public ObjectWalk(Repository repo) {
175 		this(repo.newObjectReader());
176 	}
177 
178 	/**
179 	 * Create a new revision and object walker for a given repository.
180 	 *
181 	 * @param or
182 	 *            the reader the walker will obtain data from. The reader should
183 	 *            be released by the caller when the walker is no longer
184 	 *            required.
185 	 */
186 	public ObjectWalk(ObjectReader or) {
187 		super(or);
188 		setRetainBody(false);
189 		rootObjects = new ArrayList<>();
190 		pendingObjects = new BlockObjQueue();
191 		objectFilter = ObjectFilter.ALL;
192 		pathBuf = new byte[256];
193 	}
194 
195 	/**
196 	 * Mark an object or commit to start graph traversal from.
197 	 * <p>
198 	 * Callers are encouraged to use
199 	 * {@link org.eclipse.jgit.revwalk.RevWalk#parseAny(AnyObjectId)} instead of
200 	 * {@link org.eclipse.jgit.revwalk.RevWalk#lookupAny(AnyObjectId, int)}, as
201 	 * this method requires the object to be parsed before it can be added as a
202 	 * root for the traversal.
203 	 * <p>
204 	 * The method will automatically parse an unparsed object, but error
205 	 * handling may be more difficult for the application to explain why a
206 	 * RevObject is not actually valid. The object pool of this walker would
207 	 * also be 'poisoned' by the invalid RevObject.
208 	 * <p>
209 	 * This method will automatically call
210 	 * {@link org.eclipse.jgit.revwalk.RevWalk#markStart(RevCommit)} if passed
211 	 * RevCommit instance, or a RevTag that directly (or indirectly) references
212 	 * a RevCommit.
213 	 *
214 	 * @param o
215 	 *            the object to start traversing from. The object passed must be
216 	 *            from this same revision walker.
217 	 * @throws org.eclipse.jgit.errors.MissingObjectException
218 	 *             the object supplied is not available from the object
219 	 *             database. This usually indicates the supplied object is
220 	 *             invalid, but the reference was constructed during an earlier
221 	 *             invocation to
222 	 *             {@link org.eclipse.jgit.revwalk.RevWalk#lookupAny(AnyObjectId, int)}.
223 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
224 	 *             the object was not parsed yet and it was discovered during
225 	 *             parsing that it is not actually the type of the instance
226 	 *             passed in. This usually indicates the caller used the wrong
227 	 *             type in a
228 	 *             {@link org.eclipse.jgit.revwalk.RevWalk#lookupAny(AnyObjectId, int)}
229 	 *             call.
230 	 * @throws java.io.IOException
231 	 *             a pack file or loose object could not be read.
232 	 */
233 	public void markStart(RevObject o) throws MissingObjectException,
234 			IncorrectObjectTypeException, IOException {
235 		while (o instanceof RevTag) {
236 			addObject(o);
237 			o = ((RevTag) o).getObject();
238 			parseHeaders(o);
239 		}
240 
241 		if (o instanceof RevCommit)
242 			super.markStart((RevCommit) o);
243 		else
244 			addObject(o);
245 	}
246 
247 	/**
248 	 * Mark an object to not produce in the output.
249 	 * <p>
250 	 * Uninteresting objects denote not just themselves but also their entire
251 	 * reachable chain, back until the merge base of an uninteresting commit and
252 	 * an otherwise interesting commit.
253 	 * <p>
254 	 * Callers are encouraged to use
255 	 * {@link org.eclipse.jgit.revwalk.RevWalk#parseAny(AnyObjectId)} instead of
256 	 * {@link org.eclipse.jgit.revwalk.RevWalk#lookupAny(AnyObjectId, int)}, as
257 	 * this method requires the object to be parsed before it can be added as a
258 	 * root for the traversal.
259 	 * <p>
260 	 * The method will automatically parse an unparsed object, but error
261 	 * handling may be more difficult for the application to explain why a
262 	 * RevObject is not actually valid. The object pool of this walker would
263 	 * also be 'poisoned' by the invalid RevObject.
264 	 * <p>
265 	 * This method will automatically call
266 	 * {@link org.eclipse.jgit.revwalk.RevWalk#markStart(RevCommit)} if passed
267 	 * RevCommit instance, or a RevTag that directly (or indirectly) references
268 	 * a RevCommit.
269 	 *
270 	 * @param o
271 	 *            the object to start traversing from. The object passed must be
272 	 * @throws org.eclipse.jgit.errors.MissingObjectException
273 	 *             the object supplied is not available from the object
274 	 *             database. This usually indicates the supplied object is
275 	 *             invalid, but the reference was constructed during an earlier
276 	 *             invocation to
277 	 *             {@link org.eclipse.jgit.revwalk.RevWalk#lookupAny(AnyObjectId, int)}.
278 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
279 	 *             the object was not parsed yet and it was discovered during
280 	 *             parsing that it is not actually the type of the instance
281 	 *             passed in. This usually indicates the caller used the wrong
282 	 *             type in a
283 	 *             {@link org.eclipse.jgit.revwalk.RevWalk#lookupAny(AnyObjectId, int)}
284 	 *             call.
285 	 * @throws java.io.IOException
286 	 *             a pack file or loose object could not be read.
287 	 */
288 	public void markUninteresting(RevObject o) throws MissingObjectException,
289 			IncorrectObjectTypeException, IOException {
290 		while (o instanceof RevTag) {
291 			o.flags |= UNINTERESTING;
292 			if (boundary)
293 				addObject(o);
294 			o = ((RevTag) o).getObject();
295 			parseHeaders(o);
296 		}
297 
298 		if (o instanceof RevCommit)
299 			super.markUninteresting((RevCommit) o);
300 		else if (o instanceof RevTree)
301 			markTreeUninteresting((RevTree) o);
302 		else
303 			o.flags |= UNINTERESTING;
304 
305 		if (o.getType() != OBJ_COMMIT && boundary)
306 			addObject(o);
307 	}
308 
309 	/** {@inheritDoc} */
310 	@Override
311 	public void sort(RevSort s) {
312 		super.sort(s);
313 		boundary = hasRevSort(RevSort.BOUNDARY);
314 	}
315 
316 	/** {@inheritDoc} */
317 	@Override
318 	public void sort(RevSort s, boolean use) {
319 		super.sort(s, use);
320 		boundary = hasRevSort(RevSort.BOUNDARY);
321 	}
322 
323 	/**
324 	 * Get the currently configured object filter.
325 	 *
326 	 * @return the current filter. Never null as a filter is always needed.
327 	 * @since 4.0
328 	 */
329 	public ObjectFilter getObjectFilter() {
330 		return objectFilter;
331 	}
332 
333 	/**
334 	 * Set the object filter for this walker. This filter affects the objects
335 	 * visited by {@link #nextObject()}. It does not affect the commits listed
336 	 * by {@link #next()}.
337 	 * <p>
338 	 * If the filter returns false for an object, then that object is skipped
339 	 * and objects reachable from it are not enqueued to be walked recursively.
340 	 * This can be used to speed up the object walk by skipping subtrees that
341 	 * are known to be uninteresting.
342 	 *
343 	 * @param newFilter
344 	 *            the new filter. If null the special
345 	 *            {@link org.eclipse.jgit.revwalk.filter.ObjectFilter#ALL}
346 	 *            filter will be used instead, as it matches every object.
347 	 * @since 4.0
348 	 */
349 	public void setObjectFilter(ObjectFilter newFilter) {
350 		assertNotStarted();
351 		objectFilter = newFilter != null ? newFilter : ObjectFilter.ALL;
352 	}
353 
354 	/**
355 	 * Sets the visitation policy to use during this walk.
356 	 *
357 	 * @param policy
358 	 *            the {@code VisitationPolicy} to use
359 	 * @since 5.4
360 	 */
361 	public void setVisitationPolicy(VisitationPolicy policy) {
362 		assertNotStarted();
363 		visitationPolicy = requireNonNull(policy);
364 	}
365 
366 	/** {@inheritDoc} */
367 	@Override
368 	public RevCommit next() throws MissingObjectException,
369 			IncorrectObjectTypeException, IOException {
370 		for (;;) {
371 			final RevCommit r = super.next();
372 			if (r == null) {
373 				return null;
374 			}
375 			final RevTree t = r.getTree();
376 			if ((r.flags & UNINTERESTING) != 0) {
377 				if (objectFilter.include(this, t)) {
378 					markTreeUninteresting(t);
379 				}
380 				if (boundary) {
381 					return r;
382 				}
383 				continue;
384 			}
385 			if (objectFilter.include(this, t)) {
386 				pendingObjects.add(t);
387 			}
388 			return r;
389 		}
390 	}
391 
392 	/**
393 	 * Skips the current tree such that {@link #nextObject()} does not return
394 	 * any objects inside it. This should be called right after
395 	 * {@link #nextObject()} returns the tree.
396 	 *
397 	 * @since 5.4
398 	 */
399 	public void skipTree() {
400 		currVisit.ptr = currVisit.buf.length;
401 	}
402 
403 	/**
404 	 * Pop the next most recent object.
405 	 *
406 	 * @return next most recent object; null if traversal is over.
407 	 * @throws org.eclipse.jgit.errors.MissingObjectException
408 	 *             one or more of the next objects are not available from the
409 	 *             object database, but were thought to be candidates for
410 	 *             traversal. This usually indicates a broken link.
411 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
412 	 *             one or more of the objects in a tree do not match the type
413 	 *             indicated.
414 	 * @throws java.io.IOException
415 	 *             a pack file or loose object could not be read.
416 	 */
417 	public RevObject nextObject() throws MissingObjectException,
418 			IncorrectObjectTypeException, IOException {
419 		pathLen = 0;
420 
421 		TreeVisit tv = currVisit;
422 		while (tv != null) {
423 			byte[] buf = tv.buf;
424 			for (int ptr = tv.ptr; ptr < buf.length;) {
425 				int startPtr = ptr;
426 				ptr = findObjectId(buf, ptr);
427 				idBuffer.fromRaw(buf, ptr);
428 				ptr += ID_SZ;
429 
430 				if (!objectFilter.include(this, idBuffer)) {
431 					continue;
432 				}
433 
434 				RevObject obj = objects.get(idBuffer);
435 				if (obj != null && !visitationPolicy.shouldVisit(obj))
436 					continue;
437 
438 				int mode = parseMode(buf, startPtr, ptr, tv);
439 				switch (mode >>> TYPE_SHIFT) {
440 				case TYPE_FILE:
441 				case TYPE_SYMLINK:
442 					if (obj == null) {
443 						obj = new RevBlob(idBuffer);
444 						visitationPolicy.visited(obj);
445 						objects.add(obj);
446 						return obj;
447 					}
448 					if (!(obj instanceof RevBlob))
449 						throw new IncorrectObjectTypeException(obj, OBJ_BLOB);
450 					visitationPolicy.visited(obj);
451 					if ((obj.flags & UNINTERESTING) == 0)
452 						return obj;
453 					if (boundary)
454 						return obj;
455 					continue;
456 
457 				case TYPE_TREE:
458 					if (obj == null) {
459 						obj = new RevTree(idBuffer);
460 						visitationPolicy.visited(obj);
461 						objects.add(obj);
462 						return pushTree(obj);
463 					}
464 					if (!(obj instanceof RevTree))
465 						throw new IncorrectObjectTypeException(obj, OBJ_TREE);
466 					visitationPolicy.visited(obj);
467 					if ((obj.flags & UNINTERESTING) == 0)
468 						return pushTree(obj);
469 					if (boundary)
470 						return pushTree(obj);
471 					continue;
472 
473 				case TYPE_GITLINK:
474 					continue;
475 
476 				default:
477 					throw new CorruptObjectException(MessageFormat.format(
478 							JGitText.get().corruptObjectInvalidMode3,
479 							String.format("%o", Integer.valueOf(mode)), //$NON-NLS-1$
480 							idBuffer.name(),
481 							RawParseUtils.decode(buf, tv.namePtr, tv.nameEnd),
482 							tv.obj));
483 				}
484 			}
485 
486 			currVisit = tv.parent;
487 			releaseTreeVisit(tv);
488 			tv = currVisit;
489 		}
490 
491 		for (;;) {
492 			RevObject o = pendingObjects.next();
493 			if (o == null) {
494 				return null;
495 			}
496 			if (!visitationPolicy.shouldVisit(o)) {
497 				continue;
498 			}
499 			visitationPolicy.visited(o);
500 			if ((o.flags & UNINTERESTING) == 0 || boundary) {
501 				if (o instanceof RevTree) {
502 					// The previous while loop should have exhausted the stack
503 					// of trees.
504 					assert currVisit == null;
505 
506 					pushTree(o);
507 				}
508 				return o;
509 			}
510 		}
511 	}
512 
513 	private static int findObjectId(byte[] buf, int ptr) {
514 		// Skip over the mode and name until the NUL before the ObjectId
515 		// can be located. Skip the NUL as the function returns.
516 		for (;;) {
517 			if (buf[++ptr] == 0) return ++ptr;
518 			if (buf[++ptr] == 0) return ++ptr;
519 			if (buf[++ptr] == 0) return ++ptr;
520 			if (buf[++ptr] == 0) return ++ptr;
521 
522 			if (buf[++ptr] == 0) return ++ptr;
523 			if (buf[++ptr] == 0) return ++ptr;
524 			if (buf[++ptr] == 0) return ++ptr;
525 			if (buf[++ptr] == 0) return ++ptr;
526 
527 			if (buf[++ptr] == 0) return ++ptr;
528 			if (buf[++ptr] == 0) return ++ptr;
529 			if (buf[++ptr] == 0) return ++ptr;
530 			if (buf[++ptr] == 0) return ++ptr;
531 
532 			if (buf[++ptr] == 0) return ++ptr;
533 			if (buf[++ptr] == 0) return ++ptr;
534 			if (buf[++ptr] == 0) return ++ptr;
535 			if (buf[++ptr] == 0) return ++ptr;
536 		}
537 	}
538 
539 	private static int parseMode(byte[] buf, int startPtr, int recEndPtr, TreeVisit tv) {
540 		int mode = buf[startPtr] - '0';
541 		for (;;) {
542 			byte c = buf[++startPtr];
543 			if (' ' == c)
544 				break;
545 			mode <<= 3;
546 			mode += c - '0';
547 
548 			c = buf[++startPtr];
549 			if (' ' == c)
550 				break;
551 			mode <<= 3;
552 			mode += c - '0';
553 
554 			c = buf[++startPtr];
555 			if (' ' == c)
556 				break;
557 			mode <<= 3;
558 			mode += c - '0';
559 
560 			c = buf[++startPtr];
561 			if (' ' == c)
562 				break;
563 			mode <<= 3;
564 			mode += c - '0';
565 
566 			c = buf[++startPtr];
567 			if (' ' == c)
568 				break;
569 			mode <<= 3;
570 			mode += c - '0';
571 
572 			c = buf[++startPtr];
573 			if (' ' == c)
574 				break;
575 			mode <<= 3;
576 			mode += c - '0';
577 
578 			c = buf[++startPtr];
579 			if (' ' == c)
580 				break;
581 			mode <<= 3;
582 			mode += c - '0';
583 		}
584 
585 		tv.ptr = recEndPtr;
586 		tv.namePtr = startPtr + 1;
587 		tv.nameEnd = recEndPtr - (ID_SZ + 1);
588 		return mode;
589 	}
590 
591 	/**
592 	 * Verify all interesting objects are available, and reachable.
593 	 * <p>
594 	 * Callers should populate starting points and ending points with
595 	 * {@link #markStart(RevObject)} and {@link #markUninteresting(RevObject)}
596 	 * and then use this method to verify all objects between those two points
597 	 * exist in the repository and are readable.
598 	 * <p>
599 	 * This method returns successfully if everything is connected; it throws an
600 	 * exception if there is a connectivity problem. The exception message
601 	 * provides some detail about the connectivity failure.
602 	 *
603 	 * @throws org.eclipse.jgit.errors.MissingObjectException
604 	 *             one or more of the next objects are not available from the
605 	 *             object database, but were thought to be candidates for
606 	 *             traversal. This usually indicates a broken link.
607 	 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
608 	 *             one or more of the objects in a tree do not match the type
609 	 *             indicated.
610 	 * @throws java.io.IOException
611 	 *             a pack file or loose object could not be read.
612 	 */
613 	public void checkConnectivity() throws MissingObjectException,
614 			IncorrectObjectTypeException, IOException {
615 		for (;;) {
616 			final RevCommit c = next();
617 			if (c == null)
618 				break;
619 		}
620 		for (;;) {
621 			final RevObject o = nextObject();
622 			if (o == null)
623 				break;
624 			if (o instanceof RevBlob && !reader.has(o))
625 				throw new MissingObjectException(o, OBJ_BLOB);
626 		}
627 	}
628 
629 	/**
630 	 * Get the current object's complete path.
631 	 * <p>
632 	 * This method is not very efficient and is primarily meant for debugging
633 	 * and final output generation. Applications should try to avoid calling it,
634 	 * and if invoked do so only once per interesting entry, where the name is
635 	 * absolutely required for correct function.
636 	 *
637 	 * @return complete path of the current entry, from the root of the
638 	 *         repository. If the current entry is in a subtree there will be at
639 	 *         least one '/' in the returned string. Null if the current entry
640 	 *         has no path, such as for annotated tags or root level trees.
641 	 */
642 	public String getPathString() {
643 		if (pathLen == 0) {
644 			pathLen = updatePathBuf(currVisit);
645 			if (pathLen == 0)
646 				return null;
647 		}
648 		return RawParseUtils.decode(pathBuf, 0, pathLen);
649 	}
650 
651 	/**
652 	 * @return the current traversal depth from the root tree object
653 	 * @since 5.4
654 	 */
655 	public int getTreeDepth() {
656 		if (currVisit == null) {
657 			return 0;
658 		}
659 		return currVisit.depth;
660 	}
661 
662 	/**
663 	 * Get the current object's path hash code.
664 	 * <p>
665 	 * This method computes a hash code on the fly for this path, the hash is
666 	 * suitable to cluster objects that may have similar paths together.
667 	 *
668 	 * @return path hash code; any integer may be returned.
669 	 */
670 	public int getPathHashCode() {
671 		TreeVisit tv = currVisit;
672 		if (tv == null)
673 			return 0;
674 
675 		int nameEnd = tv.nameEnd;
676 		if (nameEnd == 0) {
677 			// When nameEnd == 0 the subtree is itself the current path
678 			// being visited. The name hash must be obtained from its
679 			// parent tree. If there is no parent, this is a root tree with
680 			// a hash code of 0.
681 			tv = tv.parent;
682 			if (tv == null)
683 				return 0;
684 			nameEnd = tv.nameEnd;
685 		}
686 
687 		byte[] buf;
688 		int ptr;
689 
690 		if (16 <= (nameEnd - tv.namePtr)) {
691 			buf = tv.buf;
692 			ptr = nameEnd - 16;
693 		} else {
694 			nameEnd = pathLen;
695 			if (nameEnd == 0) {
696 				nameEnd = updatePathBuf(currVisit);
697 				pathLen = nameEnd;
698 			}
699 			buf = pathBuf;
700 			ptr = Math.max(0, nameEnd - 16);
701 		}
702 
703 		int hash = 0;
704 		for (; ptr < nameEnd; ptr++) {
705 			byte c = buf[ptr];
706 			if (c != ' ')
707 				hash = (hash >>> 2) + (c << 24);
708 		}
709 		return hash;
710 	}
711 
712 	/**
713 	 * Get the internal buffer holding the current path.
714 	 *
715 	 * @return the internal buffer holding the current path.
716 	 */
717 	public byte[] getPathBuffer() {
718 		if (pathLen == 0)
719 			pathLen = updatePathBuf(currVisit);
720 		return pathBuf;
721 	}
722 
723 	/**
724 	 * Get length of the path in {@link #getPathBuffer()}.
725 	 *
726 	 * @return length of the path in {@link #getPathBuffer()}.
727 	 */
728 	public int getPathLength() {
729 		if (pathLen == 0)
730 			pathLen = updatePathBuf(currVisit);
731 		return pathLen;
732 	}
733 
734 	private int updatePathBuf(TreeVisit tv) {
735 		if (tv == null)
736 			return 0;
737 
738 		// If nameEnd == 0 this tree has not yet contributed an entry.
739 		// Update only for the parent, which if null will be empty.
740 		int nameEnd = tv.nameEnd;
741 		if (nameEnd == 0)
742 			return updatePathBuf(tv.parent);
743 
744 		int ptr = tv.pathLen;
745 		if (ptr == 0) {
746 			ptr = updatePathBuf(tv.parent);
747 			if (ptr == pathBuf.length)
748 				growPathBuf(ptr);
749 			if (ptr != 0)
750 				pathBuf[ptr++] = '/';
751 			tv.pathLen = ptr;
752 		}
753 
754 		int namePtr = tv.namePtr;
755 		int nameLen = nameEnd - namePtr;
756 		int end = ptr + nameLen;
757 		while (pathBuf.length < end)
758 			growPathBuf(ptr);
759 		System.arraycopy(tv.buf, namePtr, pathBuf, ptr, nameLen);
760 		return end;
761 	}
762 
763 	private void growPathBuf(int ptr) {
764 		byte[] newBuf = new byte[pathBuf.length << 1];
765 		System.arraycopy(pathBuf, 0, newBuf, 0, ptr);
766 		pathBuf = newBuf;
767 	}
768 
769 	/** {@inheritDoc} */
770 	@Override
771 	public void dispose() {
772 		super.dispose();
773 		pendingObjects = new BlockObjQueue();
774 		currVisit = null;
775 		freeVisit = null;
776 	}
777 
778 	/** {@inheritDoc} */
779 	@Override
780 	protected void reset(int retainFlags) {
781 		super.reset(retainFlags);
782 
783 		for (RevObject obj : rootObjects)
784 			obj.flags &= ~IN_PENDING;
785 
786 		rootObjects = new ArrayList<>();
787 		pendingObjects = new BlockObjQueue();
788 		currVisit = null;
789 		freeVisit = null;
790 	}
791 
792 	private void addObject(RevObject o) {
793 		if ((o.flags & IN_PENDING) == 0) {
794 			o.flags |= IN_PENDING;
795 			rootObjects.add(o);
796 			pendingObjects.add(o);
797 		}
798 	}
799 
800 	private void markTreeUninteresting(RevTree tree)
801 			throws MissingObjectException, IncorrectObjectTypeException,
802 			IOException {
803 		if ((tree.flags & UNINTERESTING) != 0)
804 			return;
805 		tree.flags |= UNINTERESTING;
806 
807 		byte[] raw = reader.open(tree, OBJ_TREE).getCachedBytes();
808 		for (int ptr = 0; ptr < raw.length;) {
809 			byte c = raw[ptr];
810 			int mode = c - '0';
811 			for (;;) {
812 				c = raw[++ptr];
813 				if (' ' == c)
814 					break;
815 				mode <<= 3;
816 				mode += c - '0';
817 			}
818 			while (raw[++ptr] != 0) {
819 				// Skip entry name.
820 			}
821 			ptr++; // Skip NUL after entry name.
822 
823 			switch (mode >>> TYPE_SHIFT) {
824 			case TYPE_FILE:
825 			case TYPE_SYMLINK:
826 				idBuffer.fromRaw(raw, ptr);
827 				lookupBlob(idBuffer).flags |= UNINTERESTING;
828 				break;
829 
830 			case TYPE_TREE:
831 				idBuffer.fromRaw(raw, ptr);
832 				markTreeUninteresting(lookupTree(idBuffer));
833 				break;
834 
835 			case TYPE_GITLINK:
836 				break;
837 
838 			default:
839 				idBuffer.fromRaw(raw, ptr);
840 				throw new CorruptObjectException(MessageFormat.format(
841 						JGitText.get().corruptObjectInvalidMode3,
842 						String.format("%o", Integer.valueOf(mode)), //$NON-NLS-1$
843 						idBuffer.name(), "", tree)); //$NON-NLS-1$
844 			}
845 			ptr += ID_SZ;
846 		}
847 	}
848 
849 	private RevObject/../../../org/eclipse/jgit/revwalk/RevObject.html#RevObject">RevObject pushTree(RevObject obj) throws LargeObjectException,
850 			MissingObjectException, IncorrectObjectTypeException, IOException {
851 		TreeVisit tv = freeVisit;
852 		if (tv != null) {
853 			freeVisit = tv.parent;
854 			tv.ptr = 0;
855 			tv.namePtr = 0;
856 			tv.nameEnd = 0;
857 			tv.pathLen = 0;
858 		} else {
859 			tv = new TreeVisit();
860 		}
861 		tv.obj = obj;
862 		tv.buf = reader.open(obj, OBJ_TREE).getCachedBytes();
863 		tv.parent = currVisit;
864 		currVisit = tv;
865 		if (tv.parent == null) {
866 			tv.depth = 1;
867 		} else {
868 			tv.depth = tv.parent.depth + 1;
869 		}
870 
871 		return obj;
872 	}
873 
874 	private void releaseTreeVisit(TreeVisit tv) {
875 		tv.buf = null;
876 		tv.parent = freeVisit;
877 		freeVisit = tv;
878 	}
879 
880 	private static class TreeVisit {
881 		/** Parent tree visit that entered this tree, null if root tree. */
882 		TreeVisit parent;
883 
884 		/** The RevTree currently being iterated through. */
885 		RevObject obj;
886 
887 		/** Canonical encoding of the tree named by {@link #obj}. */
888 		byte[] buf;
889 
890 		/** Index of next entry to parse in {@link #buf}. */
891 		int ptr;
892 
893 		/** Start of the current name entry in {@link #buf}. */
894 		int namePtr;
895 
896 		/** One past end of name, {@code nameEnd - namePtr} is the length. */
897 		int nameEnd;
898 
899 		/** Number of bytes in the path leading up to this tree. */
900 		int pathLen;
901 
902 		/** Number of levels deep from the root tree. 0 for root tree. */
903 		int depth;
904 	}
905 }