選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

ObjectWalk.java 15KB

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