Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

ObjectWalk.java 23KB

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