]> granicus.if.org Git - git/commitdiff
fsck_walk(): optionally name objects on the go
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Sun, 17 Jul 2016 10:59:49 +0000 (12:59 +0200)
committerJunio C Hamano <gitster@pobox.com>
Mon, 18 Jul 2016 18:35:00 +0000 (11:35 -0700)
If fsck_options->name_objects is initialized, and if it already has
name(s) for the object(s) that are to be the starting point(s) for
fsck_walk(), then that function will now add names for the objects
that were walked.

This will be highly useful for teaching git-fsck to identify root causes
for broken links, which is the task for the next patch in this series.

Note that this patch opts for decorating the objects with plain strings
instead of full-blown structs (à la `struct rev_name` in the code of
the `git name-rev` command), for several reasons:

- the code is much simpler than if it had to work with structs that
  describe arbitrarily long names such as "master~14^2~5:builtin/am.c",

- the string processing is actually quite light-weight compared to the
  rest of fsck's operation,

- the caller of fsck_walk() is expected to provide names for the
  starting points, and using plain and simple strings is just the
  easiest way to do that.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
fsck.c
fsck.h

diff --git a/fsck.c b/fsck.c
index 05315451c56fe19b2a2e8663d1f4847070bc946f..6ed90ec15edceb40c7e9ba5c8646d6e36052a4fe 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -9,6 +9,7 @@
 #include "refs.h"
 #include "utf8.h"
 #include "sha1-array.h"
+#include "decorate.h"
 
 #define FSCK_FATAL -1
 #define FSCK_INFO -2
@@ -297,25 +298,64 @@ static int report(struct fsck_options *options, struct object *object,
        return result;
 }
 
+static char *get_object_name(struct fsck_options *options, struct object *obj)
+{
+       if (!options->object_names)
+               return NULL;
+       return lookup_decoration(options->object_names, obj);
+}
+
+static void put_object_name(struct fsck_options *options, struct object *obj,
+       const char *fmt, ...)
+{
+       va_list ap;
+       struct strbuf buf = STRBUF_INIT;
+       char *existing;
+
+       if (!options->object_names)
+               return;
+       existing = lookup_decoration(options->object_names, obj);
+       if (existing)
+               return;
+       va_start(ap, fmt);
+       strbuf_vaddf(&buf, fmt, ap);
+       add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));
+       va_end(ap);
+}
+
 static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
 {
        struct tree_desc desc;
        struct name_entry entry;
        int res = 0;
+       const char *name;
 
        if (parse_tree(tree))
                return -1;
 
+       name = get_object_name(options, &tree->object);
        init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
+               struct object *obj;
                int result;
 
                if (S_ISGITLINK(entry.mode))
                        continue;
-               if (S_ISDIR(entry.mode))
-                       result = options->walk(&lookup_tree(entry.oid->hash)->object, OBJ_TREE, data, options);
-               else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
-                       result = options->walk(&lookup_blob(entry.oid->hash)->object, OBJ_BLOB, data, options);
+
+               if (S_ISDIR(entry.mode)) {
+                       obj = &lookup_tree(entry.oid->hash)->object;
+                       if (name)
+                               put_object_name(options, obj, "%s%s/", name,
+                                       entry.path);
+                       result = options->walk(obj, OBJ_TREE, data, options);
+               }
+               else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
+                       obj = &lookup_blob(entry.oid->hash)->object;
+                       if (name)
+                               put_object_name(options, obj, "%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",
                                        oid_to_hex(&tree->object.oid), entry.path, entry.mode);
@@ -330,20 +370,55 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
 
 static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)
 {
+       int counter = 0, generation = 0, name_prefix_len = 0;
        struct commit_list *parents;
        int res;
        int result;
+       const char *name;
 
        if (parse_commit(commit))
                return -1;
 
+       name = get_object_name(options, &commit->object);
+       if (name)
+               put_object_name(options, &commit->tree->object, "%s:", name);
+
        result = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);
        if (result < 0)
                return result;
        res = result;
 
        parents = commit->parents;
+       if (name && parents) {
+               int len = strlen(name), power;
+
+               if (len && name[len - 1] == '^') {
+                       generation = 1;
+                       name_prefix_len = len - 1;
+               }
+               else { /* parse ~<generation> suffix */
+                       for (generation = 0, power = 1;
+                            len && isdigit(name[len - 1]);
+                            power *= 10)
+                               generation += power * (name[--len] - '0');
+                       if (power > 1 && len && name[len - 1] == '~')
+                               name_prefix_len = len - 1;
+               }
+       }
+
        while (parents) {
+               if (name) {
+                       struct object *obj = &parents->item->object;
+
+                       if (++counter > 1)
+                               put_object_name(options, obj, "%s^%d",
+                                       name, counter);
+                       else if (generation > 0)
+                               put_object_name(options, obj, "%.*s~%d",
+                                       name_prefix_len, name, generation + 1);
+                       else
+                               put_object_name(options, obj, "%s^", name);
+               }
                result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
                if (result < 0)
                        return result;
@@ -356,8 +431,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);
+
        if (parse_tag(tag))
                return -1;
+       if (name)
+               put_object_name(options, tag->tagged, "%s", name);
        return options->walk(tag->tagged, OBJ_ANY, data, options);
 }
 
diff --git a/fsck.h b/fsck.h
index dded84b5f9fceda7b2e9b3f13f4ccf464f6c7116..26c0d41eabb521e6a43dd7d5fa98e924dceea99c 100644 (file)
--- a/fsck.h
+++ b/fsck.h
@@ -33,6 +33,7 @@ struct fsck_options {
        unsigned strict:1;
        int *msg_type;
        struct sha1_array *skiplist;
+       struct decoration *object_names;
 };
 
 #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }