git/list-objects.c
Junio C Hamano 6e7d0efa90 list-objects.c: don't add an unparsed NULL as a pending tree
"git rev-list --first-parent --boundary $commit^..$commit" segfaults on a
merge commit since 8d2dfc4 (process_{tree,blob}: show objects without
buffering, 2009-04-10), as it tried to dereference a commit that was
discarded as UNINTERESTING without being parsed (hence lacking "tree").

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-03-14 12:33:58 -07:00

189 lines
4.8 KiB
C

#include "cache.h"
#include "tag.h"
#include "commit.h"
#include "tree.h"
#include "blob.h"
#include "diff.h"
#include "tree-walk.h"
#include "revision.h"
#include "list-objects.h"
static void process_blob(struct rev_info *revs,
struct blob *blob,
show_object_fn show,
struct name_path *path,
const char *name)
{
struct object *obj = &blob->object;
if (!revs->blob_objects)
return;
if (!obj)
die("bad blob object");
if (obj->flags & (UNINTERESTING | SEEN))
return;
obj->flags |= SEEN;
show(obj, path_name(path, name));
}
/*
* Processing a gitlink entry currently does nothing, since
* we do not recurse into the subproject.
*
* We *could* eventually add a flag that actually does that,
* which would involve:
* - is the subproject actually checked out?
* - if so, see if the subproject has already been added
* to the alternates list, and add it if not.
* - process the commit (or tag) the gitlink points to
* recursively.
*
* However, it's unclear whether there is really ever any
* reason to see superprojects and subprojects as such a
* "unified" object pool (potentially resulting in a totally
* humongous pack - avoiding which was the whole point of
* having gitlinks in the first place!).
*
* So for now, there is just a note that we *could* follow
* the link, and how to do it. Whether it necessarily makes
* any sense what-so-ever to ever do that is another issue.
*/
static void process_gitlink(struct rev_info *revs,
const unsigned char *sha1,
show_object_fn show,
struct name_path *path,
const char *name)
{
/* Nothing to do */
}
static void process_tree(struct rev_info *revs,
struct tree *tree,
show_object_fn show,
struct name_path *path,
const char *name)
{
struct object *obj = &tree->object;
struct tree_desc desc;
struct name_entry entry;
struct name_path me;
if (!revs->tree_objects)
return;
if (!obj)
die("bad tree object");
if (obj->flags & (UNINTERESTING | SEEN))
return;
if (parse_tree(tree) < 0)
die("bad tree object %s", sha1_to_hex(obj->sha1));
obj->flags |= SEEN;
show(obj, path_name(path, name));
me.up = path;
me.elem = name;
me.elem_len = strlen(name);
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
process_tree(revs,
lookup_tree(entry.sha1),
show, &me, entry.path);
else if (S_ISGITLINK(entry.mode))
process_gitlink(revs, entry.sha1,
show, &me, entry.path);
else
process_blob(revs,
lookup_blob(entry.sha1),
show, &me, entry.path);
}
free(tree->buffer);
tree->buffer = NULL;
}
static void mark_edge_parents_uninteresting(struct commit *commit,
struct rev_info *revs,
show_edge_fn show_edge)
{
struct commit_list *parents;
for (parents = commit->parents; parents; parents = parents->next) {
struct commit *parent = parents->item;
if (!(parent->object.flags & UNINTERESTING))
continue;
mark_tree_uninteresting(parent->tree);
if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
parent->object.flags |= SHOWN;
show_edge(parent);
}
}
}
void mark_edges_uninteresting(struct commit_list *list,
struct rev_info *revs,
show_edge_fn show_edge)
{
for ( ; list; list = list->next) {
struct commit *commit = list->item;
if (commit->object.flags & UNINTERESTING) {
mark_tree_uninteresting(commit->tree);
continue;
}
mark_edge_parents_uninteresting(commit, revs, show_edge);
}
}
static void add_pending_tree(struct rev_info *revs, struct tree *tree)
{
add_pending_object(revs, &tree->object, "");
}
void traverse_commit_list(struct rev_info *revs,
void (*show_commit)(struct commit *),
void (*show_object)(struct object *, const char *))
{
int i;
struct commit *commit;
while ((commit = get_revision(revs)) != NULL) {
/*
* an uninteresting boundary commit may not have its tree
* parsed yet, but we are not going to show them anyway
*/
if (commit->tree)
add_pending_tree(revs, commit->tree);
show_commit(commit);
}
for (i = 0; i < revs->pending.nr; i++) {
struct object_array_entry *pending = revs->pending.objects + i;
struct object *obj = pending->item;
const char *name = pending->name;
if (obj->flags & (UNINTERESTING | SEEN))
continue;
if (obj->type == OBJ_TAG) {
obj->flags |= SEEN;
show_object(obj, name);
continue;
}
if (obj->type == OBJ_TREE) {
process_tree(revs, (struct tree *)obj, show_object,
NULL, name);
continue;
}
if (obj->type == OBJ_BLOB) {
process_blob(revs, (struct blob *)obj, show_object,
NULL, name);
continue;
}
die("unknown pending object %s (%s)",
sha1_to_hex(obj->sha1), name);
}
if (revs->pending.nr) {
free(revs->pending.objects);
revs->pending.nr = 0;
revs->pending.alloc = 0;
revs->pending.objects = NULL;
}
}