From 90cf590f53f2939a47ca7b397e270e8228699829 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 17 Jul 2016 13:00:02 +0200 Subject: [PATCH] fsck: optionally show more helpful info for broken links When reporting broken links between commits/trees/blobs, it would be quite helpful at times if the user would be told how the object is supposed to be reachable. With the new --name-objects option, git-fsck will try to do exactly that: name the objects in a way that shows how they are reachable. For example, when some reflog got corrupted and a blob is missing that should not be, the user might want to remove the corresponding reflog entry. This option helps them find that entry: `git fsck` will now report something like this: broken link from tree b5eb6ff... (refs/stash@{}~37:) to blob ec5cf80... Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/git-fsck.txt | 9 +++++++- builtin/fsck.c | 42 ++++++++++++++++++++++++++++++++++---- fsck.c | 21 +++++++++++++++---- t/t1450-fsck.sh | 22 ++++++++++++++++++++ 4 files changed, 85 insertions(+), 9 deletions(-) diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt index 7fc68eb319..b9f060e3b2 100644 --- a/Documentation/git-fsck.txt +++ b/Documentation/git-fsck.txt @@ -11,7 +11,8 @@ SYNOPSIS [verse] 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs] [--[no-]full] [--strict] [--verbose] [--lost-found] - [--[no-]dangling] [--[no-]progress] [--connectivity-only] [*] + [--[no-]dangling] [--[no-]progress] [--connectivity-only] + [--[no-]name-objects] [*] DESCRIPTION ----------- @@ -82,6 +83,12 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs a blob, the contents are written into the file, rather than its object name. +--name-objects:: + When displaying names of reachable objects, in addition to the + SHA-1 also display a name that describes *how* they are reachable, + compatible with linkgit:git-rev-parse[1], e.g. + `HEAD@{1234567890}~25^2:src/`. + --[no-]progress:: Progress status is reported on the standard error stream by default when it is attached to a terminal, unless diff --git a/builtin/fsck.c b/builtin/fsck.c index 6c9d598853..c6d17e63fd 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -13,6 +13,7 @@ #include "dir.h" #include "progress.h" #include "streaming.h" +#include "decorate.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -35,6 +36,7 @@ static int write_lost_and_found; static int verbose; static int show_progress = -1; static int show_dangling = 1; +static int name_objects; #define ERROR_OBJECT 01 #define ERROR_REACHABLE 02 #define ERROR_PACK 04 @@ -42,7 +44,16 @@ static int show_dangling = 1; static const char *describe_object(struct object *obj) { - return oid_to_hex(&obj->oid); + static struct strbuf buf = STRBUF_INIT; + char *name = name_objects ? + lookup_decoration(fsck_walk_options.object_names, obj) : NULL; + + strbuf_reset(&buf); + strbuf_addstr(&buf, oid_to_hex(&obj->oid)); + if (name) + strbuf_addf(&buf, " (%s)", name); + + return buf.buf; } static int fsck_config(const char *var, const char *value, void *cb) @@ -378,13 +389,18 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type, static int default_refs; -static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1) +static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1, + unsigned long timestamp) { struct object *obj; if (!is_null_sha1(sha1)) { obj = lookup_object(sha1); if (obj) { + if (timestamp && name_objects) + add_decoration(fsck_walk_options.object_names, + obj, + xstrfmt("%s@{%ld}", refname, timestamp)); obj->used = 1; mark_object_reachable(obj); } else { @@ -404,8 +420,8 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, fprintf(stderr, "Checking reflog %s->%s\n", sha1_to_hex(osha1), sha1_to_hex(nsha1)); - fsck_handle_reflog_sha1(refname, osha1); - fsck_handle_reflog_sha1(refname, nsha1); + fsck_handle_reflog_sha1(refname, osha1, 0); + fsck_handle_reflog_sha1(refname, nsha1, timestamp); return 0; } @@ -434,6 +450,9 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid, } default_refs++; obj->used = 1; + if (name_objects) + add_decoration(fsck_walk_options.object_names, + obj, xstrdup(refname)); mark_object_reachable(obj); return 0; @@ -549,6 +568,9 @@ static int fsck_cache_tree(struct cache_tree *it) return 1; } obj->used = 1; + if (name_objects) + add_decoration(fsck_walk_options.object_names, + obj, xstrdup(":")); mark_object_reachable(obj); if (obj->type != OBJ_TREE) err |= objerror(obj, "non-tree in cache-tree"); @@ -577,6 +599,7 @@ static struct option fsck_opts[] = { OPT_BOOL(0, "lost-found", &write_lost_and_found, N_("write dangling objects in .git/lost-found")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), + OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")), OPT_END(), }; @@ -606,6 +629,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) include_reflogs = 0; } + if (name_objects) + fsck_walk_options.object_names = + xcalloc(1, sizeof(struct decoration)); + git_config(fsck_config, NULL); fsck_head_link(); @@ -661,6 +688,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) continue; obj->used = 1; + if (name_objects) + add_decoration(fsck_walk_options.object_names, + obj, xstrdup(arg)); mark_object_reachable(obj); heads++; continue; @@ -693,6 +723,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) continue; obj = &blob->object; obj->used = 1; + if (name_objects) + add_decoration(fsck_walk_options.object_names, + obj, + xstrfmt(":%s", active_cache[i]->name)); mark_object_reachable(obj); } if (active_cache_tree) diff --git a/fsck.c b/fsck.c index 828c5c6484..c9cf3de8d3 100644 --- a/fsck.c +++ b/fsck.c @@ -323,6 +323,19 @@ static void put_object_name(struct fsck_options *options, struct object *obj, va_end(ap); } +static const char *describe_object(struct fsck_options *o, struct object *obj) +{ + static struct strbuf buf = STRBUF_INIT; + char *name; + + 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); + + return buf.buf; +} + static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options) { struct tree_desc desc; @@ -358,7 +371,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op } else { result = error("in tree %s: entry %s has bad mode %.6o", - oid_to_hex(&tree->object.oid), entry.path, entry.mode); + describe_object(options, &tree->object), entry.path, entry.mode); } if (result < 0) return result; @@ -454,7 +467,7 @@ 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", oid_to_hex(&obj->oid)); + error("Unknown object type for %s", describe_object(options, obj)); return -1; } } @@ -901,9 +914,9 @@ int fsck_error_function(struct fsck_options *o, struct object *obj, int msg_type, const char *message) { if (msg_type == FSCK_WARN) { - warning("object %s: %s", oid_to_hex(&obj->oid), message); + warning("object %s: %s", describe_object(o, obj), message); return 0; } - error("object %s: %s", oid_to_hex(&obj->oid), message); + error("object %s: %s", describe_object(o, obj), message); return 1; } diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 7ee8ea004f..8f52da2771 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -523,4 +523,26 @@ test_expect_success 'fsck --connectivity-only' ' ) ' +remove_loose_object () { + sha1="$(git rev-parse "$1")" && + remainder=${sha1#??} && + firsttwo=${sha1%$remainder} && + rm .git/objects/$firsttwo/$remainder +} + +test_expect_success 'fsck --name-objects' ' + rm -rf name-objects && + git init name-objects && + ( + cd name-objects && + test_commit julius caesar.t && + test_commit augustus && + test_commit caesar && + remove_loose_object $(git rev-parse julius:caesar.t) && + test_must_fail git fsck --name-objects >out && + tree=$(git rev-parse --verify julius:) && + grep "$tree (\(refs/heads/master\|HEAD\)@{[0-9]*}:" out + ) +' + test_done