traverse_commit_list: support pending blobs/trees with paths
authorJeff King <peff@peff.net>
Wed, 15 Oct 2014 22:43:19 +0000 (18:43 -0400)
committerJunio C Hamano <gitster@pobox.com>
Sun, 19 Oct 2014 22:06:31 +0000 (15:06 -0700)
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>
list-objects.c
revision.c
index fad6808aad8dd601e0c2e7724a955d64ee73f4e9..2910becd6c65b66670215aefdf08e1933895aa39 100644 (file)
@@ -208,6 +208,7 @@ void traverse_commit_list(struct rev_info *revs,
                struct object_array_entry *pending = revs->pending.objects + i;
                struct object *obj = pending->item;
                const char *name = pending->name;
+               const char *path = pending->path;
                if (obj->flags & (UNINTERESTING | SEEN))
                        continue;
                if (obj->type == OBJ_TAG) {
@@ -215,14 +216,16 @@ void traverse_commit_list(struct rev_info *revs,
                        show_object(obj, NULL, name, data);
                        continue;
                }
+               if (!path)
+                       path = "";
                if (obj->type == OBJ_TREE) {
                        process_tree(revs, (struct tree *)obj, show_object,
-                                    NULL, &base, name, data);
+                                    NULL, &base, path, data);
                        continue;
                }
                if (obj->type == OBJ_BLOB) {
                        process_blob(revs, (struct blob *)obj, show_object,
-                                    NULL, name, data);
+                                    NULL, path, data);
                        continue;
                }
                die("unknown pending object %s (%s)",
index b8e02e2798d756c4069dc3092a13d309b84a399a..ebe3e93d1298c542ae655e3af3b5b2761f0061de 100644 (file)
@@ -198,9 +198,10 @@ void mark_parents_uninteresting(struct commit *commit)
        }
 }
 
-static void add_pending_object_with_mode(struct rev_info *revs,
+static void add_pending_object_with_path(struct rev_info *revs,
                                         struct object *obj,
-                                        const char *name, unsigned mode)
+                                        const char *name, unsigned mode,
+                                        const char *path)
 {
        if (!obj)
                return;
@@ -220,7 +221,14 @@ static void add_pending_object_with_mode(struct rev_info *revs,
                if (st)
                        return;
        }
-       add_object_array_with_mode(obj, name, &revs->pending, mode);
+       add_object_array_with_path(obj, name, &revs->pending, mode, path);
+}
+
+static void add_pending_object_with_mode(struct rev_info *revs,
+                                        struct object *obj,
+                                        const char *name, unsigned mode)
+{
+       add_pending_object_with_path(revs, obj, name, mode, NULL);
 }
 
 void add_pending_object(struct rev_info *revs,
@@ -265,8 +273,12 @@ void add_pending_sha1(struct rev_info *revs, const char *name,
 }
 
 static struct commit *handle_commit(struct rev_info *revs,
-                                   struct object *object, const char *name)
+                                   struct object_array_entry *entry)
 {
+       struct object *object = entry->item;
+       const char *name = entry->name;
+       const char *path = entry->path;
+       unsigned int mode = entry->mode;
        unsigned long flags = object->flags;
 
        /*
@@ -285,6 +297,14 @@ static struct commit *handle_commit(struct rev_info *revs,
                        die("bad object %s", sha1_to_hex(tag->tagged->sha1));
                }
                object->flags |= flags;
+               /*
+                * We'll handle the tagged object by looping or dropping
+                * through to the non-tag handlers below. Do not
+                * propagate data from the tag's pending entry.
+                */
+               name = "";
+               path = NULL;
+               mode = 0;
        }
 
        /*
@@ -316,7 +336,7 @@ static struct commit *handle_commit(struct rev_info *revs,
                        mark_tree_contents_uninteresting(tree);
                        return NULL;
                }
-               add_pending_object(revs, object, "");
+               add_pending_object_with_path(revs, object, name, mode, path);
                return NULL;
        }
 
@@ -328,7 +348,7 @@ static struct commit *handle_commit(struct rev_info *revs,
                        return NULL;
                if (flags & UNINTERESTING)
                        return NULL;
-               add_pending_object(revs, object, "");
+               add_pending_object_with_path(revs, object, name, mode, path);
                return NULL;
        }
        die("%s is unknown object", name);
@@ -2666,7 +2686,7 @@ int prepare_revision_walk(struct rev_info *revs)
        revs->pending.objects = NULL;
        for (i = 0; i < old_pending.nr; i++) {
                struct object_array_entry *e = old_pending.objects + i;
-               struct commit *commit = handle_commit(revs, e->item, e->name);
+               struct commit *commit = handle_commit(revs, e);
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;