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>
This commit is contained in:
Jeff King 2014-10-15 18:43:19 -04:00 committed by Junio C Hamano
parent 9e0c3c4fcd
commit 207394908e
2 changed files with 32 additions and 9 deletions

View 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)",

View 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;