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

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