Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

ObjectWalk.java 25KB

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