2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
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
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
.ObjectId
;
49 import org
.spearce
.jgit
.lib
.Repository
;
50 import org
.spearce
.jgit
.treewalk
.TreeWalk
;
53 * Specialized subclass of RevWalk to include trees, blobs and tags.
55 * Unlike RevWalk this subclass is able to remember starting roots that include
56 * annotated tags, or arbitrary trees or blobs. Once commit generation is
57 * complete and all commits have been popped by the application, individual
58 * annotated tag, tree and blob objects can be popped through the additional
59 * method {@link #nextObject()}.
61 * Tree and blob objects reachable from interesting commits are automatically
62 * scheduled for inclusion in the results of {@link #nextObject()}, returning
63 * each object exactly once. Objects are sorted and returned according to the
64 * the commits that reference them and the order they appear within a tree.
65 * Ordering can be affected by changing the {@link RevSort} used to order the
66 * commits that are returned first.
68 public class ObjectWalk
extends RevWalk
{
69 private final TreeWalk treeWalk
;
71 private BlockObjQueue objects
;
73 private RevTree currentTree
;
75 private boolean fromTreeWalk
;
77 private boolean enterSubtree
;
80 * Create a new revision and object walker for a given repository.
83 * the repository the walker will obtain data from.
85 public ObjectWalk(final Repository repo
) {
87 treeWalk
= new TreeWalk(repo
);
88 objects
= new BlockObjQueue();
92 * Mark an object or commit to start graph traversal from.
94 * Callers are encouraged to use {@link RevWalk#parseAny(AnyObjectId)}
95 * instead of {@link RevWalk#lookupAny(AnyObjectId, int)}, as this method
96 * requires the object to be parsed before it can be added as a root for the
99 * The method will automatically parse an unparsed object, but error
100 * handling may be more difficult for the application to explain why a
101 * RevObject is not actually valid. The object pool of this walker would
102 * also be 'poisoned' by the invalid RevObject.
104 * This method will automatically call {@link RevWalk#markStart(RevCommit)}
105 * if passed RevCommit instance, or a RevTag that directly (or indirectly)
106 * references a RevCommit.
109 * the object to start traversing from. The object passed must be
110 * from this same revision walker.
111 * @throws MissingObjectException
112 * the object supplied is not available from the object
113 * database. This usually indicates the supplied object is
114 * invalid, but the reference was constructed during an earlier
115 * invocation to {@link RevWalk#lookupAny(AnyObjectId, int)}.
116 * @throws IncorrectObjectTypeException
117 * the object was not parsed yet and it was discovered during
118 * parsing that it is not actually the type of the instance
119 * passed in. This usually indicates the caller used the wrong
120 * type in a {@link RevWalk#lookupAny(AnyObjectId, int)} call.
121 * @throws IOException
122 * a pack file or loose object could not be read.
124 public void markStart(RevObject o
) throws MissingObjectException
,
125 IncorrectObjectTypeException
, IOException
{
126 while (o
instanceof RevTag
) {
128 o
= ((RevTag
) o
).getObject();
132 if (o
instanceof RevCommit
)
133 super.markStart((RevCommit
) o
);
139 * Mark an object to not produce in the output.
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.
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
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.
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.
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.
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
))
180 o
= ((RevTag
) o
).getObject();
184 if (o
instanceof RevCommit
)
185 super.markUninteresting((RevCommit
) o
);
186 else if (o
instanceof RevTree
)
187 markTreeUninteresting((RevTree
) o
);
189 o
.flags
|= UNINTERESTING
;
191 if (o
.getType() != Constants
.OBJ_COMMIT
&& hasRevSort(RevSort
.BOUNDARY
)) {
197 public RevCommit
next() throws MissingObjectException
,
198 IncorrectObjectTypeException
, IOException
{
200 final RevCommit r
= super.next();
203 if ((r
.flags
& UNINTERESTING
) != 0) {
204 markTreeUninteresting(r
.getTree());
205 if (hasRevSort(RevSort
.BOUNDARY
)) {
206 objects
.add(r
.getTree());
211 objects
.add(r
.getTree());
217 * Pop the next most recent object.
219 * @return next most recent object; null if traversal is over.
220 * @throws MissingObjectException
221 * one or or more of the next objects are not available from the
222 * object database, but were thought to be candidates for
223 * traversal. This usually indicates a broken link.
224 * @throws IncorrectObjectTypeException
225 * one or or more of the objects in a tree do not match the type
227 * @throws IOException
228 * a pack file or loose object could not be read.
230 public RevObject
nextObject() throws MissingObjectException
,
231 IncorrectObjectTypeException
, IOException
{
232 fromTreeWalk
= false;
235 treeWalk
.enterSubtree();
236 enterSubtree
= false;
239 while (treeWalk
.next()) {
240 final FileMode mode
= treeWalk
.getFileMode(0);
241 final int sType
= mode
.getObjectType();
244 case Constants
.OBJ_BLOB
: {
245 final RevObject o
= lookupAny(treeWalk
.getObjectId(0), sType
);
246 if ((o
.flags
& SEEN
) != 0)
249 if ((o
.flags
& UNINTERESTING
) != 0
250 && !hasRevSort(RevSort
.BOUNDARY
))
255 case Constants
.OBJ_TREE
: {
256 final RevObject o
= lookupAny(treeWalk
.getObjectId(0), sType
);
257 if ((o
.flags
& SEEN
) != 0)
260 if ((o
.flags
& UNINTERESTING
) != 0
261 && !hasRevSort(RevSort
.BOUNDARY
))
268 if (FileMode
.GITLINK
.equals(mode
))
270 throw new CorruptObjectException("Invalid mode " + mode
271 + " for " + treeWalk
.getObjectId(0) + " "
272 + treeWalk
.getPathString() + " in " + currentTree
+ ".");
277 final RevObject o
= objects
.next();
280 if ((o
.flags
& SEEN
) != 0)
283 if ((o
.flags
& UNINTERESTING
) != 0 && !hasRevSort(RevSort
.BOUNDARY
))
285 if (o
instanceof RevTree
) {
286 currentTree
= (RevTree
) o
;
287 treeWalk
.reset(new ObjectId
[] { currentTree
});
294 * Verify all interesting objects are available, and reachable.
296 * Callers should populate starting points and ending points with
297 * {@link #markStart(RevObject)} and {@link #markUninteresting(RevObject)}
298 * and then use this method to verify all objects between those two points
299 * exist in the repository and are readable.
301 * This method returns successfully if everything is connected; it throws an
302 * exception if there is a connectivity problem. The exception message
303 * provides some detail about the connectivity failure.
305 * @throws MissingObjectException
306 * one or or more of the next objects are not available from the
307 * object database, but were thought to be candidates for
308 * traversal. This usually indicates a broken link.
309 * @throws IncorrectObjectTypeException
310 * one or or more of the objects in a tree do not match the type
312 * @throws IOException
313 * a pack file or loose object could not be read.
315 public void checkConnectivity() throws MissingObjectException
,
316 IncorrectObjectTypeException
, IOException
{
318 final RevCommit c
= next();
323 final RevObject o
= nextObject();
326 if (o
instanceof RevBlob
&& !db
.hasObject(o
))
327 throw new MissingObjectException(o
, Constants
.TYPE_BLOB
);
332 * Get the current object's complete path.
334 * This method is not very efficient and is primarily meant for debugging
335 * and final output generation. Applications should try to avoid calling it,
336 * and if invoked do so only once per interesting entry, where the name is
337 * absolutely required for correct function.
339 * @return complete path of the current entry, from the root of the
340 * repository. If the current entry is in a subtree there will be at
341 * least one '/' in the returned string. Null if the current entry
342 * has no path, such as for annotated tags or root level trees.
344 public String
getPathString() {
345 return fromTreeWalk ? treeWalk
.getPathString() : null;
349 public void dispose() {
351 objects
= new BlockObjQueue();
352 enterSubtree
= false;
357 protected void reset(final int retainFlags
) {
358 super.reset(retainFlags
);
359 objects
= new BlockObjQueue();
360 enterSubtree
= false;
363 private void addObject(final RevObject o
) {
364 if ((o
.flags
& SEEN
) == 0) {
370 private void markTreeUninteresting(final RevTree tree
)
371 throws MissingObjectException
, IncorrectObjectTypeException
,
373 if ((tree
.flags
& UNINTERESTING
) != 0)
375 tree
.flags
|= UNINTERESTING
;
377 treeWalk
.reset(new ObjectId
[] { tree
});
378 while (treeWalk
.next()) {
379 final FileMode mode
= treeWalk
.getFileMode(0);
380 final int sType
= mode
.getObjectType();
383 case Constants
.OBJ_BLOB
: {
384 final ObjectId sid
= treeWalk
.getObjectId(0);
385 lookupAny(sid
, sType
).flags
|= UNINTERESTING
;
388 case Constants
.OBJ_TREE
: {
389 final ObjectId sid
= treeWalk
.getObjectId(0);
390 final RevObject subtree
= lookupAny(sid
, sType
);
391 if ((subtree
.flags
& UNINTERESTING
) == 0) {
392 subtree
.flags
|= UNINTERESTING
;
393 treeWalk
.enterSubtree();
398 if (FileMode
.GITLINK
.equals(mode
))
400 throw new CorruptObjectException("Invalid mode " + mode
401 + " for " + treeWalk
.getObjectId(0) + " "
402 + treeWalk
.getPathString() + " in " + tree
+ ".");