traverse_commit_list: support pending blobs/trees with paths

When we call traverse_commit_list, we may have trees and blobs in the pending array. As we process these, we pass the "name" field from the pending entry as the path of the object within the tree (which then becomes the root path if we recurse into subtrees). When we set up the traversal in prepare_revision_walk, though, the "name" field of any pending trees and blobs is likely to be the ref at which we found the object. We would not want to make this part of the path (e.g., doing so would make "git rev-list --objects v2.6.11-tree" in linux.git show paths like "v2.6.11-tree/Makefile", which is nonsensical). Therefore prepare_revision_walk sets the name field of each pending tree and blobs to the empty string. However, this leaves no room for a caller who does know the correct path of a pending object to propagate that information to the revision walker. We can fix this by making two related changes: 1. Use the "path" field as the path instead of the "name" field in traverse_commit_list. If the path is not set, default to "" (which is what we always ended up with in the current code, because of prepare_revision_walk). 2. In prepare_revision_walk, make a complete copy of the entry. This makes the path field available to the walker (if there is one), solving our problem. Leaving the name field intact is now OK, as we do not use it as a path due to point (1) above (and we can use it to make more meaningful error messages if we want). We also make the original "mode" field available to the walker, though it does not actually use it. Note that we still re-add the pending objects and free the old ones (so we may strdup the path and name only to free the old ones). This could be made more efficient by simply copying the object_array entries that we are keeping. However, that would require more restructuring of the code, and is not done here. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Oct 15, 2014 at 18:43 UTC 207394908e9465d0169608725aeaa5bb355086e0
2 files changed +32 -9
list-objects.c
+5 -2
@@ -208,6 +208,7 @@ void traverse_commit_list(struct rev_info *revs,
208 struct object_array_entry *pending = revs->pending.objects + i;
209 struct object *obj = pending->item;
210 const char *name = pending->name;
211 + const char *path = pending->path;
212 if (obj->flags & (UNINTERESTING | SEEN))
213 continue;
214 if (obj->type == OBJ_TAG) {
@@ -215,14 +216,16 @@ void traverse_commit_list(struct rev_info *revs,
216 show_object(obj, NULL, name, data);
217 continue;
218 }
219 + if (!path)
220 + path = "";
221 if (obj->type == OBJ_TREE) {
222 process_tree(revs, (struct tree *)obj, show_object,
220 - NULL, &base, name, data);
223 + NULL, &base, path, data);
224 continue;
225 }
226 if (obj->type == OBJ_BLOB) {
227 process_blob(revs, (struct blob *)obj, show_object,
225 - NULL, name, data);
228 + NULL, path, data);
229 continue;
230 }
231 die("unknown pending object %s (%s)",
revision.c
+27 -7
@@ -198,9 +198,10 @@ void mark_parents_uninteresting(struct commit *commit)
198 }
199 }
200
201 -static void add_pending_object_with_mode(struct rev_info *revs,
201 +static void add_pending_object_with_path(struct rev_info *revs,
202 struct object *obj,
203 - const char *name, unsigned mode)
203 + const char *name, unsigned mode,
204 + const char *path)
205 {
206 if (!obj)
207 return;
@@ -220,7 +221,14 @@ static void add_pending_object_with_mode(struct rev_info *revs,
221 if (st)
222 return;
223 }
223 - add_object_array_with_mode(obj, name, &revs->pending, mode);
224 + add_object_array_with_path(obj, name, &revs->pending, mode, path);
225 +}
226 +
227 +static void add_pending_object_with_mode(struct rev_info *revs,
228 + struct object *obj,
229 + const char *name, unsigned mode)
230 +{
231 + add_pending_object_with_path(revs, obj, name, mode, NULL);
232 }
233
234 void add_pending_object(struct rev_info *revs,
@@ -265,8 +273,12 @@ void add_pending_sha1(struct rev_info *revs, const char *name,
273 }
274
275 static struct commit *handle_commit(struct rev_info *revs,
268 - struct object *object, const char *name)
276 + struct object_array_entry *entry)
277 {
278 + struct object *object = entry->item;
279 + const char *name = entry->name;
280 + const char *path = entry->path;
281 + unsigned int mode = entry->mode;
282 unsigned long flags = object->flags;
283
284 /*
@@ -285,6 +297,14 @@ static struct commit *handle_commit(struct rev_info *revs,
297 die("bad object %s", sha1_to_hex(tag->tagged->sha1));
298 }
299 object->flags |= flags;
300 + /*
301 + * We'll handle the tagged object by looping or dropping
302 + * through to the non-tag handlers below. Do not
303 + * propagate data from the tag's pending entry.
304 + */
305 + name = "";
306 + path = NULL;
307 + mode = 0;
308 }
309
310 /*
@@ -316,7 +336,7 @@ static struct commit *handle_commit(struct rev_info *revs,
336 mark_tree_contents_uninteresting(tree);
337 return NULL;
338 }
319 - add_pending_object(revs, object, "");
339 + add_pending_object_with_path(revs, object, name, mode, path);
340 return NULL;
341 }
342
@@ -328,7 +348,7 @@ static struct commit *handle_commit(struct rev_info *revs,
348 return NULL;
349 if (flags & UNINTERESTING)
350 return NULL;
331 - add_pending_object(revs, object, "");
351 + add_pending_object_with_path(revs, object, name, mode, path);
352 return NULL;
353 }
354 die("%s is unknown object", name);
@@ -2666,7 +2686,7 @@ int prepare_revision_walk(struct rev_info *revs)
2686 revs->pending.objects = NULL;
2687 for (i = 0; i < old_pending.nr; i++) {
2688 struct object_array_entry *e = old_pending.objects + i;
2669 - struct commit *commit = handle_commit(revs, e->item, e->name);
2689 + struct commit *commit = handle_commit(revs, e);
2690 if (commit) {
2691 if (!(commit->object.flags & SEEN)) {
2692 commit->object.flags |= SEEN;