diff --git a/builtin/fsck.c b/builtin/fsck.c index 18403a94fa..8d13794b14 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -50,40 +50,20 @@ static int name_objects; #define ERROR_REFS 010 #define ERROR_COMMIT_GRAPH 020 -static const char *describe_object(struct object *obj) +static const char *describe_object(const struct object_id *oid) { - static struct strbuf bufs[] = { - STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT - }; - static int b = 0; - struct strbuf *buf; - char *name = NULL; - - if (name_objects) - name = lookup_decoration(fsck_walk_options.object_names, obj); - - buf = bufs + b; - b = (b + 1) % ARRAY_SIZE(bufs); - strbuf_reset(buf); - strbuf_addstr(buf, oid_to_hex(&obj->oid)); - if (name) - strbuf_addf(buf, " (%s)", name); - - return buf->buf; + return fsck_describe_object(&fsck_walk_options, oid); } -static const char *printable_type(struct object *obj) +static const char *printable_type(const struct object_id *oid, + enum object_type type) { const char *ret; - if (obj->type == OBJ_NONE) { - enum object_type type = oid_object_info(the_repository, - &obj->oid, NULL); - if (type > 0) - object_as_type(the_repository, obj, type, 0); - } + if (type == OBJ_NONE) + type = oid_object_info(the_repository, oid, NULL); - ret = type_name(obj->type); + ret = type_name(type); if (!ret) ret = _("unknown"); @@ -118,26 +98,32 @@ static int objerror(struct object *obj, const char *err) errors_found |= ERROR_OBJECT; /* TRANSLATORS: e.g. error in tree 01bfda: */ fprintf_ln(stderr, _("error in %s %s: %s"), - printable_type(obj), describe_object(obj), err); + printable_type(&obj->oid, obj->type), + describe_object(&obj->oid), err); return -1; } static int fsck_error_func(struct fsck_options *o, - struct object *obj, int type, const char *message) + const struct object_id *oid, + enum object_type object_type, + int msg_type, const char *message) { - switch (type) { + switch (msg_type) { case FSCK_WARN: /* TRANSLATORS: e.g. warning in tree 01bfda: */ fprintf_ln(stderr, _("warning in %s %s: %s"), - printable_type(obj), describe_object(obj), message); + printable_type(oid, object_type), + describe_object(oid), message); return 0; case FSCK_ERROR: /* TRANSLATORS: e.g. error in tree 01bfda: */ fprintf_ln(stderr, _("error in %s %s: %s"), - printable_type(obj), describe_object(obj), message); + printable_type(oid, object_type), + describe_object(oid), message); return 1; default: - BUG("%d (FSCK_IGNORE?) should never trigger this callback", type); + BUG("%d (FSCK_IGNORE?) should never trigger this callback", + msg_type); } } @@ -155,7 +141,8 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt if (!obj) { /* ... these references to parent->fld are safe here */ printf_ln(_("broken link from %7s %s"), - printable_type(parent), describe_object(parent)); + printable_type(&parent->oid, parent->type), + describe_object(&parent->oid)); printf_ln(_("broken link from %7s %s"), (type == OBJ_ANY ? _("unknown") : type_name(type)), _("unknown")); @@ -183,10 +170,10 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt if (parent && !has_object_file(&obj->oid)) { printf_ln(_("broken link from %7s %s\n" " to %7s %s"), - printable_type(parent), - describe_object(parent), - printable_type(obj), - describe_object(obj)); + printable_type(&parent->oid, parent->type), + describe_object(&parent->oid), + printable_type(&obj->oid, obj->type), + describe_object(&obj->oid)); errors_found |= ERROR_REACHABLE; } return 1; @@ -292,8 +279,9 @@ static void check_reachable_object(struct object *obj) return; if (has_object_pack(&obj->oid)) return; /* it is in pack - forget about it */ - printf_ln(_("missing %s %s"), printable_type(obj), - describe_object(obj)); + printf_ln(_("missing %s %s"), + printable_type(&obj->oid, obj->type), + describe_object(&obj->oid)); errors_found |= ERROR_REACHABLE; return; } @@ -318,8 +306,9 @@ static void check_unreachable_object(struct object *obj) * since this is something that is prunable. */ if (show_unreachable) { - printf_ln(_("unreachable %s %s"), printable_type(obj), - describe_object(obj)); + printf_ln(_("unreachable %s %s"), + printable_type(&obj->oid, obj->type), + describe_object(&obj->oid)); return; } @@ -337,12 +326,13 @@ static void check_unreachable_object(struct object *obj) */ if (!(obj->flags & USED)) { if (show_dangling) - printf_ln(_("dangling %s %s"), printable_type(obj), - describe_object(obj)); + printf_ln(_("dangling %s %s"), + printable_type(&obj->oid, obj->type), + describe_object(&obj->oid)); if (write_lost_and_found) { char *filename = git_pathdup("lost-found/%s/%s", obj->type == OBJ_COMMIT ? "commit" : "other", - describe_object(obj)); + describe_object(&obj->oid)); FILE *f; if (safe_create_leading_directories_const(filename)) { @@ -355,7 +345,7 @@ static void check_unreachable_object(struct object *obj) if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1)) die_errno(_("could not write '%s'"), filename); } else - fprintf(f, "%s\n", describe_object(obj)); + fprintf(f, "%s\n", describe_object(&obj->oid)); if (fclose(f)) die_errno(_("could not finish '%s'"), filename); @@ -374,7 +364,7 @@ static void check_unreachable_object(struct object *obj) static void check_object(struct object *obj) { if (verbose) - fprintf_ln(stderr, _("Checking %s"), describe_object(obj)); + fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid)); if (obj->flags & REACHABLE) check_reachable_object(obj); @@ -432,7 +422,8 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) if (verbose) fprintf_ln(stderr, _("Checking %s %s"), - printable_type(obj), describe_object(obj)); + printable_type(&obj->oid, obj->type), + describe_object(&obj->oid)); if (fsck_walk(obj, NULL, &fsck_obj_options)) objerror(obj, _("broken links")); @@ -445,7 +436,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) if (!commit->parents && show_root) printf_ln(_("root %s"), - describe_object(&commit->object)); + describe_object(&commit->object.oid)); } if (obj->type == OBJ_TAG) { @@ -453,10 +444,10 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) if (show_tags && tag->tagged) { printf_ln(_("tagged %s %s (%s) in %s"), - printable_type(tag->tagged), - describe_object(tag->tagged), + printable_type(&tag->tagged->oid, tag->tagged->type), + describe_object(&tag->tagged->oid), tag->tag, - describe_object(&tag->object)); + describe_object(&tag->object.oid)); } } @@ -499,10 +490,10 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, if (!is_null_oid(oid)) { obj = lookup_object(the_repository, oid); if (obj && (obj->flags & HAS_OBJ)) { - if (timestamp && name_objects) - add_decoration(fsck_walk_options.object_names, - obj, - xstrfmt("%s@{%"PRItime"}", refname, timestamp)); + if (timestamp) + fsck_put_object_name(&fsck_walk_options, oid, + "%s@{%"PRItime"}", + refname, timestamp); obj->flags |= USED; mark_object_reachable(obj); } else if (!is_promisor_object(oid)) { @@ -566,9 +557,8 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid, } default_refs++; obj->flags |= USED; - if (name_objects) - add_decoration(fsck_walk_options.object_names, - obj, xstrdup(refname)); + fsck_put_object_name(&fsck_walk_options, + oid, "%s", refname); mark_object_reachable(obj); return 0; @@ -742,9 +732,7 @@ static int fsck_cache_tree(struct cache_tree *it) return 1; } obj->flags |= USED; - if (name_objects) - add_decoration(fsck_walk_options.object_names, - obj, xstrdup(":")); + fsck_put_object_name(&fsck_walk_options, &it->oid, ":"); mark_object_reachable(obj); if (obj->type != OBJ_TREE) err |= objerror(obj, _("non-tree in cache-tree")); @@ -830,8 +818,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } if (name_objects) - fsck_walk_options.object_names = - xcalloc(1, sizeof(struct decoration)); + fsck_enable_object_names(&fsck_walk_options); git_config(fsck_config, NULL); @@ -890,9 +877,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } obj->flags |= USED; - if (name_objects) - add_decoration(fsck_walk_options.object_names, - obj, xstrdup(arg)); + fsck_put_object_name(&fsck_walk_options, &oid, + "%s", arg); mark_object_reachable(obj); continue; } @@ -928,10 +914,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) continue; obj = &blob->object; obj->flags |= USED; - if (name_objects) - add_decoration(fsck_walk_options.object_names, - obj, - xstrfmt(":%s", active_cache[i]->name)); + fsck_put_object_name(&fsck_walk_options, &obj->oid, + ":%s", active_cache[i]->name); mark_object_reachable(obj); } if (active_cache_tree) diff --git a/commit-graph.c b/commit-graph.c index 0aea7b2ae5..a0f868522b 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -858,9 +858,6 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len, die(_("unable to parse commit %s"), oid_to_hex(&(*list)->object.oid)); tree = get_commit_tree_oid(*list); - if (!tree) - die(_("unable to get tree for %s"), - oid_to_hex(&(*list)->object.oid)); hashwrite(f, tree->hash, hash_len); parent = (*list)->parents; diff --git a/commit.c b/commit.c index 52036bc90f..434ec030d6 100644 --- a/commit.c +++ b/commit.c @@ -402,10 +402,22 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b struct commit_graft *graft; const int tree_entry_len = the_hash_algo->hexsz + 5; const int parent_entry_len = the_hash_algo->hexsz + 7; + struct tree *tree; if (item->object.parsed) return 0; - item->object.parsed = 1; + + if (item->parents) { + /* + * Presumably this is leftover from an earlier failed parse; + * clear it out in preparation for us re-parsing (we'll hit the + * same error, but that's good, since it lets our caller know + * the result cannot be trusted. + */ + free_commit_list(item->parents); + item->parents = NULL; + } + tail += size; if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) || bufptr[tree_entry_len] != '\n') @@ -413,7 +425,12 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b if (get_oid_hex(bufptr + 5, &parent) < 0) return error("bad tree pointer in commit %s", oid_to_hex(&item->object.oid)); - set_commit_tree(item, lookup_tree(r, &parent)); + tree = lookup_tree(r, &parent); + if (!tree) + return error("bad tree pointer %s in commit %s", + oid_to_hex(&parent), + oid_to_hex(&item->object.oid)); + set_commit_tree(item, tree); bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */ pptr = &item->parents; @@ -433,8 +450,11 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b if (graft && (graft->nr_parent < 0 || grafts_replace_parents)) continue; new_parent = lookup_commit(r, &parent); - if (new_parent) - pptr = &commit_list_insert(new_parent, pptr)->next; + if (!new_parent) + return error("bad parent %s in commit %s", + oid_to_hex(&parent), + oid_to_hex(&item->object.oid)); + pptr = &commit_list_insert(new_parent, pptr)->next; } if (graft) { int i; @@ -443,7 +463,9 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b new_parent = lookup_commit(r, &graft->parent[i]); if (!new_parent) - continue; + return error("bad graft parent %s in commit %s", + oid_to_hex(&graft->parent[i]), + oid_to_hex(&item->object.oid)); pptr = &commit_list_insert(new_parent, pptr)->next; } } @@ -452,6 +474,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b if (check_graph) load_commit_graph_info(r, item); + item->object.parsed = 1; return 0; } diff --git a/fsck.c b/fsck.c index cdb7d8db03..ac4ba4c8e8 100644 --- a/fsck.c +++ b/fsck.c @@ -43,21 +43,17 @@ static struct oidset gitmodules_done = OIDSET_INIT; FUNC(MISSING_AUTHOR, ERROR) \ FUNC(MISSING_COMMITTER, ERROR) \ FUNC(MISSING_EMAIL, ERROR) \ - FUNC(MISSING_GRAFT, ERROR) \ FUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \ FUNC(MISSING_OBJECT, ERROR) \ - FUNC(MISSING_PARENT, ERROR) \ FUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \ FUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \ FUNC(MISSING_TAG, ERROR) \ FUNC(MISSING_TAG_ENTRY, ERROR) \ - FUNC(MISSING_TAG_OBJECT, ERROR) \ FUNC(MISSING_TREE, ERROR) \ FUNC(MISSING_TREE_OBJECT, ERROR) \ FUNC(MISSING_TYPE, ERROR) \ FUNC(MISSING_TYPE_ENTRY, ERROR) \ FUNC(MULTIPLE_AUTHORS, ERROR) \ - FUNC(TAG_OBJECT_NOT_TAG, ERROR) \ FUNC(TREE_NOT_SORTED, ERROR) \ FUNC(UNKNOWN_TYPE, ERROR) \ FUNC(ZERO_PADDED_DATE, ERROR) \ @@ -281,14 +277,16 @@ static void append_msg_id(struct strbuf *sb, const char *msg_id) strbuf_addstr(sb, ": "); } -static int object_on_skiplist(struct fsck_options *opts, struct object *obj) +static int object_on_skiplist(struct fsck_options *opts, + const struct object_id *oid) { - return opts && obj && oidset_contains(&opts->skiplist, &obj->oid); + return opts && oid && oidset_contains(&opts->skiplist, oid); } -__attribute__((format (printf, 4, 5))) -static int report(struct fsck_options *options, struct object *object, - enum fsck_msg_id id, const char *fmt, ...) +__attribute__((format (printf, 5, 6))) +static int report(struct fsck_options *options, + const struct object_id *oid, enum object_type object_type, + enum fsck_msg_id id, const char *fmt, ...) { va_list ap; struct strbuf sb = STRBUF_INIT; @@ -297,7 +295,7 @@ static int report(struct fsck_options *options, struct object *object, if (msg_type == FSCK_IGNORE) return 0; - if (object_on_skiplist(options, object)) + if (object_on_skiplist(options, oid)) return 0; if (msg_type == FSCK_FATAL) @@ -309,49 +307,71 @@ static int report(struct fsck_options *options, struct object *object, va_start(ap, fmt); strbuf_vaddf(&sb, fmt, ap); - result = options->error_func(options, object, msg_type, sb.buf); + result = options->error_func(options, oid, object_type, + msg_type, sb.buf); strbuf_release(&sb); va_end(ap); return result; } -static char *get_object_name(struct fsck_options *options, struct object *obj) +void fsck_enable_object_names(struct fsck_options *options) { if (!options->object_names) - return NULL; - return lookup_decoration(options->object_names, obj); + options->object_names = kh_init_oid_map(); } -static void put_object_name(struct fsck_options *options, struct object *obj, - const char *fmt, ...) +const char *fsck_get_object_name(struct fsck_options *options, + const struct object_id *oid) +{ + khiter_t pos; + if (!options->object_names) + return NULL; + pos = kh_get_oid_map(options->object_names, *oid); + if (pos >= kh_end(options->object_names)) + return NULL; + return kh_value(options->object_names, pos); +} + +void fsck_put_object_name(struct fsck_options *options, + const struct object_id *oid, + const char *fmt, ...) { va_list ap; struct strbuf buf = STRBUF_INIT; - char *existing; + khiter_t pos; + int hashret; if (!options->object_names) return; - existing = lookup_decoration(options->object_names, obj); - if (existing) + + pos = kh_put_oid_map(options->object_names, *oid, &hashret); + if (!hashret) return; va_start(ap, fmt); strbuf_vaddf(&buf, fmt, ap); - add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL)); + kh_value(options->object_names, pos) = strbuf_detach(&buf, NULL); va_end(ap); } -static const char *describe_object(struct fsck_options *o, struct object *obj) +const char *fsck_describe_object(struct fsck_options *options, + const struct object_id *oid) { - static struct strbuf buf = STRBUF_INIT; - char *name; + static struct strbuf bufs[] = { + STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT + }; + static int b = 0; + struct strbuf *buf; + const char *name = fsck_get_object_name(options, oid); - strbuf_reset(&buf); - strbuf_addstr(&buf, oid_to_hex(&obj->oid)); - if (o->object_names && (name = lookup_decoration(o->object_names, obj))) - strbuf_addf(&buf, " (%s)", name); + buf = bufs + b; + b = (b + 1) % ARRAY_SIZE(bufs); + strbuf_reset(buf); + strbuf_addstr(buf, oid_to_hex(oid)); + if (name) + strbuf_addf(buf, " (%s)", name); - return buf.buf; + return buf->buf; } static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options) @@ -364,7 +384,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op if (parse_tree(tree)) return -1; - name = get_object_name(options, &tree->object); + name = fsck_get_object_name(options, &tree->object.oid); if (init_tree_desc_gently(&desc, tree->buffer, tree->size)) return -1; while (tree_entry_gently(&desc, &entry)) { @@ -377,20 +397,21 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op if (S_ISDIR(entry.mode)) { obj = (struct object *)lookup_tree(the_repository, &entry.oid); if (name && obj) - put_object_name(options, obj, "%s%s/", name, - entry.path); + fsck_put_object_name(options, &entry.oid, "%s%s/", + name, entry.path); result = options->walk(obj, OBJ_TREE, data, options); } else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) { obj = (struct object *)lookup_blob(the_repository, &entry.oid); if (name && obj) - put_object_name(options, obj, "%s%s", name, - entry.path); + fsck_put_object_name(options, &entry.oid, "%s%s", + name, entry.path); result = options->walk(obj, OBJ_BLOB, data, options); } else { result = error("in tree %s: entry %s has bad mode %.6o", - describe_object(options, &tree->object), entry.path, entry.mode); + fsck_describe_object(options, &tree->object.oid), + entry.path, entry.mode); } if (result < 0) return result; @@ -411,10 +432,10 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio if (parse_commit(commit)) return -1; - name = get_object_name(options, &commit->object); + name = fsck_get_object_name(options, &commit->object.oid); if (name) - put_object_name(options, &get_commit_tree(commit)->object, - "%s:", name); + fsck_put_object_name(options, get_commit_tree_oid(commit), + "%s:", name); result = options->walk((struct object *)get_commit_tree(commit), OBJ_TREE, data, options); @@ -442,16 +463,17 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio while (parents) { if (name) { - struct object *obj = &parents->item->object; + struct object_id *oid = &parents->item->object.oid; if (counter++) - put_object_name(options, obj, "%s^%d", - name, counter); + fsck_put_object_name(options, oid, "%s^%d", + name, counter); else if (generation > 0) - put_object_name(options, obj, "%.*s~%d", - name_prefix_len, name, generation + 1); + fsck_put_object_name(options, oid, "%.*s~%d", + name_prefix_len, name, + generation + 1); else - put_object_name(options, obj, "%s^", name); + fsck_put_object_name(options, oid, "%s^", name); } result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options); if (result < 0) @@ -465,12 +487,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options) { - char *name = get_object_name(options, &tag->object); + const char *name = fsck_get_object_name(options, &tag->object.oid); if (parse_tag(tag)) return -1; if (name) - put_object_name(options, tag->tagged, "%s", name); + fsck_put_object_name(options, &tag->tagged->oid, "%s", name); return options->walk(tag->tagged, OBJ_ANY, data, options); } @@ -492,7 +514,8 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options) case OBJ_TAG: return fsck_walk_tag((struct tag *)obj, data, options); default: - error("Unknown object type for %s", describe_object(options, obj)); + error("Unknown object type for %s", + fsck_describe_object(options, &obj->oid)); return -1; } } @@ -543,7 +566,9 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con return c1 < c2 ? 0 : TREE_UNORDERED; } -static int fsck_tree(struct tree *item, struct fsck_options *options) +static int fsck_tree(const struct object_id *oid, + const char *buffer, unsigned long size, + struct fsck_options *options) { int retval = 0; int has_null_sha1 = 0; @@ -560,8 +585,8 @@ static int fsck_tree(struct tree *item, struct fsck_options *options) unsigned o_mode; const char *o_name; - if (init_tree_desc_gently(&desc, item->buffer, item->size)) { - retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree"); + if (init_tree_desc_gently(&desc, buffer, size)) { + retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree"); return retval; } @@ -587,13 +612,14 @@ static int fsck_tree(struct tree *item, struct fsck_options *options) if (!S_ISLNK(mode)) oidset_insert(&gitmodules_found, oid); else - retval += report(options, &item->object, + retval += report(options, + oid, OBJ_TREE, FSCK_MSG_GITMODULES_SYMLINK, ".gitmodules is a symbolic link"); } if (update_tree_entry_gently(&desc)) { - retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree"); break; } @@ -638,30 +664,31 @@ static int fsck_tree(struct tree *item, struct fsck_options *options) } if (has_null_sha1) - retval += report(options, &item->object, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1"); if (has_full_path) - retval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, "contains full pathnames"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_FULL_PATHNAME, "contains full pathnames"); if (has_empty_name) - retval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, "contains empty pathname"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_EMPTY_NAME, "contains empty pathname"); if (has_dot) - retval += report(options, &item->object, FSCK_MSG_HAS_DOT, "contains '.'"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOT, "contains '.'"); if (has_dotdot) - retval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, "contains '..'"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOTDOT, "contains '..'"); if (has_dotgit) - retval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, "contains '.git'"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOTGIT, "contains '.git'"); if (has_zero_pad) - retval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes"); if (has_bad_modes) - retval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, "contains bad file modes"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_FILEMODE, "contains bad file modes"); if (has_dup_entries) - retval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries"); if (not_properly_sorted) - retval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted"); + retval += report(options, oid, OBJ_TREE, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted"); return retval; } static int verify_headers(const void *data, unsigned long size, - struct object *obj, struct fsck_options *options) + const struct object_id *oid, enum object_type type, + struct fsck_options *options) { const char *buffer = (const char *)data; unsigned long i; @@ -669,7 +696,7 @@ static int verify_headers(const void *data, unsigned long size, for (i = 0; i < size; i++) { switch (buffer[i]) { case '\0': - return report(options, obj, + return report(options, oid, type, FSCK_MSG_NUL_IN_HEADER, "unterminated header: NUL at offset %ld", i); case '\n': @@ -687,11 +714,13 @@ static int verify_headers(const void *data, unsigned long size, if (size && buffer[size - 1] == '\n') return 0; - return report(options, obj, + return report(options, oid, type, FSCK_MSG_UNTERMINATED_HEADER, "unterminated header"); } -static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options) +static int fsck_ident(const char **ident, + const struct object_id *oid, enum object_type type, + struct fsck_options *options) { const char *p = *ident; char *end; @@ -701,28 +730,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option (*ident)++; if (*p == '<') - return report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email"); + return report(options, oid, type, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email"); p += strcspn(p, "<>\n"); if (*p == '>') - return report(options, obj, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name"); + return report(options, oid, type, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name"); if (*p != '<') - return report(options, obj, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email"); + return report(options, oid, type, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email"); if (p[-1] != ' ') - return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email"); + return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email"); p++; p += strcspn(p, "<>\n"); if (*p != '>') - return report(options, obj, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email"); + return report(options, oid, type, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email"); p++; if (*p != ' ') - return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date"); + return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date"); p++; if (*p == '0' && p[1] != ' ') - return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date"); + return report(options, oid, type, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date"); if (date_overflows(parse_timestamp(p, &end, 10))) - return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow"); + return report(options, oid, type, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow"); if ((end == p || *end != ' ')) - return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date"); + return report(options, oid, type, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date"); p = end + 1; if ((*p != '+' && *p != '-') || !isdigit(p[1]) || @@ -730,83 +759,60 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option !isdigit(p[3]) || !isdigit(p[4]) || (p[5] != '\n')) - return report(options, obj, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone"); + return report(options, oid, type, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone"); p += 6; return 0; } -static int fsck_commit_buffer(struct commit *commit, const char *buffer, - unsigned long size, struct fsck_options *options) +static int fsck_commit(const struct object_id *oid, + const char *buffer, unsigned long size, + struct fsck_options *options) { - struct object_id tree_oid, oid; - struct commit_graft *graft; - unsigned parent_count, parent_line_count = 0, author_count; + struct object_id tree_oid, parent_oid; + unsigned author_count; int err; const char *buffer_begin = buffer; const char *p; - if (verify_headers(buffer, size, &commit->object, options)) + if (verify_headers(buffer, size, oid, OBJ_COMMIT, options)) return -1; if (!skip_prefix(buffer, "tree ", &buffer)) - return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line"); + return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line"); if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') { - err = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"); + err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"); if (err) return err; } buffer = p + 1; while (skip_prefix(buffer, "parent ", &buffer)) { - if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') { - err = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"); + if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') { + err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"); if (err) return err; } buffer = p + 1; - parent_line_count++; - } - graft = lookup_commit_graft(the_repository, &commit->object.oid); - parent_count = commit_list_count(commit->parents); - if (graft) { - if (graft->nr_parent == -1 && !parent_count) - ; /* shallow commit */ - else if (graft->nr_parent != parent_count) { - err = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, "graft objects missing"); - if (err) - return err; - } - } else { - if (parent_count != parent_line_count) { - err = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, "parent objects missing"); - if (err) - return err; - } } author_count = 0; while (skip_prefix(buffer, "author ", &buffer)) { author_count++; - err = fsck_ident(&buffer, &commit->object, options); + err = fsck_ident(&buffer, oid, OBJ_COMMIT, options); if (err) return err; } if (author_count < 1) - err = report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line"); + err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line"); else if (author_count > 1) - err = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines"); + err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines"); if (err) return err; if (!skip_prefix(buffer, "committer ", &buffer)) - return report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line"); - err = fsck_ident(&buffer, &commit->object, options); + return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line"); + err = fsck_ident(&buffer, oid, OBJ_COMMIT, options); if (err) return err; - if (!get_commit_tree(commit)) { - err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", oid_to_hex(&tree_oid)); - if (err) - return err; - } if (memchr(buffer_begin, '\0', size)) { - err = report(options, &commit->object, FSCK_MSG_NUL_IN_COMMIT, + err = report(options, oid, OBJ_COMMIT, FSCK_MSG_NUL_IN_COMMIT, "NUL byte in the commit object body"); if (err) return err; @@ -814,91 +820,60 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, return 0; } -static int fsck_commit(struct commit *commit, const char *data, - unsigned long size, struct fsck_options *options) +static int fsck_tag(const struct object_id *oid, const char *buffer, + unsigned long size, struct fsck_options *options) { - const char *buffer = data ? data : get_commit_buffer(commit, &size); - int ret = fsck_commit_buffer(commit, buffer, size, options); - if (!data) - unuse_commit_buffer(commit, buffer); - return ret; -} - -static int fsck_tag_buffer(struct tag *tag, const char *data, - unsigned long size, struct fsck_options *options) -{ - struct object_id oid; + struct object_id tagged_oid; int ret = 0; - const char *buffer; - char *to_free = NULL, *eol; + char *eol; struct strbuf sb = STRBUF_INIT; const char *p; - if (data) - buffer = data; - else { - enum object_type type; - - buffer = to_free = - read_object_file(&tag->object.oid, &type, &size); - if (!buffer) - return report(options, &tag->object, - FSCK_MSG_MISSING_TAG_OBJECT, - "cannot read tag object"); - - if (type != OBJ_TAG) { - ret = report(options, &tag->object, - FSCK_MSG_TAG_OBJECT_NOT_TAG, - "expected tag got %s", - type_name(type)); - goto done; - } - } - - ret = verify_headers(buffer, size, &tag->object, options); + ret = verify_headers(buffer, size, oid, OBJ_TAG, options); if (ret) goto done; if (!skip_prefix(buffer, "object ", &buffer)) { - ret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line"); + ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line"); goto done; } - if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') { - ret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1"); + if (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\n') { + ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1"); if (ret) goto done; } buffer = p + 1; if (!skip_prefix(buffer, "type ", &buffer)) { - ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line"); + ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line"); goto done; } eol = strchr(buffer, '\n'); if (!eol) { - ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line"); + ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line"); goto done; } if (type_from_string_gently(buffer, eol - buffer, 1) < 0) - ret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, "invalid 'type' value"); + ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, "invalid 'type' value"); if (ret) goto done; buffer = eol + 1; if (!skip_prefix(buffer, "tag ", &buffer)) { - ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line"); + ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line"); goto done; } eol = strchr(buffer, '\n'); if (!eol) { - ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line"); + ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line"); goto done; } strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer); if (check_refname_format(sb.buf, 0)) { - ret = report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME, - "invalid 'tag' name: %.*s", - (int)(eol - buffer), buffer); + ret = report(options, oid, OBJ_TAG, + FSCK_MSG_BAD_TAG_NAME, + "invalid 'tag' name: %.*s", + (int)(eol - buffer), buffer); if (ret) goto done; } @@ -906,32 +881,20 @@ static int fsck_tag_buffer(struct tag *tag, const char *data, if (!skip_prefix(buffer, "tagger ", &buffer)) { /* early tags do not contain 'tagger' lines; warn only */ - ret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line"); + ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line"); if (ret) goto done; } else - ret = fsck_ident(&buffer, &tag->object, options); + ret = fsck_ident(&buffer, oid, OBJ_TAG, options); done: strbuf_release(&sb); - free(to_free); return ret; } -static int fsck_tag(struct tag *tag, const char *data, - unsigned long size, struct fsck_options *options) -{ - struct object *tagged = tag->tagged; - - if (!tagged) - return report(options, &tag->object, FSCK_MSG_BAD_TAG_OBJECT, "could not load tagged object"); - - return fsck_tag_buffer(tag, data, size, options); -} - struct fsck_gitmodules_data { - struct object *obj; + const struct object_id *oid; struct fsck_options *options; int ret; }; @@ -949,19 +912,22 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata) name = xmemdupz(subsection, subsection_len); if (check_submodule_name(name) < 0) - data->ret |= report(data->options, data->obj, + data->ret |= report(data->options, + data->oid, OBJ_BLOB, FSCK_MSG_GITMODULES_NAME, "disallowed submodule name: %s", name); if (!strcmp(key, "url") && value && looks_like_command_line_option(value)) - data->ret |= report(data->options, data->obj, + data->ret |= report(data->options, + data->oid, OBJ_BLOB, FSCK_MSG_GITMODULES_URL, "disallowed submodule url: %s", value); if (!strcmp(key, "path") && value && looks_like_command_line_option(value)) - data->ret |= report(data->options, data->obj, + data->ret |= report(data->options, + data->oid, OBJ_BLOB, FSCK_MSG_GITMODULES_PATH, "disallowed submodule path: %s", value); @@ -970,17 +936,17 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata) return 0; } -static int fsck_blob(struct blob *blob, const char *buf, +static int fsck_blob(const struct object_id *oid, const char *buf, unsigned long size, struct fsck_options *options) { struct fsck_gitmodules_data data; struct config_options config_opts = { 0 }; - if (!oidset_contains(&gitmodules_found, &blob->object.oid)) + if (!oidset_contains(&gitmodules_found, oid)) return 0; - oidset_insert(&gitmodules_done, &blob->object.oid); + oidset_insert(&gitmodules_done, oid); - if (object_on_skiplist(options, &blob->object)) + if (object_on_skiplist(options, oid)) return 0; if (!buf) { @@ -989,18 +955,18 @@ static int fsck_blob(struct blob *blob, const char *buf, * blob too gigantic to load into memory. Let's just consider * that an error. */ - return report(options, &blob->object, + return report(options, oid, OBJ_BLOB, FSCK_MSG_GITMODULES_LARGE, ".gitmodules too large to parse"); } - data.obj = &blob->object; + data.oid = oid; data.options = options; data.ret = 0; config_opts.error_action = CONFIG_ERROR_SILENT; if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB, ".gitmodules", buf, size, &data, &config_opts)) - data.ret |= report(options, &blob->object, + data.ret |= report(options, oid, OBJ_BLOB, FSCK_MSG_GITMODULES_PARSE, "could not parse gitmodules blob"); @@ -1011,31 +977,33 @@ int fsck_object(struct object *obj, void *data, unsigned long size, struct fsck_options *options) { if (!obj) - return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck"); + return report(options, NULL, OBJ_NONE, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck"); if (obj->type == OBJ_BLOB) - return fsck_blob((struct blob *)obj, data, size, options); + return fsck_blob(&obj->oid, data, size, options); if (obj->type == OBJ_TREE) - return fsck_tree((struct tree *) obj, options); + return fsck_tree(&obj->oid, data, size, options); if (obj->type == OBJ_COMMIT) - return fsck_commit((struct commit *) obj, (const char *) data, - size, options); + return fsck_commit(&obj->oid, data, size, options); if (obj->type == OBJ_TAG) - return fsck_tag((struct tag *) obj, (const char *) data, - size, options); + return fsck_tag(&obj->oid, data, size, options); - return report(options, obj, FSCK_MSG_UNKNOWN_TYPE, "unknown type '%d' (internal fsck error)", - obj->type); + return report(options, &obj->oid, obj->type, + FSCK_MSG_UNKNOWN_TYPE, + "unknown type '%d' (internal fsck error)", + obj->type); } int fsck_error_function(struct fsck_options *o, - struct object *obj, int msg_type, const char *message) + const struct object_id *oid, + enum object_type object_type, + int msg_type, const char *message) { if (msg_type == FSCK_WARN) { - warning("object %s: %s", describe_object(o, obj), message); + warning("object %s: %s", fsck_describe_object(o, oid), message); return 0; } - error("object %s: %s", describe_object(o, obj), message); + error("object %s: %s", fsck_describe_object(o, oid), message); return 1; } @@ -1047,7 +1015,6 @@ int fsck_finish(struct fsck_options *options) oidset_iter_init(&gitmodules_found, &iter); while ((oid = oidset_iter_next(&iter))) { - struct blob *blob; enum object_type type; unsigned long size; char *buf; @@ -1055,29 +1022,22 @@ int fsck_finish(struct fsck_options *options) if (oidset_contains(&gitmodules_done, oid)) continue; - blob = lookup_blob(the_repository, oid); - if (!blob) { - struct object *obj = lookup_unknown_object(oid); - ret |= report(options, obj, - FSCK_MSG_GITMODULES_BLOB, - "non-blob found at .gitmodules"); - continue; - } - buf = read_object_file(oid, &type, &size); if (!buf) { - if (is_promisor_object(&blob->object.oid)) + if (is_promisor_object(oid)) continue; - ret |= report(options, &blob->object, + ret |= report(options, + oid, OBJ_BLOB, FSCK_MSG_GITMODULES_MISSING, "unable to read .gitmodules blob"); continue; } if (type == OBJ_BLOB) - ret |= fsck_blob(blob, buf, size, options); + ret |= fsck_blob(oid, buf, size, options); else - ret |= report(options, &blob->object, + ret |= report(options, + oid, type, FSCK_MSG_GITMODULES_BLOB, "non-blob found at .gitmodules"); free(buf); diff --git a/fsck.h b/fsck.h index b95595ae5f..69cf715e79 100644 --- a/fsck.h +++ b/fsck.h @@ -27,10 +27,12 @@ typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct f /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */ typedef int (*fsck_error)(struct fsck_options *o, - struct object *obj, int type, const char *message); + const struct object_id *oid, enum object_type object_type, + int msg_type, const char *message); int fsck_error_function(struct fsck_options *o, - struct object *obj, int type, const char *message); + const struct object_id *oid, enum object_type object_type, + int msg_type, const char *message); struct fsck_options { fsck_walk_func walk; @@ -38,7 +40,7 @@ struct fsck_options { unsigned strict:1; int *msg_type; struct oidset skiplist; - struct decoration *object_names; + kh_oid_map_t *object_names; }; #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL, OIDSET_INIT } @@ -52,7 +54,11 @@ struct fsck_options { * 0 everything OK */ int fsck_walk(struct object *obj, void *data, struct fsck_options *options); -/* If NULL is passed for data, we assume the object is local and read it. */ + +/* + * Blob objects my pass a NULL data pointer, which indicates they are too large + * to fit in memory. All other types must pass a real buffer. + */ int fsck_object(struct object *obj, void *data, unsigned long size, struct fsck_options *options); @@ -63,4 +69,29 @@ int fsck_object(struct object *obj, void *data, unsigned long size, */ int fsck_finish(struct fsck_options *options); +/* + * Subsystem for storing human-readable names for each object. + * + * If fsck_enable_object_names() has not been called, all other functions are + * noops. + * + * Use fsck_put_object_name() to seed initial names (e.g. from refnames); the + * fsck code will extend that while walking trees, etc. + * + * Use fsck_get_object_name() to get a single name (or NULL if none). Or the + * more convenient describe_object(), which always produces an output string + * with the oid combined with the name (if any). Note that the return value + * points to a rotating array of static buffers, and may be invalidated by a + * subsequent call. + */ +void fsck_enable_object_names(struct fsck_options *options); +const char *fsck_get_object_name(struct fsck_options *options, + const struct object_id *oid); +__attribute__((format (printf,3,4))) +void fsck_put_object_name(struct fsck_options *options, + const struct object_id *oid, + const char *fmt, ...); +const char *fsck_describe_object(struct fsck_options *options, + const struct object_id *oid); + #endif diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 50d28e6fdb..7c7ff7e961 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -616,7 +616,7 @@ test_expect_success 'fsck --name-objects' ' remove_object $(git rev-parse julius:caesar.t) && test_must_fail git fsck --name-objects >out && tree=$(git rev-parse --verify julius:) && - test_i18ngrep -E "$tree \((refs/heads/master|HEAD)@\{[0-9]*\}:" out + test_i18ngrep "$tree (refs/tags/julius:" out ) ' diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index d42b3efe39..127b404856 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -660,7 +660,7 @@ test_expect_success 'corrupt commit-graph write (missing tree)' ' git commit-tree -p "$broken" -m "good" "$tree" >good && test_must_fail git commit-graph write --stdin-commits \ test_err && - test_i18ngrep "unable to get tree for" test_err + test_i18ngrep "unable to parse commit" test_err ) ' diff --git a/t/t6102-rev-list-unexpected-objects.sh b/t/t6102-rev-list-unexpected-objects.sh index 28611c978e..52cde097dd 100755 --- a/t/t6102-rev-list-unexpected-objects.sh +++ b/t/t6102-rev-list-unexpected-objects.sh @@ -52,7 +52,7 @@ test_expect_success 'traverse unexpected non-commit parent (lone)' ' ' test_expect_success 'traverse unexpected non-commit parent (seen)' ' - test_must_fail git rev-list --objects $commit $broken_commit \ + test_must_fail git rev-list --objects $blob $broken_commit \ >output 2>&1 && test_i18ngrep "not a commit" output ' diff --git a/tag.c b/tag.c index bfa0e31435..71b544467e 100644 --- a/tag.c +++ b/tag.c @@ -141,7 +141,16 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u if (item->object.parsed) return 0; - item->object.parsed = 1; + + if (item->tag) { + /* + * Presumably left over from a previous failed parse; + * clear it out in preparation for re-parsing (we'll probably + * hit the same error, which lets us tell our current caller + * about the problem). + */ + FREE_AND_NULL(item->tag); + } if (size < the_hash_algo->hexsz + 24) return -1; @@ -167,10 +176,15 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u } else if (!strcmp(type, tag_type)) { item->tagged = (struct object *)lookup_tag(r, &oid); } else { - error("Unknown type %s", type); - item->tagged = NULL; + return error("unknown tag type '%s' in %s", + type, oid_to_hex(&item->object.oid)); } + if (!item->tagged) + return error("bad tag pointer to %s in %s", + oid_to_hex(&oid), + oid_to_hex(&item->object.oid)); + if (bufptr + 4 < tail && starts_with(bufptr, "tag ")) ; /* good */ else @@ -187,6 +201,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u else item->date = 0; + item->object.parsed = 1; return 0; }