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 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  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 java.io.IOException;
  45. import org.eclipse.jgit.errors.CorruptObjectException;
  46. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  47. import org.eclipse.jgit.errors.MissingObjectException;
  48. import org.eclipse.jgit.lib.AnyObjectId;
  49. import org.eclipse.jgit.lib.Constants;
  50. import org.eclipse.jgit.lib.FileMode;
  51. import org.eclipse.jgit.lib.Repository;
  52. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  53. /**
  54. * Specialized subclass of RevWalk to include trees, blobs and tags.
  55. * <p>
  56. * Unlike RevWalk this subclass is able to remember starting roots that include
  57. * annotated tags, or arbitrary trees or blobs. Once commit generation is
  58. * complete and all commits have been popped by the application, individual
  59. * annotated tag, tree and blob objects can be popped through the additional
  60. * method {@link #nextObject()}.
  61. * <p>
  62. * Tree and blob objects reachable from interesting commits are automatically
  63. * scheduled for inclusion in the results of {@link #nextObject()}, returning
  64. * each object exactly once. Objects are sorted and returned according to the
  65. * the commits that reference them and the order they appear within a tree.
  66. * Ordering can be affected by changing the {@link RevSort} used to order the
  67. * commits that are returned first.
  68. */
  69. public class ObjectWalk extends RevWalk {
  70. /**
  71. * Indicates a non-RevCommit is in {@link #pendingObjects}.
  72. * <p>
  73. * We can safely reuse {@link RevWalk#REWRITE} here for the same value as it
  74. * is only set on RevCommit and {@link #pendingObjects} never has RevCommit
  75. * instances inserted into it.
  76. */
  77. private static final int IN_PENDING = RevWalk.REWRITE;
  78. private CanonicalTreeParser treeWalk;
  79. private BlockObjQueue pendingObjects;
  80. private RevTree currentTree;
  81. private boolean fromTreeWalk;
  82. private RevTree nextSubtree;
  83. /**
  84. * Create a new revision and object walker for a given repository.
  85. *
  86. * @param repo
  87. * the repository the walker will obtain data from.
  88. */
  89. public ObjectWalk(final Repository repo) {
  90. super(repo);
  91. pendingObjects = new BlockObjQueue();
  92. treeWalk = new CanonicalTreeParser();
  93. }
  94. /**
  95. * Mark an object or commit to start graph traversal from.
  96. * <p>
  97. * Callers are encouraged to use {@link RevWalk#parseAny(AnyObjectId)}
  98. * instead of {@link RevWalk#lookupAny(AnyObjectId, int)}, as this method
  99. * requires the object to be parsed before it can be added as a root for the
  100. * traversal.
  101. * <p>
  102. * The method will automatically parse an unparsed object, but error
  103. * handling may be more difficult for the application to explain why a
  104. * RevObject is not actually valid. The object pool of this walker would
  105. * also be 'poisoned' by the invalid RevObject.
  106. * <p>
  107. * This method will automatically call {@link RevWalk#markStart(RevCommit)}
  108. * if passed RevCommit instance, or a RevTag that directly (or indirectly)
  109. * references a RevCommit.
  110. *
  111. * @param o
  112. * the object to start traversing from. The object passed must be
  113. * from this same revision walker.
  114. * @throws MissingObjectException
  115. * the object supplied is not available from the object
  116. * database. This usually indicates the supplied object is
  117. * invalid, but the reference was constructed during an earlier
  118. * invocation to {@link RevWalk#lookupAny(AnyObjectId, int)}.
  119. * @throws IncorrectObjectTypeException
  120. * the object was not parsed yet and it was discovered during
  121. * parsing that it is not actually the type of the instance
  122. * passed in. This usually indicates the caller used the wrong
  123. * type in a {@link RevWalk#lookupAny(AnyObjectId, int)} call.
  124. * @throws IOException
  125. * a pack file or loose object could not be read.
  126. */
  127. public void markStart(RevObject o) throws MissingObjectException,
  128. IncorrectObjectTypeException, IOException {
  129. while (o instanceof RevTag) {
  130. addObject(o);
  131. o = ((RevTag) o).getObject();
  132. parseHeaders(o);
  133. }
  134. if (o instanceof RevCommit)
  135. super.markStart((RevCommit) o);
  136. else
  137. addObject(o);
  138. }
  139. /**
  140. * Mark an object to not produce in the output.
  141. * <p>
  142. * Uninteresting objects denote not just themselves but also their entire
  143. * reachable chain, back until the merge base of an uninteresting commit and
  144. * an otherwise interesting commit.
  145. * <p>
  146. * Callers are encouraged to use {@link RevWalk#parseAny(AnyObjectId)}
  147. * instead of {@link RevWalk#lookupAny(AnyObjectId, int)}, as this method
  148. * requires the object to be parsed before it can be added as a root for the
  149. * traversal.
  150. * <p>
  151. * The method will automatically parse an unparsed object, but error
  152. * handling may be more difficult for the application to explain why a
  153. * RevObject is not actually valid. The object pool of this walker would
  154. * also be 'poisoned' by the invalid RevObject.
  155. * <p>
  156. * This method will automatically call {@link RevWalk#markStart(RevCommit)}
  157. * if passed RevCommit instance, or a RevTag that directly (or indirectly)
  158. * references a RevCommit.
  159. *
  160. * @param o
  161. * the object to start traversing from. The object passed must be
  162. * @throws MissingObjectException
  163. * the object supplied is not available from the object
  164. * database. This usually indicates the supplied object is
  165. * invalid, but the reference was constructed during an earlier
  166. * invocation to {@link RevWalk#lookupAny(AnyObjectId, int)}.
  167. * @throws IncorrectObjectTypeException
  168. * the object was not parsed yet and it was discovered during
  169. * parsing that it is not actually the type of the instance
  170. * passed in. This usually indicates the caller used the wrong
  171. * type in a {@link RevWalk#lookupAny(AnyObjectId, int)} call.
  172. * @throws IOException
  173. * a pack file or loose object could not be read.
  174. */
  175. public void markUninteresting(RevObject o) throws MissingObjectException,
  176. IncorrectObjectTypeException, IOException {
  177. while (o instanceof RevTag) {
  178. o.flags |= UNINTERESTING;
  179. if (hasRevSort(RevSort.BOUNDARY))
  180. addObject(o);
  181. o = ((RevTag) o).getObject();
  182. parseHeaders(o);
  183. }
  184. if (o instanceof RevCommit)
  185. super.markUninteresting((RevCommit) o);
  186. else if (o instanceof RevTree)
  187. markTreeUninteresting((RevTree) o);
  188. else
  189. o.flags |= UNINTERESTING;
  190. if (o.getType() != Constants.OBJ_COMMIT && hasRevSort(RevSort.BOUNDARY)) {
  191. addObject(o);
  192. }
  193. }
  194. @Override
  195. public RevCommit next() throws MissingObjectException,
  196. IncorrectObjectTypeException, IOException {
  197. for (;;) {
  198. final RevCommit r = super.next();
  199. if (r == null)
  200. return null;
  201. if ((r.flags & UNINTERESTING) != 0) {
  202. markTreeUninteresting(r.getTree());
  203. if (hasRevSort(RevSort.BOUNDARY)) {
  204. pendingObjects.add(r.getTree());
  205. return r;
  206. }
  207. continue;
  208. }
  209. pendingObjects.add(r.getTree());
  210. return r;
  211. }
  212. }
  213. /**
  214. * Pop the next most recent object.
  215. *
  216. * @return next most recent object; null if traversal is over.
  217. * @throws MissingObjectException
  218. * one or or more of the next objects are not available from the
  219. * object database, but were thought to be candidates for
  220. * traversal. This usually indicates a broken link.
  221. * @throws IncorrectObjectTypeException
  222. * one or or more of the objects in a tree do not match the type
  223. * indicated.
  224. * @throws IOException
  225. * a pack file or loose object could not be read.
  226. */
  227. public RevObject nextObject() throws MissingObjectException,
  228. IncorrectObjectTypeException, IOException {
  229. fromTreeWalk = false;
  230. if (nextSubtree != null) {
  231. treeWalk = treeWalk.createSubtreeIterator0(db, nextSubtree, curs);
  232. nextSubtree = null;
  233. }
  234. while (!treeWalk.eof()) {
  235. final FileMode mode = treeWalk.getEntryFileMode();
  236. final int sType = mode.getObjectType();
  237. switch (sType) {
  238. case Constants.OBJ_BLOB: {
  239. treeWalk.getEntryObjectId(idBuffer);
  240. final RevBlob o = lookupBlob(idBuffer);
  241. if ((o.flags & SEEN) != 0)
  242. break;
  243. o.flags |= SEEN;
  244. if (shouldSkipObject(o))
  245. break;
  246. fromTreeWalk = true;
  247. return o;
  248. }
  249. case Constants.OBJ_TREE: {
  250. treeWalk.getEntryObjectId(idBuffer);
  251. final RevTree o = lookupTree(idBuffer);
  252. if ((o.flags & SEEN) != 0)
  253. break;
  254. o.flags |= SEEN;
  255. if (shouldSkipObject(o))
  256. break;
  257. nextSubtree = o;
  258. fromTreeWalk = true;
  259. return o;
  260. }
  261. default:
  262. if (FileMode.GITLINK.equals(mode))
  263. break;
  264. treeWalk.getEntryObjectId(idBuffer);
  265. throw new CorruptObjectException("Invalid mode " + mode
  266. + " for " + idBuffer.name() + " '"
  267. + treeWalk.getEntryPathString() + "' in "
  268. + currentTree.name() + ".");
  269. }
  270. treeWalk = treeWalk.next();
  271. }
  272. for (;;) {
  273. final RevObject o = pendingObjects.next();
  274. if (o == null)
  275. return null;
  276. if ((o.flags & SEEN) != 0)
  277. continue;
  278. o.flags |= SEEN;
  279. if (shouldSkipObject(o))
  280. continue;
  281. if (o instanceof RevTree) {
  282. currentTree = (RevTree) o;
  283. treeWalk = treeWalk.resetRoot(db, currentTree, curs);
  284. }
  285. return o;
  286. }
  287. }
  288. private final boolean shouldSkipObject(final RevObject o) {
  289. return (o.flags & UNINTERESTING) != 0 && !hasRevSort(RevSort.BOUNDARY);
  290. }
  291. /**
  292. * Verify all interesting objects are available, and reachable.
  293. * <p>
  294. * Callers should populate starting points and ending points with
  295. * {@link #markStart(RevObject)} and {@link #markUninteresting(RevObject)}
  296. * and then use this method to verify all objects between those two points
  297. * exist in the repository and are readable.
  298. * <p>
  299. * This method returns successfully if everything is connected; it throws an
  300. * exception if there is a connectivity problem. The exception message
  301. * provides some detail about the connectivity failure.
  302. *
  303. * @throws MissingObjectException
  304. * one or or more of the next objects are not available from the
  305. * object database, but were thought to be candidates for
  306. * traversal. This usually indicates a broken link.
  307. * @throws IncorrectObjectTypeException
  308. * one or or more of the objects in a tree do not match the type
  309. * indicated.
  310. * @throws IOException
  311. * a pack file or loose object could not be read.
  312. */
  313. public void checkConnectivity() throws MissingObjectException,
  314. IncorrectObjectTypeException, IOException {
  315. for (;;) {
  316. final RevCommit c = next();
  317. if (c == null)
  318. break;
  319. }
  320. for (;;) {
  321. final RevObject o = nextObject();
  322. if (o == null)
  323. break;
  324. if (o instanceof RevBlob && !db.hasObject(o))
  325. throw new MissingObjectException(o, Constants.TYPE_BLOB);
  326. }
  327. }
  328. /**
  329. * Get the current object's complete path.
  330. * <p>
  331. * This method is not very efficient and is primarily meant for debugging
  332. * and final output generation. Applications should try to avoid calling it,
  333. * and if invoked do so only once per interesting entry, where the name is
  334. * absolutely required for correct function.
  335. *
  336. * @return complete path of the current entry, from the root of the
  337. * repository. If the current entry is in a subtree there will be at
  338. * least one '/' in the returned string. Null if the current entry
  339. * has no path, such as for annotated tags or root level trees.
  340. */
  341. public String getPathString() {
  342. return fromTreeWalk ? treeWalk.getEntryPathString() : null;
  343. }
  344. @Override
  345. public void dispose() {
  346. super.dispose();
  347. pendingObjects = new BlockObjQueue();
  348. treeWalk = new CanonicalTreeParser();
  349. nextSubtree = null;
  350. currentTree = null;
  351. }
  352. @Override
  353. protected void reset(final int retainFlags) {
  354. super.reset(retainFlags);
  355. pendingObjects = new BlockObjQueue();
  356. treeWalk = new CanonicalTreeParser();
  357. nextSubtree = null;
  358. }
  359. private void addObject(final RevObject o) {
  360. if ((o.flags & IN_PENDING) == 0) {
  361. o.flags |= IN_PENDING;
  362. pendingObjects.add(o);
  363. }
  364. }
  365. private void markTreeUninteresting(final RevTree tree)
  366. throws MissingObjectException, IncorrectObjectTypeException,
  367. IOException {
  368. if ((tree.flags & UNINTERESTING) != 0)
  369. return;
  370. tree.flags |= UNINTERESTING;
  371. treeWalk = treeWalk.resetRoot(db, tree, curs);
  372. while (!treeWalk.eof()) {
  373. final FileMode mode = treeWalk.getEntryFileMode();
  374. final int sType = mode.getObjectType();
  375. switch (sType) {
  376. case Constants.OBJ_BLOB: {
  377. treeWalk.getEntryObjectId(idBuffer);
  378. lookupBlob(idBuffer).flags |= UNINTERESTING;
  379. break;
  380. }
  381. case Constants.OBJ_TREE: {
  382. treeWalk.getEntryObjectId(idBuffer);
  383. final RevTree t = lookupTree(idBuffer);
  384. if ((t.flags & UNINTERESTING) == 0) {
  385. t.flags |= UNINTERESTING;
  386. treeWalk = treeWalk.createSubtreeIterator0(db, t, curs);
  387. continue;
  388. }
  389. break;
  390. }
  391. default:
  392. if (FileMode.GITLINK.equals(mode))
  393. break;
  394. treeWalk.getEntryObjectId(idBuffer);
  395. throw new CorruptObjectException("Invalid mode " + mode
  396. + " for " + idBuffer.name() + " "
  397. + treeWalk.getEntryPathString() + " in " + tree + ".");
  398. }
  399. treeWalk = treeWalk.next();
  400. }
  401. }
  402. }