You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ObjectWalk.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  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. package org.eclipse.jgit.revwalk;
  44. import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
  45. import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
  46. import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
  47. import java.io.IOException;
  48. import java.text.MessageFormat;
  49. import java.util.ArrayList;
  50. import java.util.List;
  51. import org.eclipse.jgit.errors.CorruptObjectException;
  52. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  53. import org.eclipse.jgit.errors.LargeObjectException;
  54. import org.eclipse.jgit.errors.MissingObjectException;
  55. import org.eclipse.jgit.internal.JGitText;
  56. import org.eclipse.jgit.lib.AnyObjectId;
  57. import org.eclipse.jgit.lib.ObjectReader;
  58. import org.eclipse.jgit.lib.Repository;
  59. import org.eclipse.jgit.util.RawParseUtils;
  60. /**
  61. * Specialized subclass of RevWalk to include trees, blobs and tags.
  62. * <p>
  63. * Unlike RevWalk this subclass is able to remember starting roots that include
  64. * annotated tags, or arbitrary trees or blobs. Once commit generation is
  65. * complete and all commits have been popped by the application, individual
  66. * annotated tag, tree and blob objects can be popped through the additional
  67. * method {@link #nextObject()}.
  68. * <p>
  69. * Tree and blob objects reachable from interesting commits are automatically
  70. * scheduled for inclusion in the results of {@link #nextObject()}, returning
  71. * each object exactly once. Objects are sorted and returned according to the
  72. * the commits that reference them and the order they appear within a tree.
  73. * Ordering can be affected by changing the {@link RevSort} used to order the
  74. * commits that are returned first.
  75. */
  76. public class ObjectWalk extends RevWalk {
  77. private static final int ID_SZ = 20;
  78. private static final int TYPE_SHIFT = 12;
  79. private static final int TYPE_TREE = 0040000 >>> TYPE_SHIFT;
  80. private static final int TYPE_SYMLINK = 0120000 >>> TYPE_SHIFT;
  81. private static final int TYPE_FILE = 0100000 >>> TYPE_SHIFT;
  82. private static final int TYPE_GITLINK = 0160000 >>> TYPE_SHIFT;
  83. /**
  84. * Indicates a non-RevCommit is in {@link #pendingObjects}.
  85. * <p>
  86. * We can safely reuse {@link RevWalk#REWRITE} here for the same value as it
  87. * is only set on RevCommit and {@link #pendingObjects} never has RevCommit
  88. * instances inserted into it.
  89. */
  90. private static final int IN_PENDING = RevWalk.REWRITE;
  91. private List<RevObject> rootObjects;
  92. private BlockObjQueue pendingObjects;
  93. private RevCommit firstCommit;
  94. private RevCommit lastCommit;
  95. private TreeVisit freeVisit;
  96. private TreeVisit currVisit;
  97. private byte[] pathBuf;
  98. private int pathLen;
  99. private boolean boundary;
  100. /**
  101. * Create a new revision and object walker for a given repository.
  102. *
  103. * @param repo
  104. * the repository the walker will obtain data from.
  105. */
  106. public ObjectWalk(final Repository repo) {
  107. this(repo.newObjectReader());
  108. }
  109. /**
  110. * Create a new revision and object walker for a given repository.
  111. *
  112. * @param or
  113. * the reader the walker will obtain data from. The reader should
  114. * be released by the caller when the walker is no longer
  115. * required.
  116. */
  117. public ObjectWalk(ObjectReader or) {
  118. super(or);
  119. rootObjects = new ArrayList<RevObject>();
  120. pendingObjects = new BlockObjQueue();
  121. pathBuf = new byte[256];
  122. }
  123. /**
  124. * Mark an object or commit to start graph traversal from.
  125. * <p>
  126. * Callers are encouraged to use {@link RevWalk#parseAny(AnyObjectId)}
  127. * instead of {@link RevWalk#lookupAny(AnyObjectId, int)}, as this method
  128. * requires the object to be parsed before it can be added as a root for the
  129. * traversal.
  130. * <p>
  131. * The method will automatically parse an unparsed object, but error
  132. * handling may be more difficult for the application to explain why a
  133. * RevObject is not actually valid. The object pool of this walker would
  134. * also be 'poisoned' by the invalid RevObject.
  135. * <p>
  136. * This method will automatically call {@link RevWalk#markStart(RevCommit)}
  137. * if passed RevCommit instance, or a RevTag that directly (or indirectly)
  138. * references a RevCommit.
  139. *
  140. * @param o
  141. * the object to start traversing from. The object passed must be
  142. * from this same revision walker.
  143. * @throws MissingObjectException
  144. * the object supplied is not available from the object
  145. * database. This usually indicates the supplied object is
  146. * invalid, but the reference was constructed during an earlier
  147. * invocation to {@link RevWalk#lookupAny(AnyObjectId, int)}.
  148. * @throws IncorrectObjectTypeException
  149. * the object was not parsed yet and it was discovered during
  150. * parsing that it is not actually the type of the instance
  151. * passed in. This usually indicates the caller used the wrong
  152. * type in a {@link RevWalk#lookupAny(AnyObjectId, int)} call.
  153. * @throws IOException
  154. * a pack file or loose object could not be read.
  155. */
  156. public void markStart(RevObject o) throws MissingObjectException,
  157. IncorrectObjectTypeException, IOException {
  158. while (o instanceof RevTag) {
  159. addObject(o);
  160. o = ((RevTag) o).getObject();
  161. parseHeaders(o);
  162. }
  163. if (o instanceof RevCommit)
  164. super.markStart((RevCommit) o);
  165. else
  166. addObject(o);
  167. }
  168. /**
  169. * Mark an object to not produce in the output.
  170. * <p>
  171. * Uninteresting objects denote not just themselves but also their entire
  172. * reachable chain, back until the merge base of an uninteresting commit and
  173. * an otherwise interesting commit.
  174. * <p>
  175. * Callers are encouraged to use {@link RevWalk#parseAny(AnyObjectId)}
  176. * instead of {@link RevWalk#lookupAny(AnyObjectId, int)}, as this method
  177. * requires the object to be parsed before it can be added as a root for the
  178. * traversal.
  179. * <p>
  180. * The method will automatically parse an unparsed object, but error
  181. * handling may be more difficult for the application to explain why a
  182. * RevObject is not actually valid. The object pool of this walker would
  183. * also be 'poisoned' by the invalid RevObject.
  184. * <p>
  185. * This method will automatically call {@link RevWalk#markStart(RevCommit)}
  186. * if passed RevCommit instance, or a RevTag that directly (or indirectly)
  187. * references a RevCommit.
  188. *
  189. * @param o
  190. * the object to start traversing from. The object passed must be
  191. * @throws MissingObjectException
  192. * the object supplied is not available from the object
  193. * database. This usually indicates the supplied object is
  194. * invalid, but the reference was constructed during an earlier
  195. * invocation to {@link RevWalk#lookupAny(AnyObjectId, int)}.
  196. * @throws IncorrectObjectTypeException
  197. * the object was not parsed yet and it was discovered during
  198. * parsing that it is not actually the type of the instance
  199. * passed in. This usually indicates the caller used the wrong
  200. * type in a {@link RevWalk#lookupAny(AnyObjectId, int)} call.
  201. * @throws IOException
  202. * a pack file or loose object could not be read.
  203. */
  204. public void markUninteresting(RevObject o) throws MissingObjectException,
  205. IncorrectObjectTypeException, IOException {
  206. while (o instanceof RevTag) {
  207. o.flags |= UNINTERESTING;
  208. if (boundary)
  209. addObject(o);
  210. o = ((RevTag) o).getObject();
  211. parseHeaders(o);
  212. }
  213. if (o instanceof RevCommit)
  214. markUninteresting((RevCommit) o);
  215. else if (o instanceof RevTree)
  216. markTreeUninteresting((RevTree) o);
  217. else
  218. o.flags |= UNINTERESTING;
  219. if (o.getType() != OBJ_COMMIT && boundary)
  220. addObject(o);
  221. }
  222. @Override
  223. public void markUninteresting(RevCommit c) throws MissingObjectException,
  224. IncorrectObjectTypeException, IOException {
  225. super.markUninteresting(c);
  226. try {
  227. markTreeUninteresting(c.getTree());
  228. } catch (MissingObjectException e) {
  229. // we don't care if the tree of the commit does not exist locally
  230. }
  231. }
  232. @Override
  233. public void sort(RevSort s) {
  234. super.sort(s);
  235. boundary = hasRevSort(RevSort.BOUNDARY);
  236. }
  237. @Override
  238. public void sort(RevSort s, boolean use) {
  239. super.sort(s, use);
  240. boundary = hasRevSort(RevSort.BOUNDARY);
  241. }
  242. @Override
  243. public RevCommit next() throws MissingObjectException,
  244. IncorrectObjectTypeException, IOException {
  245. for (;;) {
  246. final RevCommit r = super.next();
  247. if (r == null) {
  248. if (firstCommit != null)
  249. reader.walkAdviceBeginTrees(this, firstCommit, lastCommit);
  250. return null;
  251. }
  252. if ((r.flags & UNINTERESTING) != 0) {
  253. markTreeUninteresting(r.getTree());
  254. if (boundary)
  255. return r;
  256. continue;
  257. }
  258. if (firstCommit == null)
  259. firstCommit = r;
  260. lastCommit = r;
  261. pendingObjects.add(r.getTree());
  262. return r;
  263. }
  264. }
  265. /**
  266. * Pop the next most recent object.
  267. *
  268. * @return next most recent object; null if traversal is over.
  269. * @throws MissingObjectException
  270. * one or or more of the next objects are not available from the
  271. * object database, but were thought to be candidates for
  272. * traversal. This usually indicates a broken link.
  273. * @throws IncorrectObjectTypeException
  274. * one or or more of the objects in a tree do not match the type
  275. * indicated.
  276. * @throws IOException
  277. * a pack file or loose object could not be read.
  278. */
  279. public RevObject nextObject() throws MissingObjectException,
  280. IncorrectObjectTypeException, IOException {
  281. pathLen = 0;
  282. TreeVisit tv = currVisit;
  283. while (tv != null) {
  284. byte[] buf = tv.buf;
  285. for (int ptr = tv.ptr; ptr < buf.length;) {
  286. int startPtr = ptr;
  287. ptr = findObjectId(buf, ptr);
  288. idBuffer.fromRaw(buf, ptr);
  289. ptr += ID_SZ;
  290. RevObject obj = objects.get(idBuffer);
  291. if (obj != null && (obj.flags & SEEN) != 0)
  292. continue;
  293. int mode = parseMode(buf, startPtr, ptr, tv);
  294. int flags;
  295. switch (mode >>> TYPE_SHIFT) {
  296. case TYPE_FILE:
  297. case TYPE_SYMLINK:
  298. if (obj == null) {
  299. obj = new RevBlob(idBuffer);
  300. obj.flags = SEEN;
  301. objects.add(obj);
  302. return obj;
  303. }
  304. if (!(obj instanceof RevBlob))
  305. throw new IncorrectObjectTypeException(obj, OBJ_BLOB);
  306. obj.flags = flags = obj.flags | SEEN;
  307. if ((flags & UNINTERESTING) == 0)
  308. return obj;
  309. if (boundary)
  310. return obj;
  311. continue;
  312. case TYPE_TREE:
  313. if (obj == null) {
  314. obj = new RevTree(idBuffer);
  315. obj.flags = SEEN;
  316. objects.add(obj);
  317. return enterTree(obj);
  318. }
  319. if (!(obj instanceof RevTree))
  320. throw new IncorrectObjectTypeException(obj, OBJ_TREE);
  321. obj.flags = flags = obj.flags | SEEN;
  322. if ((flags & UNINTERESTING) == 0)
  323. return enterTree(obj);
  324. if (boundary)
  325. return enterTree(obj);
  326. continue;
  327. case TYPE_GITLINK:
  328. continue;
  329. default:
  330. throw new CorruptObjectException(MessageFormat.format(
  331. JGitText.get().corruptObjectInvalidMode3,
  332. String.format("%o", Integer.valueOf(mode)), //$NON-NLS-1$
  333. idBuffer.name(),
  334. RawParseUtils.decode(buf, tv.namePtr, tv.nameEnd),
  335. tv.obj));
  336. }
  337. }
  338. currVisit = tv.parent;
  339. releaseTreeVisit(tv);
  340. tv = currVisit;
  341. }
  342. for (;;) {
  343. RevObject o = pendingObjects.next();
  344. if (o == null) {
  345. reader.walkAdviceEnd();
  346. return null;
  347. }
  348. int flags = o.flags;
  349. if ((flags & SEEN) != 0)
  350. continue;
  351. flags |= SEEN;
  352. o.flags = flags;
  353. if ((flags & UNINTERESTING) == 0 | boundary) {
  354. if (o instanceof RevTree) {
  355. tv = newTreeVisit(o);
  356. tv.parent = null;
  357. currVisit = tv;
  358. }
  359. return o;
  360. }
  361. }
  362. }
  363. private RevObject enterTree(RevObject obj) throws MissingObjectException,
  364. IncorrectObjectTypeException, IOException {
  365. TreeVisit tv = newTreeVisit(obj);
  366. tv.parent = currVisit;
  367. currVisit = tv;
  368. return obj;
  369. }
  370. private static int findObjectId(byte[] buf, int ptr) {
  371. // Skip over the mode and name until the NUL before the ObjectId
  372. // can be located. Skip the NUL as the function returns.
  373. for (;;) {
  374. if (buf[++ptr] == 0) return ++ptr;
  375. if (buf[++ptr] == 0) return ++ptr;
  376. if (buf[++ptr] == 0) return ++ptr;
  377. if (buf[++ptr] == 0) return ++ptr;
  378. if (buf[++ptr] == 0) return ++ptr;
  379. if (buf[++ptr] == 0) return ++ptr;
  380. if (buf[++ptr] == 0) return ++ptr;
  381. if (buf[++ptr] == 0) return ++ptr;
  382. if (buf[++ptr] == 0) return ++ptr;
  383. if (buf[++ptr] == 0) return ++ptr;
  384. if (buf[++ptr] == 0) return ++ptr;
  385. if (buf[++ptr] == 0) return ++ptr;
  386. if (buf[++ptr] == 0) return ++ptr;
  387. if (buf[++ptr] == 0) return ++ptr;
  388. if (buf[++ptr] == 0) return ++ptr;
  389. if (buf[++ptr] == 0) return ++ptr;
  390. }
  391. }
  392. private static int parseMode(byte[] buf, int startPtr, int recEndPtr, TreeVisit tv) {
  393. int mode = buf[startPtr] - '0';
  394. for (;;) {
  395. byte c = buf[++startPtr];
  396. if (' ' == c)
  397. break;
  398. mode <<= 3;
  399. mode += c - '0';
  400. c = buf[++startPtr];
  401. if (' ' == c)
  402. break;
  403. mode <<= 3;
  404. mode += c - '0';
  405. c = buf[++startPtr];
  406. if (' ' == c)
  407. break;
  408. mode <<= 3;
  409. mode += c - '0';
  410. c = buf[++startPtr];
  411. if (' ' == c)
  412. break;
  413. mode <<= 3;
  414. mode += c - '0';
  415. c = buf[++startPtr];
  416. if (' ' == c)
  417. break;
  418. mode <<= 3;
  419. mode += c - '0';
  420. c = buf[++startPtr];
  421. if (' ' == c)
  422. break;
  423. mode <<= 3;
  424. mode += c - '0';
  425. c = buf[++startPtr];
  426. if (' ' == c)
  427. break;
  428. mode <<= 3;
  429. mode += c - '0';
  430. }
  431. tv.ptr = recEndPtr;
  432. tv.namePtr = startPtr + 1;
  433. tv.nameEnd = recEndPtr - (ID_SZ + 1);
  434. return mode;
  435. }
  436. /**
  437. * Verify all interesting objects are available, and reachable.
  438. * <p>
  439. * Callers should populate starting points and ending points with
  440. * {@link #markStart(RevObject)} and {@link #markUninteresting(RevObject)}
  441. * and then use this method to verify all objects between those two points
  442. * exist in the repository and are readable.
  443. * <p>
  444. * This method returns successfully if everything is connected; it throws an
  445. * exception if there is a connectivity problem. The exception message
  446. * provides some detail about the connectivity failure.
  447. *
  448. * @throws MissingObjectException
  449. * one or or more of the next objects are not available from the
  450. * object database, but were thought to be candidates for
  451. * traversal. This usually indicates a broken link.
  452. * @throws IncorrectObjectTypeException
  453. * one or or more of the objects in a tree do not match the type
  454. * indicated.
  455. * @throws IOException
  456. * a pack file or loose object could not be read.
  457. */
  458. public void checkConnectivity() throws MissingObjectException,
  459. IncorrectObjectTypeException, IOException {
  460. for (;;) {
  461. final RevCommit c = next();
  462. if (c == null)
  463. break;
  464. }
  465. for (;;) {
  466. final RevObject o = nextObject();
  467. if (o == null)
  468. break;
  469. if (o instanceof RevBlob && !reader.has(o))
  470. throw new MissingObjectException(o, OBJ_BLOB);
  471. }
  472. }
  473. /**
  474. * Get the current object's complete path.
  475. * <p>
  476. * This method is not very efficient and is primarily meant for debugging
  477. * and final output generation. Applications should try to avoid calling it,
  478. * and if invoked do so only once per interesting entry, where the name is
  479. * absolutely required for correct function.
  480. *
  481. * @return complete path of the current entry, from the root of the
  482. * repository. If the current entry is in a subtree there will be at
  483. * least one '/' in the returned string. Null if the current entry
  484. * has no path, such as for annotated tags or root level trees.
  485. */
  486. public String getPathString() {
  487. if (pathLen == 0) {
  488. pathLen = updatePathBuf(currVisit);
  489. if (pathLen == 0)
  490. return null;
  491. }
  492. return RawParseUtils.decode(pathBuf, 0, pathLen);
  493. }
  494. /**
  495. * Get the current object's path hash code.
  496. * <p>
  497. * This method computes a hash code on the fly for this path, the hash is
  498. * suitable to cluster objects that may have similar paths together.
  499. *
  500. * @return path hash code; any integer may be returned.
  501. */
  502. public int getPathHashCode() {
  503. TreeVisit tv = currVisit;
  504. if (tv == null)
  505. return 0;
  506. int nameEnd = tv.nameEnd;
  507. if (nameEnd == 0) {
  508. // When nameEnd == 0 the subtree is itself the current path
  509. // being visited. The name hash must be obtained from its
  510. // parent tree. If there is no parent, this is a root tree with
  511. // a hash code of 0.
  512. tv = tv.parent;
  513. if (tv == null)
  514. return 0;
  515. nameEnd = tv.nameEnd;
  516. }
  517. byte[] buf;
  518. int ptr;
  519. if (16 <= (nameEnd - tv.namePtr)) {
  520. buf = tv.buf;
  521. ptr = nameEnd - 16;
  522. } else {
  523. nameEnd = pathLen;
  524. if (nameEnd == 0) {
  525. nameEnd = updatePathBuf(currVisit);
  526. pathLen = nameEnd;
  527. }
  528. buf = pathBuf;
  529. ptr = Math.max(0, nameEnd - 16);
  530. }
  531. int hash = 0;
  532. for (; ptr < nameEnd; ptr++) {
  533. byte c = buf[ptr];
  534. if (c != ' ')
  535. hash = (hash >>> 2) + (c << 24);
  536. }
  537. return hash;
  538. }
  539. /** @return the internal buffer holding the current path. */
  540. public byte[] getPathBuffer() {
  541. if (pathLen == 0)
  542. pathLen = updatePathBuf(currVisit);
  543. return pathBuf;
  544. }
  545. /** @return length of the path in {@link #getPathBuffer()}. */
  546. public int getPathLength() {
  547. if (pathLen == 0)
  548. pathLen = updatePathBuf(currVisit);
  549. return pathLen;
  550. }
  551. private int updatePathBuf(TreeVisit tv) {
  552. if (tv == null)
  553. return 0;
  554. // If nameEnd == 0 this tree has not yet contributed an entry.
  555. // Update only for the parent, which if null will be empty.
  556. int nameEnd = tv.nameEnd;
  557. if (nameEnd == 0)
  558. return updatePathBuf(tv.parent);
  559. int ptr = tv.pathLen;
  560. if (ptr == 0) {
  561. ptr = updatePathBuf(tv.parent);
  562. if (ptr == pathBuf.length)
  563. growPathBuf(ptr);
  564. if (ptr != 0)
  565. pathBuf[ptr++] = '/';
  566. tv.pathLen = ptr;
  567. }
  568. int namePtr = tv.namePtr;
  569. int nameLen = nameEnd - namePtr;
  570. int end = ptr + nameLen;
  571. while (pathBuf.length < end)
  572. growPathBuf(ptr);
  573. System.arraycopy(tv.buf, namePtr, pathBuf, ptr, nameLen);
  574. return end;
  575. }
  576. private void growPathBuf(int ptr) {
  577. byte[] newBuf = new byte[pathBuf.length << 1];
  578. System.arraycopy(pathBuf, 0, newBuf, 0, ptr);
  579. pathBuf = newBuf;
  580. }
  581. @Override
  582. public void dispose() {
  583. super.dispose();
  584. pendingObjects = new BlockObjQueue();
  585. firstCommit = null;
  586. lastCommit = null;
  587. currVisit = null;
  588. freeVisit = null;
  589. }
  590. @Override
  591. protected void reset(final int retainFlags) {
  592. super.reset(retainFlags);
  593. for (RevObject obj : rootObjects)
  594. obj.flags &= ~IN_PENDING;
  595. rootObjects = new ArrayList<RevObject>();
  596. pendingObjects = new BlockObjQueue();
  597. firstCommit = null;
  598. lastCommit = null;
  599. currVisit = null;
  600. freeVisit = null;
  601. }
  602. private void addObject(final RevObject o) {
  603. if ((o.flags & IN_PENDING) == 0) {
  604. o.flags |= IN_PENDING;
  605. rootObjects.add(o);
  606. pendingObjects.add(o);
  607. }
  608. }
  609. private void markTreeUninteresting(final RevTree tree)
  610. throws MissingObjectException, IncorrectObjectTypeException,
  611. IOException {
  612. if ((tree.flags & UNINTERESTING) != 0)
  613. return;
  614. tree.flags |= UNINTERESTING;
  615. byte[] raw = reader.open(tree, OBJ_TREE).getCachedBytes();
  616. for (int ptr = 0; ptr < raw.length;) {
  617. byte c = raw[ptr];
  618. int mode = c - '0';
  619. for (;;) {
  620. c = raw[++ptr];
  621. if (' ' == c)
  622. break;
  623. mode <<= 3;
  624. mode += c - '0';
  625. }
  626. while (raw[++ptr] != 0) {
  627. // Skip entry name.
  628. }
  629. ptr++; // Skip NUL after entry name.
  630. switch (mode >>> TYPE_SHIFT) {
  631. case TYPE_FILE:
  632. case TYPE_SYMLINK:
  633. idBuffer.fromRaw(raw, ptr);
  634. lookupBlob(idBuffer).flags |= UNINTERESTING;
  635. break;
  636. case TYPE_TREE:
  637. idBuffer.fromRaw(raw, ptr);
  638. markTreeUninteresting(lookupTree(idBuffer));
  639. break;
  640. case TYPE_GITLINK:
  641. break;
  642. default:
  643. idBuffer.fromRaw(raw, ptr);
  644. throw new CorruptObjectException(MessageFormat.format(
  645. JGitText.get().corruptObjectInvalidMode3,
  646. String.format("%o", Integer.valueOf(mode)), //$NON-NLS-1$
  647. idBuffer.name(), "", tree)); //$NON-NLS-1$
  648. }
  649. ptr += ID_SZ;
  650. }
  651. }
  652. private TreeVisit newTreeVisit(RevObject obj) throws LargeObjectException,
  653. MissingObjectException, IncorrectObjectTypeException, IOException {
  654. TreeVisit tv = freeVisit;
  655. if (tv != null) {
  656. freeVisit = tv.parent;
  657. tv.ptr = 0;
  658. tv.namePtr = 0;
  659. tv.nameEnd = 0;
  660. tv.pathLen = 0;
  661. } else {
  662. tv = new TreeVisit();
  663. }
  664. tv.obj = obj;
  665. tv.buf = reader.open(obj, OBJ_TREE).getCachedBytes();
  666. return tv;
  667. }
  668. private void releaseTreeVisit(TreeVisit tv) {
  669. tv.buf = null;
  670. tv.parent = freeVisit;
  671. freeVisit = tv;
  672. }
  673. private static class TreeVisit {
  674. /** Parent tree visit that entered this tree, null if root tree. */
  675. TreeVisit parent;
  676. /** The RevTree currently being iterated through. */
  677. RevObject obj;
  678. /** Canonical encoding of the tree named by {@link #obj}. */
  679. byte[] buf;
  680. /** Index of next entry to parse in {@link #buf}. */
  681. int ptr;
  682. /** Start of the current name entry in {@link #buf}. */
  683. int namePtr;
  684. /** One past end of name, {@code nameEnd - namePtr} is the length. */
  685. int nameEnd;
  686. /** Number of bytes in the path leading up to this tree. */
  687. int pathLen;
  688. }
  689. }