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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  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. super.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 sort(RevSort s) {
  224. super.sort(s);
  225. boundary = hasRevSort(RevSort.BOUNDARY);
  226. }
  227. @Override
  228. public void sort(RevSort s, boolean use) {
  229. super.sort(s, use);
  230. boundary = hasRevSort(RevSort.BOUNDARY);
  231. }
  232. @Override
  233. public RevCommit next() throws MissingObjectException,
  234. IncorrectObjectTypeException, IOException {
  235. for (;;) {
  236. final RevCommit r = super.next();
  237. if (r == null) {
  238. if (firstCommit != null)
  239. reader.walkAdviceBeginTrees(this, firstCommit, lastCommit);
  240. return null;
  241. }
  242. if ((r.flags & UNINTERESTING) != 0) {
  243. markTreeUninteresting(r.getTree());
  244. if (boundary)
  245. return r;
  246. continue;
  247. }
  248. if (firstCommit == null)
  249. firstCommit = r;
  250. lastCommit = r;
  251. pendingObjects.add(r.getTree());
  252. return r;
  253. }
  254. }
  255. /**
  256. * Pop the next most recent object.
  257. *
  258. * @return next most recent object; null if traversal is over.
  259. * @throws MissingObjectException
  260. * one or or more of the next objects are not available from the
  261. * object database, but were thought to be candidates for
  262. * traversal. This usually indicates a broken link.
  263. * @throws IncorrectObjectTypeException
  264. * one or or more of the objects in a tree do not match the type
  265. * indicated.
  266. * @throws IOException
  267. * a pack file or loose object could not be read.
  268. */
  269. public RevObject nextObject() throws MissingObjectException,
  270. IncorrectObjectTypeException, IOException {
  271. pathLen = 0;
  272. TreeVisit tv = currVisit;
  273. while (tv != null) {
  274. byte[] buf = tv.buf;
  275. for (int ptr = tv.ptr; ptr < buf.length;) {
  276. int startPtr = ptr;
  277. ptr = findObjectId(buf, ptr);
  278. idBuffer.fromRaw(buf, ptr);
  279. ptr += ID_SZ;
  280. RevObject obj = objects.get(idBuffer);
  281. if (obj != null && (obj.flags & SEEN) != 0)
  282. continue;
  283. int mode = parseMode(buf, startPtr, ptr, tv);
  284. int flags;
  285. switch (mode >>> TYPE_SHIFT) {
  286. case TYPE_FILE:
  287. case TYPE_SYMLINK:
  288. if (obj == null) {
  289. obj = new RevBlob(idBuffer);
  290. obj.flags = SEEN;
  291. objects.add(obj);
  292. return obj;
  293. }
  294. if (!(obj instanceof RevBlob))
  295. throw new IncorrectObjectTypeException(obj, OBJ_BLOB);
  296. obj.flags = flags = obj.flags | SEEN;
  297. if ((flags & UNINTERESTING) == 0)
  298. return obj;
  299. if (boundary)
  300. return obj;
  301. continue;
  302. case TYPE_TREE:
  303. if (obj == null) {
  304. obj = new RevTree(idBuffer);
  305. obj.flags = SEEN;
  306. objects.add(obj);
  307. return enterTree(obj);
  308. }
  309. if (!(obj instanceof RevTree))
  310. throw new IncorrectObjectTypeException(obj, OBJ_TREE);
  311. obj.flags = flags = obj.flags | SEEN;
  312. if ((flags & UNINTERESTING) == 0)
  313. return enterTree(obj);
  314. if (boundary)
  315. return enterTree(obj);
  316. continue;
  317. case TYPE_GITLINK:
  318. continue;
  319. default:
  320. throw new CorruptObjectException(MessageFormat.format(
  321. JGitText.get().corruptObjectInvalidMode3,
  322. String.format("%o", Integer.valueOf(mode)), //$NON-NLS-1$
  323. idBuffer.name(),
  324. RawParseUtils.decode(buf, tv.namePtr, tv.nameEnd),
  325. tv.obj));
  326. }
  327. }
  328. currVisit = tv.parent;
  329. releaseTreeVisit(tv);
  330. tv = currVisit;
  331. }
  332. for (;;) {
  333. RevObject o = pendingObjects.next();
  334. if (o == null) {
  335. reader.walkAdviceEnd();
  336. return null;
  337. }
  338. int flags = o.flags;
  339. if ((flags & SEEN) != 0)
  340. continue;
  341. flags |= SEEN;
  342. o.flags = flags;
  343. if ((flags & UNINTERESTING) == 0 | boundary) {
  344. if (o instanceof RevTree) {
  345. tv = newTreeVisit(o);
  346. tv.parent = null;
  347. currVisit = tv;
  348. }
  349. return o;
  350. }
  351. }
  352. }
  353. private RevObject enterTree(RevObject obj) throws MissingObjectException,
  354. IncorrectObjectTypeException, IOException {
  355. TreeVisit tv = newTreeVisit(obj);
  356. tv.parent = currVisit;
  357. currVisit = tv;
  358. return obj;
  359. }
  360. private static int findObjectId(byte[] buf, int ptr) {
  361. // Skip over the mode and name until the NUL before the ObjectId
  362. // can be located. Skip the NUL as the function returns.
  363. for (;;) {
  364. if (buf[++ptr] == 0) return ++ptr;
  365. if (buf[++ptr] == 0) return ++ptr;
  366. if (buf[++ptr] == 0) return ++ptr;
  367. if (buf[++ptr] == 0) return ++ptr;
  368. if (buf[++ptr] == 0) return ++ptr;
  369. if (buf[++ptr] == 0) return ++ptr;
  370. if (buf[++ptr] == 0) return ++ptr;
  371. if (buf[++ptr] == 0) return ++ptr;
  372. if (buf[++ptr] == 0) return ++ptr;
  373. if (buf[++ptr] == 0) return ++ptr;
  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. }
  381. }
  382. private static int parseMode(byte[] buf, int startPtr, int recEndPtr, TreeVisit tv) {
  383. int mode = buf[startPtr] - '0';
  384. for (;;) {
  385. byte c = buf[++startPtr];
  386. if (' ' == c)
  387. break;
  388. mode <<= 3;
  389. mode += c - '0';
  390. c = buf[++startPtr];
  391. if (' ' == c)
  392. break;
  393. mode <<= 3;
  394. mode += c - '0';
  395. 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. }
  421. tv.ptr = recEndPtr;
  422. tv.namePtr = startPtr + 1;
  423. tv.nameEnd = recEndPtr - (ID_SZ + 1);
  424. return mode;
  425. }
  426. /**
  427. * Verify all interesting objects are available, and reachable.
  428. * <p>
  429. * Callers should populate starting points and ending points with
  430. * {@link #markStart(RevObject)} and {@link #markUninteresting(RevObject)}
  431. * and then use this method to verify all objects between those two points
  432. * exist in the repository and are readable.
  433. * <p>
  434. * This method returns successfully if everything is connected; it throws an
  435. * exception if there is a connectivity problem. The exception message
  436. * provides some detail about the connectivity failure.
  437. *
  438. * @throws MissingObjectException
  439. * one or or more of the next objects are not available from the
  440. * object database, but were thought to be candidates for
  441. * traversal. This usually indicates a broken link.
  442. * @throws IncorrectObjectTypeException
  443. * one or or more of the objects in a tree do not match the type
  444. * indicated.
  445. * @throws IOException
  446. * a pack file or loose object could not be read.
  447. */
  448. public void checkConnectivity() throws MissingObjectException,
  449. IncorrectObjectTypeException, IOException {
  450. for (;;) {
  451. final RevCommit c = next();
  452. if (c == null)
  453. break;
  454. }
  455. for (;;) {
  456. final RevObject o = nextObject();
  457. if (o == null)
  458. break;
  459. if (o instanceof RevBlob && !reader.has(o))
  460. throw new MissingObjectException(o, OBJ_BLOB);
  461. }
  462. }
  463. /**
  464. * Get the current object's complete path.
  465. * <p>
  466. * This method is not very efficient and is primarily meant for debugging
  467. * and final output generation. Applications should try to avoid calling it,
  468. * and if invoked do so only once per interesting entry, where the name is
  469. * absolutely required for correct function.
  470. *
  471. * @return complete path of the current entry, from the root of the
  472. * repository. If the current entry is in a subtree there will be at
  473. * least one '/' in the returned string. Null if the current entry
  474. * has no path, such as for annotated tags or root level trees.
  475. */
  476. public String getPathString() {
  477. if (pathLen == 0) {
  478. pathLen = updatePathBuf(currVisit);
  479. if (pathLen == 0)
  480. return null;
  481. }
  482. return RawParseUtils.decode(pathBuf, 0, pathLen);
  483. }
  484. /**
  485. * Get the current object's path hash code.
  486. * <p>
  487. * This method computes a hash code on the fly for this path, the hash is
  488. * suitable to cluster objects that may have similar paths together.
  489. *
  490. * @return path hash code; any integer may be returned.
  491. */
  492. public int getPathHashCode() {
  493. TreeVisit tv = currVisit;
  494. if (tv == null)
  495. return 0;
  496. int nameEnd = tv.nameEnd;
  497. if (nameEnd == 0) {
  498. // When nameEnd == 0 the subtree is itself the current path
  499. // being visited. The name hash must be obtained from its
  500. // parent tree. If there is no parent, this is a root tree with
  501. // a hash code of 0.
  502. tv = tv.parent;
  503. if (tv == null)
  504. return 0;
  505. nameEnd = tv.nameEnd;
  506. }
  507. byte[] buf;
  508. int ptr;
  509. if (16 <= (nameEnd - tv.namePtr)) {
  510. buf = tv.buf;
  511. ptr = nameEnd - 16;
  512. } else {
  513. nameEnd = pathLen;
  514. if (nameEnd == 0) {
  515. nameEnd = updatePathBuf(currVisit);
  516. pathLen = nameEnd;
  517. }
  518. buf = pathBuf;
  519. ptr = Math.max(0, nameEnd - 16);
  520. }
  521. int hash = 0;
  522. for (; ptr < nameEnd; ptr++) {
  523. byte c = buf[ptr];
  524. if (c != ' ')
  525. hash = (hash >>> 2) + (c << 24);
  526. }
  527. return hash;
  528. }
  529. /** @return the internal buffer holding the current path. */
  530. public byte[] getPathBuffer() {
  531. if (pathLen == 0)
  532. pathLen = updatePathBuf(currVisit);
  533. return pathBuf;
  534. }
  535. /** @return length of the path in {@link #getPathBuffer()}. */
  536. public int getPathLength() {
  537. if (pathLen == 0)
  538. pathLen = updatePathBuf(currVisit);
  539. return pathLen;
  540. }
  541. private int updatePathBuf(TreeVisit tv) {
  542. if (tv == null)
  543. return 0;
  544. // If nameEnd == 0 this tree has not yet contributed an entry.
  545. // Update only for the parent, which if null will be empty.
  546. int nameEnd = tv.nameEnd;
  547. if (nameEnd == 0)
  548. return updatePathBuf(tv.parent);
  549. int ptr = tv.pathLen;
  550. if (ptr == 0) {
  551. ptr = updatePathBuf(tv.parent);
  552. if (ptr == pathBuf.length)
  553. growPathBuf(ptr);
  554. if (ptr != 0)
  555. pathBuf[ptr++] = '/';
  556. tv.pathLen = ptr;
  557. }
  558. int namePtr = tv.namePtr;
  559. int nameLen = nameEnd - namePtr;
  560. int end = ptr + nameLen;
  561. while (pathBuf.length < end)
  562. growPathBuf(ptr);
  563. System.arraycopy(tv.buf, namePtr, pathBuf, ptr, nameLen);
  564. return end;
  565. }
  566. private void growPathBuf(int ptr) {
  567. byte[] newBuf = new byte[pathBuf.length << 1];
  568. System.arraycopy(pathBuf, 0, newBuf, 0, ptr);
  569. pathBuf = newBuf;
  570. }
  571. @Override
  572. public void dispose() {
  573. super.dispose();
  574. pendingObjects = new BlockObjQueue();
  575. firstCommit = null;
  576. lastCommit = null;
  577. currVisit = null;
  578. freeVisit = null;
  579. }
  580. @Override
  581. protected void reset(final int retainFlags) {
  582. super.reset(retainFlags);
  583. for (RevObject obj : rootObjects)
  584. obj.flags &= ~IN_PENDING;
  585. rootObjects = new ArrayList<RevObject>();
  586. pendingObjects = new BlockObjQueue();
  587. firstCommit = null;
  588. lastCommit = null;
  589. currVisit = null;
  590. freeVisit = null;
  591. }
  592. private void addObject(final RevObject o) {
  593. if ((o.flags & IN_PENDING) == 0) {
  594. o.flags |= IN_PENDING;
  595. rootObjects.add(o);
  596. pendingObjects.add(o);
  597. }
  598. }
  599. private void markTreeUninteresting(final RevTree tree)
  600. throws MissingObjectException, IncorrectObjectTypeException,
  601. IOException {
  602. if ((tree.flags & UNINTERESTING) != 0)
  603. return;
  604. tree.flags |= UNINTERESTING;
  605. byte[] raw = reader.open(tree, OBJ_TREE).getCachedBytes();
  606. for (int ptr = 0; ptr < raw.length;) {
  607. byte c = raw[ptr];
  608. int mode = c - '0';
  609. for (;;) {
  610. c = raw[++ptr];
  611. if (' ' == c)
  612. break;
  613. mode <<= 3;
  614. mode += c - '0';
  615. }
  616. while (raw[++ptr] != 0) {
  617. // Skip entry name.
  618. }
  619. ptr++; // Skip NUL after entry name.
  620. switch (mode >>> TYPE_SHIFT) {
  621. case TYPE_FILE:
  622. case TYPE_SYMLINK:
  623. idBuffer.fromRaw(raw, ptr);
  624. lookupBlob(idBuffer).flags |= UNINTERESTING;
  625. break;
  626. case TYPE_TREE:
  627. idBuffer.fromRaw(raw, ptr);
  628. markTreeUninteresting(lookupTree(idBuffer));
  629. break;
  630. case TYPE_GITLINK:
  631. break;
  632. default:
  633. idBuffer.fromRaw(raw, ptr);
  634. throw new CorruptObjectException(MessageFormat.format(
  635. JGitText.get().corruptObjectInvalidMode3,
  636. String.format("%o", Integer.valueOf(mode)), //$NON-NLS-1$
  637. idBuffer.name(), "", tree)); //$NON-NLS-1$
  638. }
  639. ptr += ID_SZ;
  640. }
  641. }
  642. private TreeVisit newTreeVisit(RevObject obj) throws LargeObjectException,
  643. MissingObjectException, IncorrectObjectTypeException, IOException {
  644. TreeVisit tv = freeVisit;
  645. if (tv != null) {
  646. freeVisit = tv.parent;
  647. tv.ptr = 0;
  648. tv.namePtr = 0;
  649. tv.nameEnd = 0;
  650. tv.pathLen = 0;
  651. } else {
  652. tv = new TreeVisit();
  653. }
  654. tv.obj = obj;
  655. tv.buf = reader.open(obj, OBJ_TREE).getCachedBytes();
  656. return tv;
  657. }
  658. private void releaseTreeVisit(TreeVisit tv) {
  659. tv.buf = null;
  660. tv.parent = freeVisit;
  661. freeVisit = tv;
  662. }
  663. private static class TreeVisit {
  664. /** Parent tree visit that entered this tree, null if root tree. */
  665. TreeVisit parent;
  666. /** The RevTree currently being iterated through. */
  667. RevObject obj;
  668. /** Canonical encoding of the tree named by {@link #obj}. */
  669. byte[] buf;
  670. /** Index of next entry to parse in {@link #buf}. */
  671. int ptr;
  672. /** Start of the current name entry in {@link #buf}. */
  673. int namePtr;
  674. /** One past end of name, {@code nameEnd - namePtr} is the length. */
  675. int nameEnd;
  676. /** Number of bytes in the path leading up to this tree. */
  677. int pathLen;
  678. }
  679. }