fsck: offer a function to demote fsck errors to warnings
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Mon, 22 Jun 2015 15:25:25 +0000 (17:25 +0200)
committerJunio C Hamano <gitster@pobox.com>
Tue, 23 Jun 2015 21:26:46 +0000 (14:26 -0700)
There are legacy repositories out there whose older commits and tags
have issues that prevent pushing them when 'receive.fsckObjects' is set.
One real-life example is a commit object that has been hand-crafted to
list two authors.

Often, it is not possible to fix those issues without disrupting the
work with said repositories, yet it is still desirable to perform checks
by setting `receive.fsckObjects = true`. This commit is the first step
to allow demoting specific fsck issues to mere warnings.

The `fsck_set_msg_types()` function added by this commit parses a list
of settings in the form:

missingemail=warn,badname=warn,...

Unfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by
git fsck so far, but other call paths (e.g. git index-pack --strict)
error out *always* no matter what type was specified. Therefore, we need
to take extra care to set all message types to FSCK_ERROR by default in
those cases.

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 30c1a19aa583a4093fa6b9e2d2f6a6a7842e0b00..41208a412a3546079ef39b3803ca5694f2e3c811 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -108,13 +108,79 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,
 {
        int msg_type;
 
-       msg_type = msg_id_info[msg_id].msg_type;
-       if (options->strict && msg_type == FSCK_WARN)
-               msg_type = FSCK_ERROR;
+       assert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);
+
+       if (options->msg_type)
+               msg_type = options->msg_type[msg_id];
+       else {
+               msg_type = msg_id_info[msg_id].msg_type;
+               if (options->strict && msg_type == FSCK_WARN)
+                       msg_type = FSCK_ERROR;
+       }
 
        return msg_type;
 }
 
+static int parse_msg_type(const char *str)
+{
+       if (!strcmp(str, "error"))
+               return FSCK_ERROR;
+       else if (!strcmp(str, "warn"))
+               return FSCK_WARN;
+       else
+               die("Unknown fsck message type: '%s'", str);
+}
+
+void fsck_set_msg_type(struct fsck_options *options,
+               const char *msg_id, const char *msg_type)
+{
+       int id = parse_msg_id(msg_id), type;
+
+       if (id < 0)
+               die("Unhandled message id: %s", msg_id);
+       type = parse_msg_type(msg_type);
+
+       if (!options->msg_type) {
+               int i;
+               int *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);
+               for (i = 0; i < FSCK_MSG_MAX; i++)
+                       msg_type[i] = fsck_msg_type(i, options);
+               options->msg_type = msg_type;
+       }
+
+       options->msg_type[id] = type;
+}
+
+void fsck_set_msg_types(struct fsck_options *options, const char *values)
+{
+       char *buf = xstrdup(values), *to_free = buf;
+       int done = 0;
+
+       while (!done) {
+               int len = strcspn(buf, " ,|"), equal;
+
+               done = !buf[len];
+               if (!len) {
+                       buf++;
+                       continue;
+               }
+               buf[len] = '\0';
+
+               for (equal = 0;
+                    equal < len && buf[equal] != '=' && buf[equal] != ':';
+                    equal++)
+                       buf[equal] = tolower(buf[equal]);
+               buf[equal] = '\0';
+
+               if (equal == len)
+                       die("Missing '=': '%s'", buf);
+
+               fsck_set_msg_type(options, buf, buf + equal + 1);
+               buf += len + 1;
+       }
+       free(to_free);
+}
+
 __attribute__((format (printf, 4, 5)))
 static int report(struct fsck_options *options, struct object *object,
        enum fsck_msg_id id, const char *fmt, ...)
@@ -604,6 +670,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
 
 int fsck_error_function(struct object *obj, int msg_type, const char *message)
 {
+       if (msg_type == FSCK_WARN) {
+               warning("object %s: %s", sha1_to_hex(obj->sha1), message);
+               return 0;
+       }
        error("object %s: %s", sha1_to_hex(obj->sha1), message);
        return 1;
 }
diff --git a/fsck.h b/fsck.h
index f6f268aa72540a7cdc4e8efdb10b0fb61d6e2ca7..af3c84e8784d57ff68f13e2dbc8f19f940f1d9d4 100644 (file)
--- a/fsck.h
+++ b/fsck.h
@@ -6,6 +6,10 @@
 
 struct fsck_options;
 
+void fsck_set_msg_type(struct fsck_options *options,
+               const char *msg_id, const char *msg_type);
+void fsck_set_msg_types(struct fsck_options *options, const char *values);
+
 /*
  * callback function for fsck_walk
  * type is the expected type of the object or OBJ_ANY
@@ -25,10 +29,11 @@ struct fsck_options {
        fsck_walk_func walk;
        fsck_error error_func;
        unsigned strict:1;
+       int *msg_type;
 };
 
-#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }
-#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }
+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }
+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }
 
 /* descend in all linked child objects
  * the return value is: