{"thread":{"id":"42845","subject":"[PATCH 0/3] Teach `git fsck` a new option: `--name-objects`","startedAt":"2016-07-14T15:30:26Z","lastAt":"2016-07-18T18:43:01Z","messageCount":16,"participants":["Johannes Schindelin","Junio C Hamano","Eric Sunshine"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"291418","messageId":"cover.1468510191.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":null,"subject":"[PATCH 0/3] Teach `git fsck` a new option: `--name-objects`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-14T15:30:10Z","receivedAt":"2016-07-14T15:30:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When using experimental features (such as worktrees), it is quite\npossible to end up with a repository that is a little bit corrupted. In\nthis developer's case, the auto gc run during interactive rebases in\nworktrees completely messed up the reflogs.\n\nThe symptoms are broken links between commits/trees/blobs.\n\nTrying to work around such problems can be a real challenge: while\nseveral tools will report when objects are missing, all of them simply\nstate the SHA-1. This is not useful when the user has to kiss the\noffending reflog good-bye, but does not even know which one.\n\nThis patch series introduces a new option to `git fsck`: --name-objects.\nWith this option, the fsck command will report not only the SHA-1 of\nmissing objects, but also a name by which this object is supposed to be\nreachable.\n\nExample output:\n\n\t...\n\tbroken link from    tree b5eb6ff...  (refs/stash@{<date>}~37:)\n\t\t      to    blob ec5cf80...\n\nOriginally, I intended to teach name-rev a new mode where it would also\nname objects other than commits and tags, but since the objects in\nquestion were lost to a garbage collection, and therefore there would\nnot have been any objects to call names to begin with, I had to abandon\nsaid quest.\n\n\nJohannes Schindelin (3):\n  fsck: refactor how to describe objects\n  fsck_walk(): optionally name objects on the go\n  fsck: optionally show more helpful info for broken links\n\n Documentation/git-fsck.txt |  9 +++++-\n builtin/fsck.c             | 77 ++++++++++++++++++++++++++++++++++++----------\n fsck.c                     | 72 +++++++++++++++++++++++++++++++++++++++++++\n fsck.h                     |  1 +\n t/t1450-fsck.sh            | 22 +++++++++++++\n 5 files changed, 163 insertions(+), 18 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/fsck-name-objects-v1\n-- \n2.9.0.278.g1caae67\n\nbase-commit: 79ed43c28f626a4e805f350a77c54968b59be6e9\n"},{"id":"291419","messageId":"75cd5de6b5a027a32d6ed6b922aa601226d464e5.1468510191.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":"cover.1468510191.git.johannes.schindelin@gmx.de","subject":"[PATCH 1/3] fsck: refactor how to describe objects","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-14T15:30:19Z","receivedAt":"2016-07-14T15:30:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In many places, we refer to objects via their SHA-1s. Let's abstract\nthat into a function.\n\nFor the moment, it does nothing else than what we did previously: print\nout the 40-digit hex string. But that will change over the course of the\nnext patches.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c | 37 +++++++++++++++++++++++--------------\n 1 file changed, 23 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 3f27456..87df191 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -40,6 +40,11 @@ static int show_dangling = 1;\n #define ERROR_PACK 04\n #define ERROR_REFS 010\n \n+static const char *describe_object(struct object *obj)\n+{\n+\treturn oid_to_hex(&obj->oid);\n+}\n+\n static int fsck_config(const char *var, const char *value, void *cb)\n {\n \tif (strcmp(var, \"fsck.skiplist\") == 0) {\n@@ -67,7 +72,7 @@ static void objreport(struct object *obj, const char *msg_type,\n \t\t\tconst char *err)\n {\n \tfprintf(stderr, \"%s in %s %s: %s\\n\",\n-\t\tmsg_type, typename(obj->type), oid_to_hex(&obj->oid), err);\n+\t\tmsg_type, typename(obj->type), describe_object(obj), err);\n }\n \n static int objerror(struct object *obj, const char *err)\n@@ -97,7 +102,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt\n \tif (!obj) {\n \t\t/* ... these references to parent->fld are safe here */\n \t\tprintf(\"broken link from %7s %s\\n\",\n-\t\t\t   typename(parent->type), oid_to_hex(&parent->oid));\n+\t\t\t   typename(parent->type), describe_object(parent));\n \t\tprintf(\"broken link from %7s %s\\n\",\n \t\t\t   (type == OBJ_ANY ? \"unknown\" : typename(type)), \"unknown\");\n \t\terrors_found |= ERROR_REACHABLE;\n@@ -114,9 +119,9 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (parent && !has_object_file(&obj->oid)) {\n \t\t\tprintf(\"broken link from %7s %s\\n\",\n-\t\t\t\t typename(parent->type), oid_to_hex(&parent->oid));\n+\t\t\t\t typename(parent->type), describe_object(parent));\n \t\t\tprintf(\"              to %7s %s\\n\",\n-\t\t\t\t typename(obj->type), oid_to_hex(&obj->oid));\n+\t\t\t\t typename(obj->type), describe_object(obj));\n \t\t\terrors_found |= ERROR_REACHABLE;\n \t\t}\n \t\treturn 1;\n@@ -190,7 +195,8 @@ static void check_reachable_object(struct object *obj)\n \t\t\treturn; /* it is in pack - forget about it */\n \t\tif (connectivity_only && has_object_file(&obj->oid))\n \t\t\treturn;\n-\t\tprintf(\"missing %s %s\\n\", typename(obj->type), oid_to_hex(&obj->oid));\n+\t\tprintf(\"missing %s %s\\n\", typename(obj->type),\n+\t\t\tdescribe_object(obj));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n \t}\n@@ -215,7 +221,8 @@ static void check_unreachable_object(struct object *obj)\n \t * since this is something that is prunable.\n \t */\n \tif (show_unreachable) {\n-\t\tprintf(\"unreachable %s %s\\n\", typename(obj->type), oid_to_hex(&obj->oid));\n+\t\tprintf(\"unreachable %s %s\\n\", typename(obj->type),\n+\t\t\tdescribe_object(obj));\n \t\treturn;\n \t}\n \n@@ -234,11 +241,11 @@ static void check_unreachable_object(struct object *obj)\n \tif (!obj->used) {\n \t\tif (show_dangling)\n \t\t\tprintf(\"dangling %s %s\\n\", typename(obj->type),\n-\t\t\t       oid_to_hex(&obj->oid));\n+\t\t\t       describe_object(obj));\n \t\tif (write_lost_and_found) {\n \t\t\tchar *filename = git_pathdup(\"lost-found/%s/%s\",\n \t\t\t\tobj->type == OBJ_COMMIT ? \"commit\" : \"other\",\n-\t\t\t\toid_to_hex(&obj->oid));\n+\t\t\t\tdescribe_object(obj));\n \t\t\tFILE *f;\n \n \t\t\tif (safe_create_leading_directories_const(filename)) {\n@@ -252,7 +259,7 @@ static void check_unreachable_object(struct object *obj)\n \t\t\t\tif (stream_blob_to_fd(fileno(f), obj->oid.hash, NULL, 1))\n \t\t\t\t\tdie_errno(\"Could not write '%s'\", filename);\n \t\t\t} else\n-\t\t\t\tfprintf(f, \"%s\\n\", oid_to_hex(&obj->oid));\n+\t\t\t\tfprintf(f, \"%s\\n\", describe_object(obj));\n \t\t\tif (fclose(f))\n \t\t\t\tdie_errno(\"Could not finish '%s'\",\n \t\t\t\t\t  filename);\n@@ -271,7 +278,7 @@ static void check_unreachable_object(struct object *obj)\n static void check_object(struct object *obj)\n {\n \tif (verbose)\n-\t\tfprintf(stderr, \"Checking %s\\n\", oid_to_hex(&obj->oid));\n+\t\tfprintf(stderr, \"Checking %s\\n\", describe_object(obj));\n \n \tif (obj->flags & REACHABLE)\n \t\tcheck_reachable_object(obj);\n@@ -307,7 +314,7 @@ static int fsck_obj(struct object *obj)\n \n \tif (verbose)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n-\t\t\ttypename(obj->type), oid_to_hex(&obj->oid));\n+\t\t\ttypename(obj->type), describe_object(obj));\n \n \tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n@@ -326,15 +333,17 @@ static int fsck_obj(struct object *obj)\n \t\tfree_commit_buffer(commit);\n \n \t\tif (!commit->parents && show_root)\n-\t\t\tprintf(\"root %s\\n\", oid_to_hex(&commit->object.oid));\n+\t\t\tprintf(\"root %s\\n\", describe_object(&commit->object));\n \t}\n \n \tif (obj->type == OBJ_TAG) {\n \t\tstruct tag *tag = (struct tag *) obj;\n \n \t\tif (show_tags && tag->tagged) {\n-\t\t\tprintf(\"tagged %s %s\", typename(tag->tagged->type), oid_to_hex(&tag->tagged->oid));\n-\t\t\tprintf(\" (%s) in %s\\n\", tag->tag, oid_to_hex(&tag->object.oid));\n+\t\t\tprintf(\"tagged %s %s\", typename(tag->tagged->type),\n+\t\t\t\tdescribe_object(tag->tagged));\n+\t\t\tprintf(\" (%s) in %s\\n\", tag->tag,\n+\t\t\t\tdescribe_object(&tag->object));\n \t\t}\n \t}\n \n-- \n2.9.0.278.g1caae67\n\n\n"},{"id":"291420","messageId":"f5ef009d2be6bf0137b33ac9ce85ff0ce03a48e8.1468510191.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":"cover.1468510191.git.johannes.schindelin@gmx.de","subject":"[PATCH 2/3] fsck_walk(): optionally name objects on the go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-14T15:30:24Z","receivedAt":"2016-07-14T15:30:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"If fsck_options->name_objects is initialized, and if it already has\nname(s) for the object(s) that are to be the starting point(s) for\nfsck_walk(), then that function will now add names for the objects\nthat were walked.\n\nThis will be highly useful for teaching git-fsck to identify root causes\nfor broken links, which is the task for the next patch in this series.\n\nNote that this patch opts for decorating the objects with plain strings\ninstead of full-blown structs (à la `struct rev_name` in the code of\nthe `git name-rev` command), for several reasons:\n\n- the code is much simpler than if it had to work with structs that\n  describe arbitrarily long names such as \"master~14^2~5:builtin/am.c\",\n\n- the string processing is actually quite light-weight compared to the\n  rest of fsck's operation,\n\n- the caller of fsck_walk() is expected to provide names for the\n  starting points, and using plain and simple strings is just the\n  easiest way to do that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n fsck.h |  1 +\n 2 files changed, 73 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex 0531545..fe6a28a 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,7 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n #include \"sha1-array.h\"\n+#include \"decorate.h\"\n \n #define FSCK_FATAL -1\n #define FSCK_INFO -2\n@@ -297,19 +298,51 @@ static int report(struct fsck_options *options, struct object *object,\n \treturn result;\n }\n \n+static char *get_object_name(struct fsck_options *options, struct object *obj)\n+{\n+\treturn options->object_names ?\n+\t\tlookup_decoration(options->object_names, obj) : NULL;\n+}\n+\n+static void put_object_name(struct fsck_options *options, struct object *obj,\n+\tconst char *fmt, ...)\n+{\n+\tva_list ap;\n+\tchar *existing = lookup_decoration(options->object_names, obj);\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\tif (existing)\n+\t\treturn;\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&buf, fmt, ap);\n+\tadd_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));\n+\tva_end(ap);\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n \tint res = 0;\n+\tconst char *name;\n \n \tif (parse_tree(tree))\n \t\treturn -1;\n \n+\tname = get_object_name(options, &tree->object);\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n \t\tint result;\n \n+\t\tif (name) {\n+\t\t\tstruct object *obj = parse_object(entry.oid->hash);\n+\n+\t\t\tif (obj)\n+\t\t\t\tput_object_name(options, obj, \"%s%s%s\", name,\n+\t\t\t\t\tentry.path,\n+\t\t\t\t\tS_ISDIR(entry.mode) ? \"/\" : \"\");\n+\t\t}\n+\n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n \t\tif (S_ISDIR(entry.mode))\n@@ -330,20 +363,55 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \n static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n+\tint counter = 0, generation = 0, name_prefix_len = 0;\n \tstruct commit_list *parents;\n \tint res;\n \tint result;\n+\tconst char *name;\n \n \tif (parse_commit(commit))\n \t\treturn -1;\n \n+\tname = get_object_name(options, &commit->object);\n+\tif (name)\n+\t\tput_object_name(options, &commit->tree->object, \"%s:\", name);\n+\n \tresult = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);\n \tif (result < 0)\n \t\treturn result;\n \tres = result;\n \n \tparents = commit->parents;\n+\tif (name && parents) {\n+\t\tint len = strlen(name), power;\n+\n+\t\tif (len && name[len - 1] == '^') {\n+\t\t\tgeneration = 1;\n+\t\t\tname_prefix_len = len - 1;\n+\t\t}\n+\t\telse { /* parse ~<generation> suffix */\n+\t\t\tfor (generation = 0, power = 1;\n+\t\t\t     len && isdigit(name[len - 1]);\n+\t\t\t     power *= 10)\n+\t\t\t\tgeneration += power * (name[--len] - '0');\n+\t\t\tif (power > 1 && len && name[len - 1] == '~')\n+\t\t\t\tname_prefix_len = len - 1;\n+\t\t}\n+\t}\n+\n \twhile (parents) {\n+\t\tif (name) {\n+\t\t\tstruct object *obj = &parents->item->object;\n+\n+\t\t\tif (++counter > 1)\n+\t\t\t\tput_object_name(options, obj, \"%s^%d\",\n+\t\t\t\t\tname, counter);\n+\t\t\telse if (generation > 0)\n+\t\t\t\tput_object_name(options, obj, \"%.*s~%d\",\n+\t\t\t\t\tname_prefix_len, name, generation + 1);\n+\t\t\telse\n+\t\t\t\tput_object_name(options, obj, \"%s^\", name);\n+\t\t}\n \t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n@@ -356,8 +424,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio\n \n static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n+\tchar *name = get_object_name(options, &tag->object);\n+\n \tif (parse_tag(tag))\n \t\treturn -1;\n+\tif (name)\n+\t\tput_object_name(options, tag->tagged, \"%s\", name);\n \treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex dded84b..26c0d41 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -33,6 +33,7 @@ struct fsck_options {\n \tunsigned strict:1;\n \tint *msg_type;\n \tstruct sha1_array *skiplist;\n+\tstruct decoration *object_names;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n-- \n2.9.0.278.g1caae67\n\n"},{"id":"291421","messageId":"2cc123136b25cae12dce2a6f30fe0ba5ae8dc811.1468510191.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":"cover.1468510191.git.johannes.schindelin@gmx.de","subject":"[PATCH 3/3] fsck: optionally show more helpful info for broken links","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-14T15:30:47Z","receivedAt":"2016-07-14T15:31:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When reporting broken links between commits/trees/blobs, it would be\nquite helpful at times if the user would be told how the object is\nsupposed to be reachable.\n\nWith the new --name-objects option, git-fsck will try to do exactly\nthat: name the objects in a way that shows how they are reachable.\n\nFor example, when some reflog got corrupted and a blob is missing that\nshould not be, the user might want to remove the corresponding reflog\nentry. This option helps them find that entry: `git fsck` will now\nreport something like this:\n\n\tbroken link from    tree b5eb6ff...  (refs/stash@{<date>}~37:)\n\t              to    blob ec5cf80...\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  9 ++++++++-\n builtin/fsck.c             | 42 ++++++++++++++++++++++++++++++++++++++----\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 3 files changed, 68 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 7fc68eb..b9f060e 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -11,7 +11,8 @@ SYNOPSIS\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n \t [--[no-]full] [--strict] [--verbose] [--lost-found]\n-\t [--[no-]dangling] [--[no-]progress] [--connectivity-only] [<object>*]\n+\t [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n+\t [--[no-]name-objects] [<object>*]\n \n DESCRIPTION\n -----------\n@@ -82,6 +83,12 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \ta blob, the contents are written into the file, rather than\n \tits object name.\n \n+--name-objects::\n+\tWhen displaying names of reachable objects, in addition to the\n+\tSHA-1 also display a name that describes *how* they are reachable,\n+\tcompatible with linkgit:git-rev-parse[1], e.g.\n+\t`HEAD@{1234567890}~25^2:src/`.\n+\n --[no-]progress::\n \tProgress status is reported on the standard error stream by\n \tdefault when it is attached to a terminal, unless\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 87df191..e2173b6 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -13,6 +13,7 @@\n #include \"dir.h\"\n #include \"progress.h\"\n #include \"streaming.h\"\n+#include \"decorate.h\"\n \n #define REACHABLE 0x0001\n #define SEEN      0x0002\n@@ -35,6 +36,7 @@ static int write_lost_and_found;\n static int verbose;\n static int show_progress = -1;\n static int show_dangling = 1;\n+static int name_objects = 0;\n #define ERROR_OBJECT 01\n #define ERROR_REACHABLE 02\n #define ERROR_PACK 04\n@@ -42,7 +44,16 @@ static int show_dangling = 1;\n \n static const char *describe_object(struct object *obj)\n {\n-\treturn oid_to_hex(&obj->oid);\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\tchar *name = name_objects ?\n+\t\tlookup_decoration(fsck_walk_options.object_names, obj) : NULL;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addstr(&buf, oid_to_hex(&obj->oid));\n+\tif (name)\n+\t\tstrbuf_addf(&buf, \" (%s)\", name);\n+\n+\treturn buf.buf;\n }\n \n static int fsck_config(const char *var, const char *value, void *cb)\n@@ -377,13 +388,18 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,\n \n static int default_refs;\n \n-static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)\n+static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,\n+\tunsigned long timestamp)\n {\n \tstruct object *obj;\n \n \tif (!is_null_sha1(sha1)) {\n \t\tobj = lookup_object(sha1);\n \t\tif (obj) {\n+\t\t\tif (timestamp && name_objects)\n+\t\t\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\t\t\tobj,\n+\t\t\t\t\txstrfmt(\"%s@{%ld}\", refname, timestamp));\n \t\t\tobj->used = 1;\n \t\t\tmark_object_reachable(obj);\n \t\t} else {\n@@ -403,8 +419,8 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,\n \t\tfprintf(stderr, \"Checking reflog %s->%s\\n\",\n \t\t\tsha1_to_hex(osha1), sha1_to_hex(nsha1));\n \n-\tfsck_handle_reflog_sha1(refname, osha1);\n-\tfsck_handle_reflog_sha1(refname, nsha1);\n+\tfsck_handle_reflog_sha1(refname, osha1, 0);\n+\tfsck_handle_reflog_sha1(refname, nsha1, timestamp);\n \treturn 0;\n }\n \n@@ -433,6 +449,9 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n \t}\n \tdefault_refs++;\n \tobj->used = 1;\n+\tif (name_objects)\n+\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\tobj, xstrdup(refname));\n \tmark_object_reachable(obj);\n \n \treturn 0;\n@@ -548,6 +567,9 @@ static int fsck_cache_tree(struct cache_tree *it)\n \t\t\treturn 1;\n \t\t}\n \t\tobj->used = 1;\n+\t\tif (name_objects)\n+\t\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\t\tobj, xstrdup(\":\"));\n \t\tmark_object_reachable(obj);\n \t\tif (obj->type != OBJ_TREE)\n \t\t\terr |= objerror(obj, \"non-tree in cache-tree\");\n@@ -576,6 +598,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n \tOPT_BOOL(0, \"progress\", &show_progress, N_(\"show progress\")),\n+\tOPT_BOOL(0, \"name-objects\", &name_objects, N_(\"show verbose names for rechable objects\")),\n \tOPT_END(),\n };\n \n@@ -605,6 +628,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tif (name_objects)\n+\t\tfsck_walk_options.object_names =\n+\t\t\txcalloc(1, sizeof(struct decoration));\n+\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n@@ -660,6 +687,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \n \t\t\tobj->used = 1;\n+\t\t\tif (name_objects)\n+\t\t\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\t\t\tobj, xstrdup(arg));\n \t\t\tmark_object_reachable(obj);\n \t\t\theads++;\n \t\t\tcontinue;\n@@ -692,6 +722,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\tobj = &blob->object;\n \t\t\tobj->used = 1;\n+\t\t\tif (name_objects)\n+\t\t\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\t\t\tobj,\n+\t\t\t\t\txstrfmt(\":%s\", active_cache[i]->name));\n \t\t\tmark_object_reachable(obj);\n \t\t}\n \t\tif (active_cache_tree)\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 7ee8ea0..8f52da2 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -523,4 +523,26 @@ test_expect_success 'fsck --connectivity-only' '\n \t)\n '\n \n+remove_loose_object () {\n+\tsha1=\"$(git rev-parse \"$1\")\" &&\n+\tremainder=${sha1#??} &&\n+\tfirsttwo=${sha1%$remainder} &&\n+\trm .git/objects/$firsttwo/$remainder\n+}\n+\n+test_expect_success 'fsck --name-objects' '\n+\trm -rf name-objects &&\n+\tgit init name-objects &&\n+\t(\n+\t\tcd name-objects &&\n+\t\ttest_commit julius caesar.t &&\n+\t\ttest_commit augustus &&\n+\t\ttest_commit caesar &&\n+\t\tremove_loose_object $(git rev-parse julius:caesar.t) &&\n+\t\ttest_must_fail git fsck --name-objects >out &&\n+\t\ttree=$(git rev-parse --verify julius:) &&\n+\t\tgrep \"$tree (\\(refs/heads/master\\|HEAD\\)@{[0-9]*}:\" out\n+\t)\n+'\n+\n test_done\n-- \n2.9.0.278.g1caae67\n"},{"id":"291433","messageId":"xmqqmvlkrxhm.fsf@gitster.mtv.corp.google.com","threadId":"42845","inReplyTo":"cover.1468510191.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 0/3] Teach `git fsck` a new option: `--name-objects`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-14T16:32:53Z","receivedAt":"2016-07-14T16:33:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Example output:\n>\n> \t...\n> \tbroken link from    tree b5eb6ff...  (refs/stash@{<date>}~37:)\n> \t\t      to    blob ec5cf80...\n\nThe objective makes sense, and their progression is very nicely\nstructured.  I can \"smell\" that these are going in the right\ndirection only with a cursory scan of the three patches.\n\n> Originally, I intended to teach name-rev a new mode where it would also\n> name objects other than commits and tags,...\n\nAs to having it in name-rev, it is still a \"good to have\" for an\nobject that does exist.  It would be \"super nice\" if it also worked\nfor a missing object.  It makes tons of sense from the end-user UI\npoint of view to have this feature there.\n\nI however agree with you that it is sensible to do this in \"fsck\"\nfirst and leave the \"good to have\" to later, because (1) naming an\narbitrary blob like this needs full object-store scan like \"fsck\"\ndoes anyway, and (2) the primary occasion users would want to use\nthe \"super nice\" part of the feature is when they discover an object\nis \"missing\", and the first thing they would want to run in such a\ncase anyway is \"fsck\".\n\nSo, in short, I very much like them.\n\n"},{"id":"291434","messageId":"xmqqinw8rxgs.fsf@gitster.mtv.corp.google.com","threadId":"42845","inReplyTo":"75cd5de6b5a027a32d6ed6b922aa601226d464e5.1468510191.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 1/3] fsck: refactor how to describe objects","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-14T16:33:23Z","receivedAt":"2016-07-14T16:33:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> In many places, we refer to objects via their SHA-1s. Let's abstract\n> that into a function.\n>\n> For the moment, it does nothing else than what we did previously: print\n> out the 40-digit hex string. But that will change over the course of the\n> next patches.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n\nMakes sense.\n"},{"id":"291438","messageId":"xmqqeg6wrw2m.fsf@gitster.mtv.corp.google.com","threadId":"42845","inReplyTo":"f5ef009d2be6bf0137b33ac9ce85ff0ce03a48e8.1468510191.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 2/3] fsck_walk(): optionally name objects on the go","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-14T17:03:29Z","receivedAt":"2016-07-14T17:03:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Note that this patch opts for decorating the objects with plain strings\n> instead of full-blown structs (à la `struct rev_name` in the code of\n> the `git name-rev` command), for several reasons:\n>\n> - the code is much simpler than if it had to work with structs that\n>   describe arbitrarily long names such as \"master~14^2~5:builtin/am.c\",\n>\n> - the string processing is actually quite light-weight compared to the\n>   rest of fsck's operation,\n>\n> - the caller of fsck_walk() is expected to provide names for the\n>   starting points, and using plain and simple strings is just the\n>   easiest way to do that.\n\nSimpler is good; we can always optimize something well-isolated like\nthis later if it proves necessary.\n\n> +static char *get_object_name(struct fsck_options *options, struct object *obj)\n> +{\n> +\treturn options->object_names ?\n> +\t\tlookup_decoration(options->object_names, obj) : NULL;\n> +}\n> +\n> +static void put_object_name(struct fsck_options *options, struct object *obj,\n> +\tconst char *fmt, ...)\n> +{\n> +\tva_list ap;\n> +\tchar *existing = lookup_decoration(options->object_names, obj);\n> +\tstruct strbuf buf = STRBUF_INIT;\n\nWhen reading a few early calling sites, it wasn't quite obvious how\nthe code avoids the \"naming\" when .object_names decoration is not\ninitialized (which is tied to the --name-objects option to decide if\nthe feature needs to be triggered).  The current \"if get_object_name\nfor the containing object gives us NULL, then we refrain from\ncalling put_object_name()\" may be good enough, but having an early\nreturn similar to get_object_name() would make it easier to grok,\ni.e.\n\n\tget_object_name(...)\n        {\n        \tif (!options->object_names)\n                \treturn NULL;\n\t\treturn lookup_decoration(...);\n\t}\n\n\tput_object_name(...)\n        {\n\t\t... decls ...\n\n        \tif (!options->object_names)\n                \treturn NULL;\n\t\texisting = lookup_decoration(...);\n                if (existing)\n                \treturn existing;\n\t\t...\n\t}\n\nIt is a minor point, as the caller needs to check \"name\" that is the\nvalue returned from get_object_name() for the containing object to\navoid wasting cycles to compute the parameters to pass to\nput_object_name() anyway.\n\n>  \twhile (tree_entry(&desc, &entry)) {\n>  \t\tint result;\n>  \n> +\t\tif (name) {\n> +\t\t\tstruct object *obj = parse_object(entry.oid->hash);\n\nThis worries me somewhat.  IIRC, \"git fsck\" uses object->parsed to\ntell between objects that are unreachable or not and act differently\nso I would fear that parsing the object here would screw up that\nlogic, when the call comes from fsck_dir() -> fsck_sha1_list() ->\nfsck_sha1() -> fsck_obj() -> fsck_walk() -> fsck_walk_tree()\ncodepath.  Is it no longer the case, I wonder?\n\nI see in the same loop there is a call to lookup_tree()->object, which\nprobably is how the existing code avoids that issue?\n"},{"id":"291505","messageId":"CAPig+cS=LVBhy1pqo2KXz-NoPZdPzkzTgV+S1Ro5+zOjDpKGPw@mail.gmail.com","threadId":"42845","inReplyTo":"2cc123136b25cae12dce2a6f30fe0ba5ae8dc811.1468510191.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 3/3] fsck: optionally show more helpful info for broken links","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-07-15T06:20:41Z","receivedAt":"2016-07-15T06:20:47Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Jul 14, 2016 at 11:30 AM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> When reporting broken links between commits/trees/blobs, it would be\n> quite helpful at times if the user would be told how the object is\n> supposed to be reachable.\n>\n> With the new --name-objects option, git-fsck will try to do exactly\n> that: name the objects in a way that shows how they are reachable.\n> [...]\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n> diff --git a/builtin/fsck.c b/builtin/fsck.c\n> @@ -576,6 +598,7 @@ static struct option fsck_opts[] = {\n>         OPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n>                                 N_(\"write dangling objects in .git/lost-found\")),\n>         OPT_BOOL(0, \"progress\", &show_progress, N_(\"show progress\")),\n> +       OPT_BOOL(0, \"name-objects\", &name_objects, N_(\"show verbose names for rechable objects\")),\n\ns/rechable/reachable/\n\n>         OPT_END(),\n>  };\n"},{"id":"291619","messageId":"alpine.DEB.2.20.1607171021520.28832@virtualbox","threadId":"42845","inReplyTo":"CAPig+cS=LVBhy1pqo2KXz-NoPZdPzkzTgV+S1Ro5+zOjDpKGPw@mail.gmail.com","subject":"Re: [PATCH 3/3] fsck: optionally show more helpful info for broken links","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-17T08:22:33Z","receivedAt":"2016-07-17T08:23:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn Fri, 15 Jul 2016, Eric Sunshine wrote:\n\n> On Thu, Jul 14, 2016 at 11:30 AM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n> > When reporting broken links between commits/trees/blobs, it would be\n> > quite helpful at times if the user would be told how the object is\n> > supposed to be reachable.\n> >\n> > With the new --name-objects option, git-fsck will try to do exactly\n> > that: name the objects in a way that shows how they are reachable.\n> > [...]\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> > diff --git a/builtin/fsck.c b/builtin/fsck.c\n> > @@ -576,6 +598,7 @@ static struct option fsck_opts[] = {\n> >         OPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n> >                                 N_(\"write dangling objects in .git/lost-found\")),\n> >         OPT_BOOL(0, \"progress\", &show_progress, N_(\"show progress\")),\n> > +       OPT_BOOL(0, \"name-objects\", &name_objects, N_(\"show verbose names for rechable objects\")),\n> \n> s/rechable/reachable/\n> \n> >         OPT_END(),\n> >  };\n\nThanks, as always, for a set of extra sharp eyes. Will be fixed in the\nnext iteration.\n\nCiao,\nDscho\n"},{"id":"291620","messageId":"alpine.DEB.2.20.1607171026420.28832@virtualbox","threadId":"42845","inReplyTo":"xmqqeg6wrw2m.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 2/3] fsck_walk(): optionally name objects on the go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-17T08:44:27Z","receivedAt":"2016-07-17T08:44:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 14 Jul 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Note that this patch opts for decorating the objects with plain strings\n> > instead of full-blown structs (à la `struct rev_name` in the code of\n> > the `git name-rev` command), for several reasons:\n> >\n> > - the code is much simpler than if it had to work with structs that\n> >   describe arbitrarily long names such as \"master~14^2~5:builtin/am.c\",\n> >\n> > - the string processing is actually quite light-weight compared to the\n> >   rest of fsck's operation,\n> >\n> > - the caller of fsck_walk() is expected to provide names for the\n> >   starting points, and using plain and simple strings is just the\n> >   easiest way to do that.\n> \n> Simpler is good; we can always optimize something well-isolated like\n> this later if it proves necessary.\n\nI am glad we agree!\n\n> > +static char *get_object_name(struct fsck_options *options, struct object *obj)\n> > +{\n> > +\treturn options->object_names ?\n> > +\t\tlookup_decoration(options->object_names, obj) : NULL;\n> > +}\n> > +\n> > +static void put_object_name(struct fsck_options *options, struct object *obj,\n> > +\tconst char *fmt, ...)\n> > +{\n> > +\tva_list ap;\n> > +\tchar *existing = lookup_decoration(options->object_names, obj);\n> > +\tstruct strbuf buf = STRBUF_INIT;\n> \n> When reading a few early calling sites, it wasn't quite obvious how\n> the code avoids the \"naming\" when .object_names decoration is not\n> initialized (which is tied to the --name-objects option to decide if\n> the feature needs to be triggered).  The current \"if get_object_name\n> for the containing object gives us NULL, then we refrain from\n> calling put_object_name()\" may be good enough, but having an early\n> return similar to get_object_name() would make it easier to grok,\n\nMy knee-jerk reaction was: in order to name objects in this part of the\ncode, we need the name of the parent/tree/whatever, so yeah, we have\nobject_names.\n\nBut you're right, it is much easier to read with the early returns.\n\nAnd who knows, maybe the fsck.c code will learn to name the starting\npoints itself in the future?\n\n> >  \twhile (tree_entry(&desc, &entry)) {\n> >  \t\tint result;\n> >  \n> > +\t\tif (name) {\n> > +\t\t\tstruct object *obj = parse_object(entry.oid->hash);\n> \n> This worries me somewhat.  IIRC, \"git fsck\" uses object->parsed to\n> tell between objects that are unreachable or not and act differently\n> so I would fear that parsing the object here would screw up that\n> logic, when the call comes from fsck_dir() -> fsck_sha1_list() ->\n> fsck_sha1() -> fsck_obj() -> fsck_walk() -> fsck_walk_tree()\n> codepath.  Is it no longer the case, I wonder?\n> \n> I see in the same loop there is a call to lookup_tree()->object, which\n> probably is how the existing code avoids that issue?\n\nMost likely. I factored that out so that the object is looked up first,\nand reused via object_as_type() for the tree/blob cases.\n\nBoth concerns will be addressed in the next iteration.\n\nThanks,\nDscho"},{"id":"291621","messageId":"cover.1468752290.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":"cover.1468510191.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 0/4] Teach `git fsck` a new option: `--name-objects`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-17T10:59:38Z","receivedAt":"2016-07-17T11:00:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When using experimental features (such as worktrees), it is quite\npossible to end up with a repository that is a little bit corrupted. In\nthis developer's case, the auto gc run during interactive rebases in\nworktrees completely messed up the reflogs.\n\nThe symptoms are broken links between commits/trees/blobs.\n\nTrying to work around such problems can be a real challenge: while\nseveral tools will report when objects are missing, all of them simply\nstate the SHA-1. This is not useful when the user has to kiss the\noffending reflog good-bye, but does not even know which one.\n\nThis patch series introduces a new option to `git fsck`: --name-objects.\nWith this option, the fsck command will report not only the SHA-1 of\nmissing objects, but also a name by which this object is supposed to be\nreachable.\n\nExample output:\n\n\t...\n\tbroken link from    tree b5eb6ff...  (refs/stash@{<date>}~37:)\n\t\t      to    blob ec5cf80...\n\nRelative to v1, a tyop was fixed, Junio's suggestions about an early\nreturn from {get,put}_object_name() and about avoiding parsing of\nobjects were addressed, and I found a couple more places in fsck.c that\ncould benefit from object names.\n\n\nJohannes Schindelin (4):\n  fsck: refactor how to describe objects\n  fsck_walk(): optionally name objects on the go\n  fsck: give the error function a chance to see the fsck_options\n  fsck: optionally show more helpful info for broken links\n\n Documentation/git-fsck.txt |   9 +++-\n builtin/fsck.c             |  80 ++++++++++++++++++++++++--------\n fsck.c                     | 113 +++++++++++++++++++++++++++++++++++++++++----\n fsck.h                     |   7 ++-\n t/t1450-fsck.sh            |  22 +++++++++\n 5 files changed, 200 insertions(+), 31 deletions(-)\n\nPublished-As: https://github.com/dscho/git/releases/tag/fsck-name-objects-v2\nInterdiff vs v1:\n\n diff --git a/builtin/fsck.c b/builtin/fsck.c\n index e2173b6..49680e9 100644\n --- a/builtin/fsck.c\n +++ b/builtin/fsck.c\n @@ -93,7 +93,8 @@ static int objerror(struct object *obj, const char *err)\n  \treturn -1;\n  }\n  \n -static int fsck_error_func(struct object *obj, int type, const char *message)\n +static int fsck_error_func(struct fsck_options *o,\n +\tstruct object *obj, int type, const char *message)\n  {\n  \tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n  \treturn (type == FSCK_WARN) ? 0 : 1;\n @@ -598,7 +599,7 @@ static struct option fsck_opts[] = {\n  \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n  \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n  \tOPT_BOOL(0, \"progress\", &show_progress, N_(\"show progress\")),\n -\tOPT_BOOL(0, \"name-objects\", &name_objects, N_(\"show verbose names for rechable objects\")),\n +\tOPT_BOOL(0, \"name-objects\", &name_objects, N_(\"show verbose names for reachable objects\")),\n  \tOPT_END(),\n  };\n  \n diff --git a/fsck.c b/fsck.c\n index fe6a28a..c9cf3de 100644\n --- a/fsck.c\n +++ b/fsck.c\n @@ -291,7 +291,7 @@ static int report(struct fsck_options *options, struct object *object,\n  \n  \tva_start(ap, fmt);\n  \tstrbuf_vaddf(&sb, fmt, ap);\n -\tresult = options->error_func(object, msg_type, sb.buf);\n +\tresult = options->error_func(options, object, msg_type, sb.buf);\n  \tstrbuf_release(&sb);\n  \tva_end(ap);\n  \n @@ -300,17 +300,21 @@ static int report(struct fsck_options *options, struct object *object,\n  \n  static char *get_object_name(struct fsck_options *options, struct object *obj)\n  {\n -\treturn options->object_names ?\n -\t\tlookup_decoration(options->object_names, obj) : NULL;\n +\tif (!options->object_names)\n +\t\treturn NULL;\n +\treturn lookup_decoration(options->object_names, obj);\n  }\n  \n  static void put_object_name(struct fsck_options *options, struct object *obj,\n  \tconst char *fmt, ...)\n  {\n  \tva_list ap;\n -\tchar *existing = lookup_decoration(options->object_names, obj);\n  \tstruct strbuf buf = STRBUF_INIT;\n +\tchar *existing;\n  \n +\tif (!options->object_names)\n +\t\treturn;\n +\texisting = lookup_decoration(options->object_names, obj);\n  \tif (existing)\n  \t\treturn;\n  \tva_start(ap, fmt);\n @@ -319,6 +323,19 @@ static void put_object_name(struct fsck_options *options, struct object *obj,\n  \tva_end(ap);\n  }\n  \n +static const char *describe_object(struct fsck_options *o, struct object *obj)\n +{\n +\tstatic struct strbuf buf = STRBUF_INIT;\n +\tchar *name;\n +\n +\tstrbuf_reset(&buf);\n +\tstrbuf_addstr(&buf, oid_to_hex(&obj->oid));\n +\tif (o->object_names && (name = lookup_decoration(o->object_names, obj)))\n +\t\tstrbuf_addf(&buf, \" (%s)\", name);\n +\n +\treturn buf.buf;\n +}\n +\n  static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n  {\n  \tstruct tree_desc desc;\n @@ -332,26 +349,29 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n  \tname = get_object_name(options, &tree->object);\n  \tinit_tree_desc(&desc, tree->buffer, tree->size);\n  \twhile (tree_entry(&desc, &entry)) {\n +\t\tstruct object *obj;\n  \t\tint result;\n  \n -\t\tif (name) {\n -\t\t\tstruct object *obj = parse_object(entry.oid->hash);\n -\n -\t\t\tif (obj)\n -\t\t\t\tput_object_name(options, obj, \"%s%s%s\", name,\n -\t\t\t\t\tentry.path,\n -\t\t\t\t\tS_ISDIR(entry.mode) ? \"/\" : \"\");\n -\t\t}\n -\n  \t\tif (S_ISGITLINK(entry.mode))\n  \t\t\tcontinue;\n -\t\tif (S_ISDIR(entry.mode))\n -\t\t\tresult = options->walk(&lookup_tree(entry.oid->hash)->object, OBJ_TREE, data, options);\n -\t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n -\t\t\tresult = options->walk(&lookup_blob(entry.oid->hash)->object, OBJ_BLOB, data, options);\n +\n +\t\tif (S_ISDIR(entry.mode)) {\n +\t\t\tobj = &lookup_tree(entry.oid->hash)->object;\n +\t\t\tif (name)\n +\t\t\t\tput_object_name(options, obj, \"%s%s/\", name,\n +\t\t\t\t\tentry.path);\n +\t\t\tresult = options->walk(obj, OBJ_TREE, data, options);\n +\t\t}\n +\t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {\n +\t\t\tobj = &lookup_blob(entry.oid->hash)->object;\n +\t\t\tif (name)\n +\t\t\t\tput_object_name(options, obj, \"%s%s\", name,\n +\t\t\t\t\tentry.path);\n +\t\t\tresult = options->walk(obj, OBJ_BLOB, data, options);\n +\t\t}\n  \t\telse {\n  \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n -\t\t\t\t\toid_to_hex(&tree->object.oid), entry.path, entry.mode);\n +\t\t\t\t\tdescribe_object(options, &tree->object), entry.path, entry.mode);\n  \t\t}\n  \t\tif (result < 0)\n  \t\t\treturn result;\n @@ -447,7 +467,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n  \tcase OBJ_TAG:\n  \t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n  \tdefault:\n -\t\terror(\"Unknown object type for %s\", oid_to_hex(&obj->oid));\n +\t\terror(\"Unknown object type for %s\", describe_object(options, obj));\n  \t\treturn -1;\n  \t}\n  }\n @@ -890,12 +910,13 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n  \t\t\t  obj->type);\n  }\n  \n -int fsck_error_function(struct object *obj, int msg_type, const char *message)\n +int fsck_error_function(struct fsck_options *o,\n +\tstruct object *obj, int msg_type, const char *message)\n  {\n  \tif (msg_type == FSCK_WARN) {\n -\t\twarning(\"object %s: %s\", oid_to_hex(&obj->oid), message);\n +\t\twarning(\"object %s: %s\", describe_object(o, obj), message);\n  \t\treturn 0;\n  \t}\n -\terror(\"object %s: %s\", oid_to_hex(&obj->oid), message);\n +\terror(\"object %s: %s\", describe_object(o, obj), message);\n  \treturn 1;\n  }\n diff --git a/fsck.h b/fsck.h\n index 26c0d41..1891c18 100644\n --- a/fsck.h\n +++ b/fsck.h\n @@ -23,9 +23,11 @@ int is_valid_msg_type(const char *msg_id, const char *msg_type);\n  typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options);\n  \n  /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */\n -typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n +typedef int (*fsck_error)(struct fsck_options *o,\n +\tstruct object *obj, int type, const char *message);\n  \n -int fsck_error_function(struct object *obj, int type, const char *message);\n +int fsck_error_function(struct fsck_options *o,\n +\tstruct object *obj, int type, const char *message);\n  \n  struct fsck_options {\n  \tfsck_walk_func walk;\n\n-- \n2.9.0.281.g286a8d9\n\nbase-commit: 29493589e97a2de0c4c1c314f61ccafaee3b5caf\n"},{"id":"291622","messageId":"cd7d97f566484e146eccebe0a0a2335c852bb532.1468752290.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":"cover.1468752290.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 2/4] fsck_walk(): optionally name objects on the go","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-17T10:59:49Z","receivedAt":"2016-07-17T11:00:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"If fsck_options->name_objects is initialized, and if it already has\nname(s) for the object(s) that are to be the starting point(s) for\nfsck_walk(), then that function will now add names for the objects\nthat were walked.\n\nThis will be highly useful for teaching git-fsck to identify root causes\nfor broken links, which is the task for the next patch in this series.\n\nNote that this patch opts for decorating the objects with plain strings\ninstead of full-blown structs (à la `struct rev_name` in the code of\nthe `git name-rev` command), for several reasons:\n\n- the code is much simpler than if it had to work with structs that\n  describe arbitrarily long names such as \"master~14^2~5:builtin/am.c\",\n\n- the string processing is actually quite light-weight compared to the\n  rest of fsck's operation,\n\n- the caller of fsck_walk() is expected to provide names for the\n  starting points, and using plain and simple strings is just the\n  easiest way to do that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n fsck.h |  1 +\n 2 files changed, 84 insertions(+), 4 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 0531545..6ed90ec 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,7 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n #include \"sha1-array.h\"\n+#include \"decorate.h\"\n \n #define FSCK_FATAL -1\n #define FSCK_INFO -2\n@@ -297,25 +298,64 @@ static int report(struct fsck_options *options, struct object *object,\n \treturn result;\n }\n \n+static char *get_object_name(struct fsck_options *options, struct object *obj)\n+{\n+\tif (!options->object_names)\n+\t\treturn NULL;\n+\treturn lookup_decoration(options->object_names, obj);\n+}\n+\n+static void put_object_name(struct fsck_options *options, struct object *obj,\n+\tconst char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tchar *existing;\n+\n+\tif (!options->object_names)\n+\t\treturn;\n+\texisting = lookup_decoration(options->object_names, obj);\n+\tif (existing)\n+\t\treturn;\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&buf, fmt, ap);\n+\tadd_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));\n+\tva_end(ap);\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n \tstruct name_entry entry;\n \tint res = 0;\n+\tconst char *name;\n \n \tif (parse_tree(tree))\n \t\treturn -1;\n \n+\tname = get_object_name(options, &tree->object);\n \tinit_tree_desc(&desc, tree->buffer, tree->size);\n \twhile (tree_entry(&desc, &entry)) {\n+\t\tstruct object *obj;\n \t\tint result;\n \n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n-\t\tif (S_ISDIR(entry.mode))\n-\t\t\tresult = options->walk(&lookup_tree(entry.oid->hash)->object, OBJ_TREE, data, options);\n-\t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n-\t\t\tresult = options->walk(&lookup_blob(entry.oid->hash)->object, OBJ_BLOB, data, options);\n+\n+\t\tif (S_ISDIR(entry.mode)) {\n+\t\t\tobj = &lookup_tree(entry.oid->hash)->object;\n+\t\t\tif (name)\n+\t\t\t\tput_object_name(options, obj, \"%s%s/\", name,\n+\t\t\t\t\tentry.path);\n+\t\t\tresult = options->walk(obj, OBJ_TREE, data, options);\n+\t\t}\n+\t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {\n+\t\t\tobj = &lookup_blob(entry.oid->hash)->object;\n+\t\t\tif (name)\n+\t\t\t\tput_object_name(options, obj, \"%s%s\", name,\n+\t\t\t\t\tentry.path);\n+\t\t\tresult = options->walk(obj, OBJ_BLOB, data, options);\n+\t\t}\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n \t\t\t\t\toid_to_hex(&tree->object.oid), entry.path, entry.mode);\n@@ -330,20 +370,55 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \n static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n+\tint counter = 0, generation = 0, name_prefix_len = 0;\n \tstruct commit_list *parents;\n \tint res;\n \tint result;\n+\tconst char *name;\n \n \tif (parse_commit(commit))\n \t\treturn -1;\n \n+\tname = get_object_name(options, &commit->object);\n+\tif (name)\n+\t\tput_object_name(options, &commit->tree->object, \"%s:\", name);\n+\n \tresult = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);\n \tif (result < 0)\n \t\treturn result;\n \tres = result;\n \n \tparents = commit->parents;\n+\tif (name && parents) {\n+\t\tint len = strlen(name), power;\n+\n+\t\tif (len && name[len - 1] == '^') {\n+\t\t\tgeneration = 1;\n+\t\t\tname_prefix_len = len - 1;\n+\t\t}\n+\t\telse { /* parse ~<generation> suffix */\n+\t\t\tfor (generation = 0, power = 1;\n+\t\t\t     len && isdigit(name[len - 1]);\n+\t\t\t     power *= 10)\n+\t\t\t\tgeneration += power * (name[--len] - '0');\n+\t\t\tif (power > 1 && len && name[len - 1] == '~')\n+\t\t\t\tname_prefix_len = len - 1;\n+\t\t}\n+\t}\n+\n \twhile (parents) {\n+\t\tif (name) {\n+\t\t\tstruct object *obj = &parents->item->object;\n+\n+\t\t\tif (++counter > 1)\n+\t\t\t\tput_object_name(options, obj, \"%s^%d\",\n+\t\t\t\t\tname, counter);\n+\t\t\telse if (generation > 0)\n+\t\t\t\tput_object_name(options, obj, \"%.*s~%d\",\n+\t\t\t\t\tname_prefix_len, name, generation + 1);\n+\t\t\telse\n+\t\t\t\tput_object_name(options, obj, \"%s^\", name);\n+\t\t}\n \t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n@@ -356,8 +431,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio\n \n static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n+\tchar *name = get_object_name(options, &tag->object);\n+\n \tif (parse_tag(tag))\n \t\treturn -1;\n+\tif (name)\n+\t\tput_object_name(options, tag->tagged, \"%s\", name);\n \treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex dded84b..26c0d41 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -33,6 +33,7 @@ struct fsck_options {\n \tunsigned strict:1;\n \tint *msg_type;\n \tstruct sha1_array *skiplist;\n+\tstruct decoration *object_names;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n-- \n2.9.0.281.g286a8d9\n\n"},{"id":"291623","messageId":"ffad4bc034bdffdfb05a59d08d070d0461b44f8e.1468752290.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":"cover.1468752290.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 3/4] fsck: give the error function a chance to see the fsck_options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-17T10:59:57Z","receivedAt":"2016-07-17T11:00:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We will need this in the next commit, where fsck will be taught to\noptionally name the objects when reporting issues about them.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c | 3 ++-\n fsck.c         | 5 +++--\n fsck.h         | 6 ++++--\n 3 files changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 87df191..6c9d598 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -82,7 +82,8 @@ static int objerror(struct object *obj, const char *err)\n \treturn -1;\n }\n \n-static int fsck_error_func(struct object *obj, int type, const char *message)\n+static int fsck_error_func(struct fsck_options *o,\n+\tstruct object *obj, int type, const char *message)\n {\n \tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n \treturn (type == FSCK_WARN) ? 0 : 1;\ndiff --git a/fsck.c b/fsck.c\nindex 6ed90ec..828c5c6 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -291,7 +291,7 @@ static int report(struct fsck_options *options, struct object *object,\n \n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n-\tresult = options->error_func(object, msg_type, sb.buf);\n+\tresult = options->error_func(options, object, msg_type, sb.buf);\n \tstrbuf_release(&sb);\n \tva_end(ap);\n \n@@ -897,7 +897,8 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \t\t\t  obj->type);\n }\n \n-int fsck_error_function(struct object *obj, int msg_type, const char *message)\n+int fsck_error_function(struct fsck_options *o,\n+\tstruct object *obj, int msg_type, const char *message)\n {\n \tif (msg_type == FSCK_WARN) {\n \t\twarning(\"object %s: %s\", oid_to_hex(&obj->oid), message);\ndiff --git a/fsck.h b/fsck.h\nindex 26c0d41..1891c18 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -23,9 +23,11 @@ int is_valid_msg_type(const char *msg_id, const char *msg_type);\n typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options);\n \n /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */\n-typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n+typedef int (*fsck_error)(struct fsck_options *o,\n+\tstruct object *obj, int type, const char *message);\n \n-int fsck_error_function(struct object *obj, int type, const char *message);\n+int fsck_error_function(struct fsck_options *o,\n+\tstruct object *obj, int type, const char *message);\n \n struct fsck_options {\n \tfsck_walk_func walk;\n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291624","messageId":"83754a3f19a172af1e0ff014060a0c698f3d6678.1468752291.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":"cover.1468752290.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 4/4] fsck: optionally show more helpful info for broken links","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-17T11:00:02Z","receivedAt":"2016-07-17T11:00:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When reporting broken links between commits/trees/blobs, it would be\nquite helpful at times if the user would be told how the object is\nsupposed to be reachable.\n\nWith the new --name-objects option, git-fsck will try to do exactly\nthat: name the objects in a way that shows how they are reachable.\n\nFor example, when some reflog got corrupted and a blob is missing that\nshould not be, the user might want to remove the corresponding reflog\nentry. This option helps them find that entry: `git fsck` will now\nreport something like this:\n\n\tbroken link from    tree b5eb6ff...  (refs/stash@{<date>}~37:)\n\t              to    blob ec5cf80...\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  9 ++++++++-\n builtin/fsck.c             | 42 ++++++++++++++++++++++++++++++++++++++----\n fsck.c                     | 21 +++++++++++++++++----\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 4 files changed, 85 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 7fc68eb..b9f060e 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -11,7 +11,8 @@ SYNOPSIS\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n \t [--[no-]full] [--strict] [--verbose] [--lost-found]\n-\t [--[no-]dangling] [--[no-]progress] [--connectivity-only] [<object>*]\n+\t [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n+\t [--[no-]name-objects] [<object>*]\n \n DESCRIPTION\n -----------\n@@ -82,6 +83,12 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \ta blob, the contents are written into the file, rather than\n \tits object name.\n \n+--name-objects::\n+\tWhen displaying names of reachable objects, in addition to the\n+\tSHA-1 also display a name that describes *how* they are reachable,\n+\tcompatible with linkgit:git-rev-parse[1], e.g.\n+\t`HEAD@{1234567890}~25^2:src/`.\n+\n --[no-]progress::\n \tProgress status is reported on the standard error stream by\n \tdefault when it is attached to a terminal, unless\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 6c9d598..49680e9 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -13,6 +13,7 @@\n #include \"dir.h\"\n #include \"progress.h\"\n #include \"streaming.h\"\n+#include \"decorate.h\"\n \n #define REACHABLE 0x0001\n #define SEEN      0x0002\n@@ -35,6 +36,7 @@ static int write_lost_and_found;\n static int verbose;\n static int show_progress = -1;\n static int show_dangling = 1;\n+static int name_objects = 0;\n #define ERROR_OBJECT 01\n #define ERROR_REACHABLE 02\n #define ERROR_PACK 04\n@@ -42,7 +44,16 @@ static int show_dangling = 1;\n \n static const char *describe_object(struct object *obj)\n {\n-\treturn oid_to_hex(&obj->oid);\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\tchar *name = name_objects ?\n+\t\tlookup_decoration(fsck_walk_options.object_names, obj) : NULL;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addstr(&buf, oid_to_hex(&obj->oid));\n+\tif (name)\n+\t\tstrbuf_addf(&buf, \" (%s)\", name);\n+\n+\treturn buf.buf;\n }\n \n static int fsck_config(const char *var, const char *value, void *cb)\n@@ -378,13 +389,18 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,\n \n static int default_refs;\n \n-static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)\n+static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,\n+\tunsigned long timestamp)\n {\n \tstruct object *obj;\n \n \tif (!is_null_sha1(sha1)) {\n \t\tobj = lookup_object(sha1);\n \t\tif (obj) {\n+\t\t\tif (timestamp && name_objects)\n+\t\t\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\t\t\tobj,\n+\t\t\t\t\txstrfmt(\"%s@{%ld}\", refname, timestamp));\n \t\t\tobj->used = 1;\n \t\t\tmark_object_reachable(obj);\n \t\t} else {\n@@ -404,8 +420,8 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,\n \t\tfprintf(stderr, \"Checking reflog %s->%s\\n\",\n \t\t\tsha1_to_hex(osha1), sha1_to_hex(nsha1));\n \n-\tfsck_handle_reflog_sha1(refname, osha1);\n-\tfsck_handle_reflog_sha1(refname, nsha1);\n+\tfsck_handle_reflog_sha1(refname, osha1, 0);\n+\tfsck_handle_reflog_sha1(refname, nsha1, timestamp);\n \treturn 0;\n }\n \n@@ -434,6 +450,9 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,\n \t}\n \tdefault_refs++;\n \tobj->used = 1;\n+\tif (name_objects)\n+\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\tobj, xstrdup(refname));\n \tmark_object_reachable(obj);\n \n \treturn 0;\n@@ -549,6 +568,9 @@ static int fsck_cache_tree(struct cache_tree *it)\n \t\t\treturn 1;\n \t\t}\n \t\tobj->used = 1;\n+\t\tif (name_objects)\n+\t\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\t\tobj, xstrdup(\":\"));\n \t\tmark_object_reachable(obj);\n \t\tif (obj->type != OBJ_TREE)\n \t\t\terr |= objerror(obj, \"non-tree in cache-tree\");\n@@ -577,6 +599,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n \tOPT_BOOL(0, \"progress\", &show_progress, N_(\"show progress\")),\n+\tOPT_BOOL(0, \"name-objects\", &name_objects, N_(\"show verbose names for reachable objects\")),\n \tOPT_END(),\n };\n \n@@ -606,6 +629,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tif (name_objects)\n+\t\tfsck_walk_options.object_names =\n+\t\t\txcalloc(1, sizeof(struct decoration));\n+\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n@@ -661,6 +688,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \n \t\t\tobj->used = 1;\n+\t\t\tif (name_objects)\n+\t\t\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\t\t\tobj, xstrdup(arg));\n \t\t\tmark_object_reachable(obj);\n \t\t\theads++;\n \t\t\tcontinue;\n@@ -693,6 +723,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\t\t\tcontinue;\n \t\t\tobj = &blob->object;\n \t\t\tobj->used = 1;\n+\t\t\tif (name_objects)\n+\t\t\t\tadd_decoration(fsck_walk_options.object_names,\n+\t\t\t\t\tobj,\n+\t\t\t\t\txstrfmt(\":%s\", active_cache[i]->name));\n \t\t\tmark_object_reachable(obj);\n \t\t}\n \t\tif (active_cache_tree)\ndiff --git a/fsck.c b/fsck.c\nindex 828c5c6..c9cf3de 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -323,6 +323,19 @@ static void put_object_name(struct fsck_options *options, struct object *obj,\n \tva_end(ap);\n }\n \n+static const char *describe_object(struct fsck_options *o, struct object *obj)\n+{\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\tchar *name;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addstr(&buf, oid_to_hex(&obj->oid));\n+\tif (o->object_names && (name = lookup_decoration(o->object_names, obj)))\n+\t\tstrbuf_addf(&buf, \" (%s)\", name);\n+\n+\treturn buf.buf;\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n@@ -358,7 +371,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op\n \t\t}\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n-\t\t\t\t\toid_to_hex(&tree->object.oid), entry.path, entry.mode);\n+\t\t\t\t\tdescribe_object(options, &tree->object), entry.path, entry.mode);\n \t\t}\n \t\tif (result < 0)\n \t\t\treturn result;\n@@ -454,7 +467,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n \tcase OBJ_TAG:\n \t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n \tdefault:\n-\t\terror(\"Unknown object type for %s\", oid_to_hex(&obj->oid));\n+\t\terror(\"Unknown object type for %s\", describe_object(options, obj));\n \t\treturn -1;\n \t}\n }\n@@ -901,9 +914,9 @@ int fsck_error_function(struct fsck_options *o,\n \tstruct object *obj, int msg_type, const char *message)\n {\n \tif (msg_type == FSCK_WARN) {\n-\t\twarning(\"object %s: %s\", oid_to_hex(&obj->oid), message);\n+\t\twarning(\"object %s: %s\", describe_object(o, obj), message);\n \t\treturn 0;\n \t}\n-\terror(\"object %s: %s\", oid_to_hex(&obj->oid), message);\n+\terror(\"object %s: %s\", describe_object(o, obj), message);\n \treturn 1;\n }\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 7ee8ea0..8f52da2 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -523,4 +523,26 @@ test_expect_success 'fsck --connectivity-only' '\n \t)\n '\n \n+remove_loose_object () {\n+\tsha1=\"$(git rev-parse \"$1\")\" &&\n+\tremainder=${sha1#??} &&\n+\tfirsttwo=${sha1%$remainder} &&\n+\trm .git/objects/$firsttwo/$remainder\n+}\n+\n+test_expect_success 'fsck --name-objects' '\n+\trm -rf name-objects &&\n+\tgit init name-objects &&\n+\t(\n+\t\tcd name-objects &&\n+\t\ttest_commit julius caesar.t &&\n+\t\ttest_commit augustus &&\n+\t\ttest_commit caesar &&\n+\t\tremove_loose_object $(git rev-parse julius:caesar.t) &&\n+\t\ttest_must_fail git fsck --name-objects >out &&\n+\t\ttree=$(git rev-parse --verify julius:) &&\n+\t\tgrep \"$tree (\\(refs/heads/master\\|HEAD\\)@{[0-9]*}:\" out\n+\t)\n+'\n+\n test_done\n-- \n2.9.0.281.g286a8d9\n"},{"id":"291625","messageId":"7056b23d0105b10efb0ea6d7fe168c72995baf15.1468752290.git.johannes.schindelin@gmx.de","threadId":"42845","inReplyTo":"cover.1468752290.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 1/4] fsck: refactor how to describe objects","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-07-17T10:59:44Z","receivedAt":"2016-07-17T11:00:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In many places, we refer to objects via their SHA-1s. Let's abstract\nthat into a function.\n\nFor the moment, it does nothing else than what we did previously: print\nout the 40-digit hex string. But that will change over the course of the\nnext patches.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c | 37 +++++++++++++++++++++++--------------\n 1 file changed, 23 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 3f27456..87df191 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -40,6 +40,11 @@ static int show_dangling = 1;\n #define ERROR_PACK 04\n #define ERROR_REFS 010\n \n+static const char *describe_object(struct object *obj)\n+{\n+\treturn oid_to_hex(&obj->oid);\n+}\n+\n static int fsck_config(const char *var, const char *value, void *cb)\n {\n \tif (strcmp(var, \"fsck.skiplist\") == 0) {\n@@ -67,7 +72,7 @@ static void objreport(struct object *obj, const char *msg_type,\n \t\t\tconst char *err)\n {\n \tfprintf(stderr, \"%s in %s %s: %s\\n\",\n-\t\tmsg_type, typename(obj->type), oid_to_hex(&obj->oid), err);\n+\t\tmsg_type, typename(obj->type), describe_object(obj), err);\n }\n \n static int objerror(struct object *obj, const char *err)\n@@ -97,7 +102,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt\n \tif (!obj) {\n \t\t/* ... these references to parent->fld are safe here */\n \t\tprintf(\"broken link from %7s %s\\n\",\n-\t\t\t   typename(parent->type), oid_to_hex(&parent->oid));\n+\t\t\t   typename(parent->type), describe_object(parent));\n \t\tprintf(\"broken link from %7s %s\\n\",\n \t\t\t   (type == OBJ_ANY ? \"unknown\" : typename(type)), \"unknown\");\n \t\terrors_found |= ERROR_REACHABLE;\n@@ -114,9 +119,9 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (parent && !has_object_file(&obj->oid)) {\n \t\t\tprintf(\"broken link from %7s %s\\n\",\n-\t\t\t\t typename(parent->type), oid_to_hex(&parent->oid));\n+\t\t\t\t typename(parent->type), describe_object(parent));\n \t\t\tprintf(\"              to %7s %s\\n\",\n-\t\t\t\t typename(obj->type), oid_to_hex(&obj->oid));\n+\t\t\t\t typename(obj->type), describe_object(obj));\n \t\t\terrors_found |= ERROR_REACHABLE;\n \t\t}\n \t\treturn 1;\n@@ -190,7 +195,8 @@ static void check_reachable_object(struct object *obj)\n \t\t\treturn; /* it is in pack - forget about it */\n \t\tif (connectivity_only && has_object_file(&obj->oid))\n \t\t\treturn;\n-\t\tprintf(\"missing %s %s\\n\", typename(obj->type), oid_to_hex(&obj->oid));\n+\t\tprintf(\"missing %s %s\\n\", typename(obj->type),\n+\t\t\tdescribe_object(obj));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n \t}\n@@ -215,7 +221,8 @@ static void check_unreachable_object(struct object *obj)\n \t * since this is something that is prunable.\n \t */\n \tif (show_unreachable) {\n-\t\tprintf(\"unreachable %s %s\\n\", typename(obj->type), oid_to_hex(&obj->oid));\n+\t\tprintf(\"unreachable %s %s\\n\", typename(obj->type),\n+\t\t\tdescribe_object(obj));\n \t\treturn;\n \t}\n \n@@ -234,11 +241,11 @@ static void check_unreachable_object(struct object *obj)\n \tif (!obj->used) {\n \t\tif (show_dangling)\n \t\t\tprintf(\"dangling %s %s\\n\", typename(obj->type),\n-\t\t\t       oid_to_hex(&obj->oid));\n+\t\t\t       describe_object(obj));\n \t\tif (write_lost_and_found) {\n \t\t\tchar *filename = git_pathdup(\"lost-found/%s/%s\",\n \t\t\t\tobj->type == OBJ_COMMIT ? \"commit\" : \"other\",\n-\t\t\t\toid_to_hex(&obj->oid));\n+\t\t\t\tdescribe_object(obj));\n \t\t\tFILE *f;\n \n \t\t\tif (safe_create_leading_directories_const(filename)) {\n@@ -252,7 +259,7 @@ static void check_unreachable_object(struct object *obj)\n \t\t\t\tif (stream_blob_to_fd(fileno(f), obj->oid.hash, NULL, 1))\n \t\t\t\t\tdie_errno(\"Could not write '%s'\", filename);\n \t\t\t} else\n-\t\t\t\tfprintf(f, \"%s\\n\", oid_to_hex(&obj->oid));\n+\t\t\t\tfprintf(f, \"%s\\n\", describe_object(obj));\n \t\t\tif (fclose(f))\n \t\t\t\tdie_errno(\"Could not finish '%s'\",\n \t\t\t\t\t  filename);\n@@ -271,7 +278,7 @@ static void check_unreachable_object(struct object *obj)\n static void check_object(struct object *obj)\n {\n \tif (verbose)\n-\t\tfprintf(stderr, \"Checking %s\\n\", oid_to_hex(&obj->oid));\n+\t\tfprintf(stderr, \"Checking %s\\n\", describe_object(obj));\n \n \tif (obj->flags & REACHABLE)\n \t\tcheck_reachable_object(obj);\n@@ -307,7 +314,7 @@ static int fsck_obj(struct object *obj)\n \n \tif (verbose)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n-\t\t\ttypename(obj->type), oid_to_hex(&obj->oid));\n+\t\t\ttypename(obj->type), describe_object(obj));\n \n \tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n@@ -326,15 +333,17 @@ static int fsck_obj(struct object *obj)\n \t\tfree_commit_buffer(commit);\n \n \t\tif (!commit->parents && show_root)\n-\t\t\tprintf(\"root %s\\n\", oid_to_hex(&commit->object.oid));\n+\t\t\tprintf(\"root %s\\n\", describe_object(&commit->object));\n \t}\n \n \tif (obj->type == OBJ_TAG) {\n \t\tstruct tag *tag = (struct tag *) obj;\n \n \t\tif (show_tags && tag->tagged) {\n-\t\t\tprintf(\"tagged %s %s\", typename(tag->tagged->type), oid_to_hex(&tag->tagged->oid));\n-\t\t\tprintf(\" (%s) in %s\\n\", tag->tag, oid_to_hex(&tag->object.oid));\n+\t\t\tprintf(\"tagged %s %s\", typename(tag->tagged->type),\n+\t\t\t\tdescribe_object(tag->tagged));\n+\t\t\tprintf(\" (%s) in %s\\n\", tag->tag,\n+\t\t\t\tdescribe_object(&tag->object));\n \t\t}\n \t}\n \n-- \n2.9.0.281.g286a8d9\n\n\n"},{"id":"291666","messageId":"xmqqinw2kcsz.fsf@gitster.mtv.corp.google.com","threadId":"42845","inReplyTo":"cover.1468752290.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 0/4] Teach `git fsck` a new option: `--name-objects`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-18T18:42:52Z","receivedAt":"2016-07-18T18:43:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n>  -static int fsck_error_func(struct object *obj, int type, const char *message)\n>  +static int fsck_error_func(struct fsck_options *o,\n>  +\tstruct object *obj, int type, const char *message)\n>   {\n>   \tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n>   \treturn (type == FSCK_WARN) ? 0 : 1;\n\nThat's a good change.\n"}]}