Use a common shouldSkipObject method to avoid UNINTERESTING items
[egit/qmx.git] / org.spearce.jgit / src / org / spearce / jgit / revwalk / ObjectWalk.java
blob484d104d52807c1a711f7a91206669cfc5e874fc
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.revwalk;
40 import java.io.IOException;
42 import org.spearce.jgit.errors.CorruptObjectException;
43 import org.spearce.jgit.errors.IncorrectObjectTypeException;
44 import org.spearce.jgit.errors.MissingObjectException;
45 import org.spearce.jgit.lib.AnyObjectId;
46 import org.spearce.jgit.lib.Constants;
47 import org.spearce.jgit.lib.FileMode;
48 import org.spearce.jgit.lib.Repository;
49 import org.spearce.jgit.treewalk.CanonicalTreeParser;
51 /**
52 * Specialized subclass of RevWalk to include trees, blobs and tags.
53 * <p>
54 * Unlike RevWalk this subclass is able to remember starting roots that include
55 * annotated tags, or arbitrary trees or blobs. Once commit generation is
56 * complete and all commits have been popped by the application, individual
57 * annotated tag, tree and blob objects can be popped through the additional
58 * method {@link #nextObject()}.
59 * <p>
60 * Tree and blob objects reachable from interesting commits are automatically
61 * scheduled for inclusion in the results of {@link #nextObject()}, returning
62 * each object exactly once. Objects are sorted and returned according to the
63 * the commits that reference them and the order they appear within a tree.
64 * Ordering can be affected by changing the {@link RevSort} used to order the
65 * commits that are returned first.
67 public class ObjectWalk extends RevWalk {
68 /**
69 * Indicates a non-RevCommit is in {@link #pendingObjects}.
70 * <p>
71 * We can safely reuse {@link RevWalk#REWRITE} here for the same value as it
72 * is only set on RevCommit and {@link #pendingObjects} never has RevCommit
73 * instances inserted into it.
75 private static final int IN_PENDING = RevWalk.REWRITE;
77 private CanonicalTreeParser treeWalk;
79 private BlockObjQueue pendingObjects;
81 private RevTree currentTree;
83 private boolean fromTreeWalk;
85 private RevTree nextSubtree;
87 /**
88 * Create a new revision and object walker for a given repository.
90 * @param repo
91 * the repository the walker will obtain data from.
93 public ObjectWalk(final Repository repo) {
94 super(repo);
95 pendingObjects = new BlockObjQueue();
96 treeWalk = new CanonicalTreeParser();
99 /**
100 * Mark an object or commit to start graph traversal from.
101 * <p>
102 * Callers are encouraged to use {@link RevWalk#parseAny(AnyObjectId)}
103 * instead of {@link RevWalk#lookupAny(AnyObjectId, int)}, as this method
104 * requires the object to be parsed before it can be added as a root for the
105 * traversal.
106 * <p>
107 * The method will automatically parse an unparsed object, but error
108 * handling may be more difficult for the application to explain why a
109 * RevObject is not actually valid. The object pool of this walker would
110 * also be 'poisoned' by the invalid RevObject.
111 * <p>
112 * This method will automatically call {@link RevWalk#markStart(RevCommit)}
113 * if passed RevCommit instance, or a RevTag that directly (or indirectly)
114 * references a RevCommit.
116 * @param o
117 * the object to start traversing from. The object passed must be
118 * from this same revision walker.
119 * @throws MissingObjectException
120 * the object supplied is not available from the object
121 * database. This usually indicates the supplied object is
122 * invalid, but the reference was constructed during an earlier
123 * invocation to {@link RevWalk#lookupAny(AnyObjectId, int)}.
124 * @throws IncorrectObjectTypeException
125 * the object was not parsed yet and it was discovered during
126 * parsing that it is not actually the type of the instance
127 * passed in. This usually indicates the caller used the wrong
128 * type in a {@link RevWalk#lookupAny(AnyObjectId, int)} call.
129 * @throws IOException
130 * a pack file or loose object could not be read.
132 public void markStart(RevObject o) throws MissingObjectException,
133 IncorrectObjectTypeException, IOException {
134 while (o instanceof RevTag) {
135 addObject(o);
136 o = ((RevTag) o).getObject();
137 parse(o);
140 if (o instanceof RevCommit)
141 super.markStart((RevCommit) o);
142 else
143 addObject(o);
147 * Mark an object to not produce in the output.
148 * <p>
149 * Uninteresting objects denote not just themselves but also their entire
150 * reachable chain, back until the merge base of an uninteresting commit and
151 * an otherwise interesting commit.
152 * <p>
153 * Callers are encouraged to use {@link RevWalk#parseAny(AnyObjectId)}
154 * instead of {@link RevWalk#lookupAny(AnyObjectId, int)}, as this method
155 * requires the object to be parsed before it can be added as a root for the
156 * traversal.
157 * <p>
158 * The method will automatically parse an unparsed object, but error
159 * handling may be more difficult for the application to explain why a
160 * RevObject is not actually valid. The object pool of this walker would
161 * also be 'poisoned' by the invalid RevObject.
162 * <p>
163 * This method will automatically call {@link RevWalk#markStart(RevCommit)}
164 * if passed RevCommit instance, or a RevTag that directly (or indirectly)
165 * references a RevCommit.
167 * @param o
168 * the object to start traversing from. The object passed must be
169 * @throws MissingObjectException
170 * the object supplied is not available from the object
171 * database. This usually indicates the supplied object is
172 * invalid, but the reference was constructed during an earlier
173 * invocation to {@link RevWalk#lookupAny(AnyObjectId, int)}.
174 * @throws IncorrectObjectTypeException
175 * the object was not parsed yet and it was discovered during
176 * parsing that it is not actually the type of the instance
177 * passed in. This usually indicates the caller used the wrong
178 * type in a {@link RevWalk#lookupAny(AnyObjectId, int)} call.
179 * @throws IOException
180 * a pack file or loose object could not be read.
182 public void markUninteresting(RevObject o) throws MissingObjectException,
183 IncorrectObjectTypeException, IOException {
184 while (o instanceof RevTag) {
185 o.flags |= UNINTERESTING;
186 if (hasRevSort(RevSort.BOUNDARY))
187 addObject(o);
188 o = ((RevTag) o).getObject();
189 parse(o);
192 if (o instanceof RevCommit)
193 super.markUninteresting((RevCommit) o);
194 else if (o instanceof RevTree)
195 markTreeUninteresting((RevTree) o);
196 else
197 o.flags |= UNINTERESTING;
199 if (o.getType() != Constants.OBJ_COMMIT && hasRevSort(RevSort.BOUNDARY)) {
200 addObject(o);
204 @Override
205 public RevCommit next() throws MissingObjectException,
206 IncorrectObjectTypeException, IOException {
207 for (;;) {
208 final RevCommit r = super.next();
209 if (r == null)
210 return null;
211 if ((r.flags & UNINTERESTING) != 0) {
212 markTreeUninteresting(r.getTree());
213 if (hasRevSort(RevSort.BOUNDARY)) {
214 pendingObjects.add(r.getTree());
215 return r;
217 continue;
219 pendingObjects.add(r.getTree());
220 return r;
225 * Pop the next most recent object.
227 * @return next most recent object; null if traversal is over.
228 * @throws MissingObjectException
229 * one or or more of the next objects are not available from the
230 * object database, but were thought to be candidates for
231 * traversal. This usually indicates a broken link.
232 * @throws IncorrectObjectTypeException
233 * one or or more of the objects in a tree do not match the type
234 * indicated.
235 * @throws IOException
236 * a pack file or loose object could not be read.
238 public RevObject nextObject() throws MissingObjectException,
239 IncorrectObjectTypeException, IOException {
240 fromTreeWalk = false;
242 if (nextSubtree != null) {
243 treeWalk = treeWalk.createSubtreeIterator0(db, nextSubtree, curs);
244 nextSubtree = null;
247 while (!treeWalk.eof()) {
248 final FileMode mode = treeWalk.getEntryFileMode();
249 final int sType = mode.getObjectType();
251 switch (sType) {
252 case Constants.OBJ_BLOB: {
253 treeWalk.getEntryObjectId(idBuffer);
254 final RevBlob o = lookupBlob(idBuffer);
255 if ((o.flags & SEEN) != 0)
256 break;
257 o.flags |= SEEN;
258 if (shouldSkipObject(o))
259 break;
260 fromTreeWalk = true;
261 return o;
263 case Constants.OBJ_TREE: {
264 treeWalk.getEntryObjectId(idBuffer);
265 final RevTree o = lookupTree(idBuffer);
266 if ((o.flags & SEEN) != 0)
267 break;
268 o.flags |= SEEN;
269 if (shouldSkipObject(o))
270 break;
271 nextSubtree = o;
272 fromTreeWalk = true;
273 return o;
275 default:
276 if (FileMode.GITLINK.equals(mode))
277 break;
278 treeWalk.getEntryObjectId(idBuffer);
279 throw new CorruptObjectException("Invalid mode " + mode
280 + " for " + idBuffer.name() + " "
281 + treeWalk.getEntryPathString() + " in " + currentTree
282 + ".");
285 treeWalk = treeWalk.next();
288 for (;;) {
289 final RevObject o = pendingObjects.next();
290 if (o == null)
291 return null;
292 if ((o.flags & SEEN) != 0)
293 continue;
294 o.flags |= SEEN;
295 if (shouldSkipObject(o))
296 continue;
297 if (o instanceof RevTree) {
298 currentTree = (RevTree) o;
299 treeWalk = treeWalk.resetRoot(db, currentTree, curs);
301 return o;
305 private final boolean shouldSkipObject(final RevObject o) {
306 return (o.flags & UNINTERESTING) != 0 && !hasRevSort(RevSort.BOUNDARY);
310 * Verify all interesting objects are available, and reachable.
311 * <p>
312 * Callers should populate starting points and ending points with
313 * {@link #markStart(RevObject)} and {@link #markUninteresting(RevObject)}
314 * and then use this method to verify all objects between those two points
315 * exist in the repository and are readable.
316 * <p>
317 * This method returns successfully if everything is connected; it throws an
318 * exception if there is a connectivity problem. The exception message
319 * provides some detail about the connectivity failure.
321 * @throws MissingObjectException
322 * one or or more of the next objects are not available from the
323 * object database, but were thought to be candidates for
324 * traversal. This usually indicates a broken link.
325 * @throws IncorrectObjectTypeException
326 * one or or more of the objects in a tree do not match the type
327 * indicated.
328 * @throws IOException
329 * a pack file or loose object could not be read.
331 public void checkConnectivity() throws MissingObjectException,
332 IncorrectObjectTypeException, IOException {
333 for (;;) {
334 final RevCommit c = next();
335 if (c == null)
336 break;
338 for (;;) {
339 final RevObject o = nextObject();
340 if (o == null)
341 break;
342 if (o instanceof RevBlob && !db.hasObject(o))
343 throw new MissingObjectException(o, Constants.TYPE_BLOB);
348 * Get the current object's complete path.
349 * <p>
350 * This method is not very efficient and is primarily meant for debugging
351 * and final output generation. Applications should try to avoid calling it,
352 * and if invoked do so only once per interesting entry, where the name is
353 * absolutely required for correct function.
355 * @return complete path of the current entry, from the root of the
356 * repository. If the current entry is in a subtree there will be at
357 * least one '/' in the returned string. Null if the current entry
358 * has no path, such as for annotated tags or root level trees.
360 public String getPathString() {
361 return fromTreeWalk ? treeWalk.getEntryPathString() : null;
364 @Override
365 public void dispose() {
366 super.dispose();
367 pendingObjects = new BlockObjQueue();
368 nextSubtree = null;
369 currentTree = null;
372 @Override
373 protected void reset(final int retainFlags) {
374 super.reset(retainFlags);
375 pendingObjects = new BlockObjQueue();
376 nextSubtree = null;
379 private void addObject(final RevObject o) {
380 if ((o.flags & IN_PENDING) == 0) {
381 o.flags |= IN_PENDING;
382 pendingObjects.add(o);
386 private void markTreeUninteresting(final RevTree tree)
387 throws MissingObjectException, IncorrectObjectTypeException,
388 IOException {
389 if ((tree.flags & UNINTERESTING) != 0)
390 return;
391 tree.flags |= UNINTERESTING;
393 treeWalk = treeWalk.resetRoot(db, tree, curs);
394 while (!treeWalk.eof()) {
395 final FileMode mode = treeWalk.getEntryFileMode();
396 final int sType = mode.getObjectType();
398 switch (sType) {
399 case Constants.OBJ_BLOB: {
400 treeWalk.getEntryObjectId(idBuffer);
401 lookupBlob(idBuffer).flags |= UNINTERESTING;
402 break;
404 case Constants.OBJ_TREE: {
405 treeWalk.getEntryObjectId(idBuffer);
406 final RevTree t = lookupTree(idBuffer);
407 if ((t.flags & UNINTERESTING) == 0) {
408 t.flags |= UNINTERESTING;
409 treeWalk = treeWalk.createSubtreeIterator0(db, t, curs);
410 continue;
412 break;
414 default:
415 if (FileMode.GITLINK.equals(mode))
416 break;
417 treeWalk.getEntryObjectId(idBuffer);
418 throw new CorruptObjectException("Invalid mode " + mode
419 + " for " + idBuffer.name() + " "
420 + treeWalk.getEntryPathString() + " in " + tree + ".");
423 treeWalk = treeWalk.next();