{"thread":{"id":"38144","subject":"[PATCH 00/18] Introduce an internal API to interact with the fsck machinery","startedAt":"2014-12-08T16:13:55Z","lastAt":"2015-06-22T21:11:00Z","messageCount":275,"participants":["Johannes Schindelin","Junio C Hamano","Eric Sunshine","Michael Haggerty","Scott Schmit","Randall S. Becker"],"isPatch":true,"patchVersion":1,"patchTotal":18},"messages":[{"id":"253345","messageId":"cover.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":null,"subject":"[PATCH 00/18] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:13:55Z","receivedAt":"2014-12-08T16:13:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At the moment, the git-fsck's integrity checks are targeted toward the\nend user, i.e. the error messages are really just messages, intended for\nhuman consumption.\n\nUnder certain circumstances, some of those errors should be allowed to\nbe turned into mere warnings, though, because the cost of fixing the\nissues might well be larger than the cost of carrying those flawed\nobjects. For example, when an already-public repository contains a\ncommit object with two authors for years, it does not make sense to\nforce the maintainer to rewrite the history, affecting all contributors\nnegatively by forcing them to update.\n\nThis branch introduces an internal fsck API to be able to turn some of\nthe errors into warnings, and to make it easier to call the fsck\nmachinery from elsewhere in general.\n\nI am proud to report that this work has been sponsored by GitHub.\n\n\nJohannes Schindelin (18):\n  Introduce fsck options\n  Introduce identifiers for fsck messages\n  Provide a function to parse fsck message IDs\n  Offer a function to demote fsck errors to warnings\n  Allow demoting errors to warnings via receive.fsck.<key> = warn\n  fsck: report the ID of the error/warning\n  Make fsck_ident() warn-friendly\n  Make fsck_commit() warn-friendly\n  fsck: handle multiple authors in commits specially\n  Make fsck_tag() warn-friendly\n  Add a simple test for receive.fsck.*\n  Disallow demoting grave fsck errors to warnings\n  Optionally ignore specific fsck issues completely\n  fsck: allow upgrading fsck warnings to errors\n  Document the new receive.fsck.* options.\n  fsck: support demoting errors to warnings\n  Introduce `git fsck --quick`\n  git receive-pack: support excluding objects from fsck'ing\n\n Documentation/config.txt        |  27 +++\n Documentation/git-fsck.txt      |   7 +-\n builtin/fsck.c                  |  66 ++++--\n builtin/index-pack.c            |  13 +-\n builtin/receive-pack.c          |  36 ++-\n builtin/unpack-objects.c        |  16 +-\n fsck.c                          | 512 +++++++++++++++++++++++++++++++---------\n fsck.h                          |  28 ++-\n t/t1450-fsck.sh                 |  33 +++\n t/t5302-pack-index.sh           |   2 +-\n t/t5504-fetch-receive-strict.sh |  46 ++++\n 11 files changed, 624 insertions(+), 162 deletions(-)\n\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253348","messageId":"56e9445d6e8a746356146bc565512f53bf8a0c8d.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 01/18] Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:14:04Z","receivedAt":"2014-12-08T16:14:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Just like the diff machinery, we are about to introduce more settings,\ntherefore it makes sense to carry them around as a (pointer to a) struct\ncontaining all of them.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c           |  20 +++++--\n builtin/index-pack.c     |   9 +--\n builtin/unpack-objects.c |  11 ++--\n fsck.c                   | 150 +++++++++++++++++++++++------------------------\n fsck.h                   |  17 +++++-\n 5 files changed, 114 insertions(+), 93 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex a27515a..2241e29 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -25,6 +25,8 @@ static int include_reflogs = 1;\n static int check_full = 1;\n static int check_strict;\n static int keep_cache_objects;\n+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n static unsigned char head_sha1[20];\n static const char *head_points_at;\n static int errors_found;\n@@ -76,7 +78,7 @@ static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n \n static struct object_array pending;\n \n-static int mark_object(struct object *obj, int type, void *data)\n+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct object *parent = data;\n \n@@ -119,7 +121,7 @@ static int mark_object(struct object *obj, int type, void *data)\n \n static void mark_object_reachable(struct object *obj)\n {\n-\tmark_object(obj, OBJ_ANY, NULL);\n+\tmark_object(obj, OBJ_ANY, NULL, NULL);\n }\n \n static int traverse_one_object(struct object *obj)\n@@ -132,7 +134,7 @@ static int traverse_one_object(struct object *obj)\n \t\tif (parse_tree(tree) < 0)\n \t\t\treturn 1; /* error already displayed */\n \t}\n-\tresult = fsck_walk(obj, mark_object, obj);\n+\tresult = fsck_walk(obj, obj, &fsck_walk_options);\n \tif (tree)\n \t\tfree_tree_buffer(tree);\n \treturn result;\n@@ -158,7 +160,7 @@ static int traverse_reachable(void)\n \treturn !!result;\n }\n \n-static int mark_used(struct object *obj, int type, void *data)\n+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn 1;\n@@ -296,9 +298,9 @@ static int fsck_obj(struct object *obj)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n \t\t\ttypename(obj->type), sha1_to_hex(obj->sha1));\n \n-\tif (fsck_walk(obj, mark_used, NULL))\n+\tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n-\tif (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))\n+\tif (fsck_object(obj, NULL, 0, &fsck_obj_options))\n \t\treturn -1;\n \n \tif (obj->type == OBJ_TREE) {\n@@ -630,6 +632,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);\n \n+\tfsck_walk_options.walk = mark_object;\n+\tfsck_obj_options.walk = mark_used;\n+\tfsck_obj_options.error_func = fsck_error_func;\n+\tif (check_strict)\n+\t\tfsck_obj_options.strict = 1;\n+\n \tif (show_progress == -1)\n \t\tshow_progress = isatty(2);\n \tif (verbose)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex a369f55..1c17c3f 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -74,6 +74,7 @@ static int nr_threads;\n static int from_stdin;\n static int strict;\n static int do_fsck_object;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n static int verbose;\n static int show_stat;\n static int check_self_contained_and_connected;\n@@ -191,7 +192,7 @@ static void cleanup_thread(void)\n #endif\n \n \n-static int mark_link(struct object *obj, int type, void *data)\n+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -782,10 +783,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (!obj)\n \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(obj, buf, size, 1,\n-\t\t\t\t    fsck_error_function))\n+\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"Error in object\"));\n-\t\t\tif (fsck_walk(obj, mark_link, NULL))\n+\t\t\tfsck_options.walk = mark_link;\n+\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n \n \t\t\tif (obj->type == OBJ_TREE) {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 855d94b..e9e8bec 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -20,6 +20,7 @@ static unsigned char buffer[4096];\n static unsigned int offset, len;\n static off_t consumed_bytes;\n static git_SHA_CTX ctx;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n /*\n  * When running under --strict mode, objects whose reachability are\n@@ -178,7 +179,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)\n  * that have reachability requirements and calls this function.\n  * Verify its reachability and validity recursively and write it out.\n  */\n-static int check_object(struct object *obj, int type, void *data)\n+static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct obj_buffer *obj_buf;\n \n@@ -203,10 +204,10 @@ static int check_object(struct object *obj, int type, void *data)\n \tobj_buf = lookup_object_buffer(obj);\n \tif (!obj_buf)\n \t\tdie(\"Whoops! Cannot find object '%s'\", sha1_to_hex(obj->sha1));\n-\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1,\n-\t\t\tfsck_error_function))\n+\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"Error in object\");\n-\tif (fsck_walk(obj, check_object, NULL))\n+\tfsck_options.walk = check_object;\n+\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\tdie(\"Error on reachable objects of %s\", sha1_to_hex(obj->sha1));\n \twrite_cached_object(obj, obj_buf);\n \treturn 0;\n@@ -217,7 +218,7 @@ static void write_rest(void)\n \tunsigned i;\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tif (obj_list[i].obj)\n-\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL);\n+\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);\n \t}\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex 2fffa43..d6f539f 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -8,7 +8,7 @@\n #include \"fsck.h\"\n #include \"refs.h\"\n \n-static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\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@@ -24,9 +24,9 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n \t\tif (S_ISDIR(entry.mode))\n-\t\t\tresult = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);\n+\t\t\tresult = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);\n \t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n-\t\t\tresult = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);\n+\t\t\tresult = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n \t\t\t\t\tsha1_to_hex(tree->object.sha1), entry.path, entry.mode);\n@@ -39,7 +39,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \treturn res;\n }\n \n-static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)\n+static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n \tstruct commit_list *parents;\n \tint res;\n@@ -48,14 +48,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \tif (parse_commit(commit))\n \t\treturn -1;\n \n-\tresult = walk((struct object *)commit->tree, OBJ_TREE, data);\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 \twhile (parents) {\n-\t\tresult = walk((struct object *)parents->item, OBJ_COMMIT, data);\n+\t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n@@ -65,14 +65,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \treturn res;\n }\n \n-static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)\n+static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n \tif (parse_tag(tag))\n \t\treturn -1;\n-\treturn walk(tag->tagged, OBJ_ANY, data);\n+\treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -80,11 +80,11 @@ int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n \tcase OBJ_BLOB:\n \t\treturn 0;\n \tcase OBJ_TREE:\n-\t\treturn fsck_walk_tree((struct tree *)obj, walk, data);\n+\t\treturn fsck_walk_tree((struct tree *)obj, data, options);\n \tcase OBJ_COMMIT:\n-\t\treturn fsck_walk_commit((struct commit *)obj, walk, data);\n+\t\treturn fsck_walk_commit((struct commit *)obj, data, options);\n \tcase OBJ_TAG:\n-\t\treturn fsck_walk_tag((struct tag *)obj, walk, data);\n+\t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n \tdefault:\n \t\terror(\"Unknown object type for %s\", sha1_to_hex(obj->sha1));\n \t\treturn -1;\n@@ -137,7 +137,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con\n \treturn c1 < c2 ? 0 : TREE_UNORDERED;\n }\n \n-static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n+static int fsck_tree(struct tree *item, struct fsck_options *options)\n {\n \tint retval;\n \tint has_null_sha1 = 0;\n@@ -191,7 +191,7 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \t\t * bits..\n \t\t */\n \t\tcase S_IFREG | 0664:\n-\t\t\tif (!strict)\n+\t\t\tif (!options->strict)\n \t\t\t\tbreak;\n \t\tdefault:\n \t\t\thas_bad_modes = 1;\n@@ -216,30 +216,30 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n \treturn retval;\n }\n \n static int require_end_of_header(const void *data, unsigned long size,\n-\tstruct object *obj, fsck_error error_func)\n+\tstruct object *obj, struct fsck_options *options)\n {\n \tconst char *buffer = (const char *)data;\n \tunsigned long i;\n@@ -247,7 +247,7 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn error_func(obj, FSCK_ERROR,\n+\t\t\treturn options->error_func(obj, FSCK_ERROR,\n \t\t\t\t\"unterminated header: NUL at offset %d\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n@@ -255,36 +255,36 @@ static int require_end_of_header(const void *data, unsigned long size,\n \t\t}\n \t}\n \n-\treturn error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n }\n \n-static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)\n+static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -292,30 +292,30 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n \n static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n \tunsigned parent_count, parent_line_count = 0;\n \tint err;\n \n-\tif (require_end_of_header(buffer, size, &commit->object, error_func))\n+\tif (require_end_of_header(buffer, size, &commit->object, options))\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -325,39 +325,39 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n \n static int fsck_commit(struct commit *commit, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tconst char *buffer = data ?  data : get_commit_buffer(commit, &size);\n-\tint ret = fsck_commit_buffer(commit, buffer, size, error_func);\n+\tint ret = fsck_commit_buffer(commit, buffer, size, options);\n \tif (!data)\n \t\tunuse_commit_buffer(commit, buffer);\n \treturn ret;\n }\n \n static int fsck_tag_buffer(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char sha1[20];\n \tint ret = 0;\n@@ -373,64 +373,64 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n \t\t}\n \t}\n \n-\tif (require_end_of_header(buffer, size, &tag->object, error_func))\n+\tif (require_end_of_header(buffer, size, &tag->object, options))\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %s\", buffer);\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %s\", buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n \telse\n-\t\tret = fsck_ident(&buffer, &tag->object, error_func);\n+\t\tret = fsck_ident(&buffer, &tag->object, options);\n \n done:\n \tstrbuf_release(&sb);\n@@ -439,34 +439,34 @@ done:\n }\n \n static int fsck_tag(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n \n-\treturn fsck_tag_buffer(tag, data, size, error_func);\n+\treturn fsck_tag_buffer(tag, data, size, options);\n }\n \n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func)\n+\tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n \tif (obj->type == OBJ_TREE)\n-\t\treturn fsck_tree((struct tree *) obj, strict, error_func);\n+\t\treturn fsck_tree((struct tree *) obj, options);\n \tif (obj->type == OBJ_COMMIT)\n \t\treturn fsck_commit((struct commit *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \tif (obj->type == OBJ_TAG)\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \n-\treturn error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex d1e6387..84a337c 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -4,6 +4,8 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n \n+struct fsck_options;\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -12,7 +14,7 @@\n  *     <0\terror signaled and abort\n  *     >0\terror signaled and do not abort\n  */\n-typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);\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 *err, ...);\n@@ -20,6 +22,15 @@ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);\n __attribute__((format (printf, 3, 4)))\n int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n \n+struct fsck_options {\n+\tfsck_walk_func walk;\n+\tfsck_error error_func;\n+\tint strict:1;\n+};\n+\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+\n /* descend in all linked child objects\n  * the return value is:\n  *    -1\terror in processing the object\n@@ -27,9 +38,9 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n  *    >0\treturn value of the first signaled error >0 (in the case of no other errors)\n  *    0\t\teverything OK\n  */\n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n /* If NULL is passed for data, we assume the object is local and read it. */\n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func);\n+\tstruct fsck_options *options);\n \n #endif\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253346","messageId":"e439c3a0607069fc5b96569090dcb8787a51e28d.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 02/18] Introduce identifiers for fsck messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:14:13Z","receivedAt":"2014-12-08T16:14:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Rather than specifying only whether a message by the fsck machinery\nconstitutes an error or a warning, let's specify an identifier relating\nto the concrete problem that was encountered. This is necessary for\nupcoming support to be able to demote certain errors to warnings.\n\nIn the course, simplify the requirements on the calling code: instead of\nhaving to handle full-blown varargs in every callback, we now send a\nstring buffer ready to be used by the callback.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c |  24 +++-----\n fsck.c         | 185 +++++++++++++++++++++++++++++++++++++++------------------\n fsck.h         |   5 +-\n 3 files changed, 137 insertions(+), 77 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2241e29..99d4538 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -47,32 +47,22 @@ static int show_dangling = 1;\n #endif\n \n static void objreport(struct object *obj, const char *severity,\n-                      const char *err, va_list params)\n+                      const char *err)\n {\n-\tfprintf(stderr, \"%s in %s %s: \",\n-\t        severity, typename(obj->type), sha1_to_hex(obj->sha1));\n-\tvfprintf(stderr, err, params);\n-\tfputs(\"\\n\", stderr);\n+\tfprintf(stderr, \"%s in %s %s: %s\\n\",\n+\t        severity, typename(obj->type), sha1_to_hex(obj->sha1), err);\n }\n \n-__attribute__((format (printf, 2, 3)))\n-static int objerror(struct object *obj, const char *err, ...)\n+static int objerror(struct object *obj, const char *err)\n {\n-\tva_list params;\n-\tva_start(params, err);\n \terrors_found |= ERROR_OBJECT;\n-\tobjreport(obj, \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, \"error\", err);\n \treturn -1;\n }\n \n-__attribute__((format (printf, 3, 4)))\n-static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n+static int fsck_error_func(struct object *obj, int type, const char *message)\n {\n-\tva_list params;\n-\tva_start(params, err);\n-\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n \treturn (type == FSCK_WARN) ? 0 : 1;\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex d6f539f..3cea034 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -8,6 +8,83 @@\n #include \"fsck.h\"\n #include \"refs.h\"\n \n+#define FOREACH_MSG_ID(FUNC) \\\n+\t/* errors */ \\\n+\tFUNC(BAD_DATE) \\\n+\tFUNC(BAD_EMAIL) \\\n+\tFUNC(BAD_NAME) \\\n+\tFUNC(BAD_PARENT_SHA1) \\\n+\tFUNC(BAD_TIMEZONE) \\\n+\tFUNC(BAD_TREE_SHA1) \\\n+\tFUNC(DATE_OVERFLOW) \\\n+\tFUNC(DUPLICATE_ENTRIES) \\\n+\tFUNC(INVALID_OBJECT_SHA1) \\\n+\tFUNC(INVALID_TAG_OBJECT) \\\n+\tFUNC(INVALID_TREE) \\\n+\tFUNC(INVALID_TYPE) \\\n+\tFUNC(MISSING_AUTHOR) \\\n+\tFUNC(MISSING_COMMITTER) \\\n+\tFUNC(MISSING_EMAIL) \\\n+\tFUNC(MISSING_GRAFT) \\\n+\tFUNC(MISSING_NAME_BEFORE_EMAIL) \\\n+\tFUNC(MISSING_OBJECT) \\\n+\tFUNC(MISSING_PARENT) \\\n+\tFUNC(MISSING_SPACE_BEFORE_DATE) \\\n+\tFUNC(MISSING_SPACE_BEFORE_EMAIL) \\\n+\tFUNC(MISSING_TAG) \\\n+\tFUNC(MISSING_TAG_ENTRY) \\\n+\tFUNC(MISSING_TAG_OBJECT) \\\n+\tFUNC(MISSING_TREE) \\\n+\tFUNC(MISSING_TYPE) \\\n+\tFUNC(MISSING_TYPE_ENTRY) \\\n+\tFUNC(NOT_SORTED) \\\n+\tFUNC(NUL_IN_HEADER) \\\n+\tFUNC(TAG_OBJECT_NOT_TAG) \\\n+\tFUNC(UNKNOWN_TYPE) \\\n+\tFUNC(UNTERMINATED_HEADER) \\\n+\tFUNC(ZERO_PADDED_DATE) \\\n+\t/* warnings */ \\\n+\tFUNC(BAD_FILEMODE) \\\n+\tFUNC(EMPTY_NAME) \\\n+\tFUNC(FULL_PATHNAME) \\\n+\tFUNC(HAS_DOT) \\\n+\tFUNC(HAS_DOTDOT) \\\n+\tFUNC(HAS_DOTGIT) \\\n+\tFUNC(INVALID_TAG_NAME) \\\n+\tFUNC(MISSING_TAGGER_ENTRY) \\\n+\tFUNC(NULL_SHA1) \\\n+\tFUNC(ZERO_PADDED_FILEMODE)\n+\n+#define FIRST_WARNING FSCK_MSG_BAD_FILEMODE\n+\n+#define MSG_ID(x) FSCK_MSG_##x,\n+enum fsck_msg_id {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\tFSCK_MSG_MAX\n+};\n+\n+int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n+{\n+\treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n+}\n+\n+__attribute__((format (printf, 4, 5)))\n+static int report(struct fsck_options *options, struct object *object,\n+\tenum fsck_msg_id id, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint msg_type = fsck_msg_type(id, options), result;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tresult = options->error_func(object, msg_type, sb.buf);\n+\tstrbuf_release(&sb);\n+\tva_end(ap);\n+\n+\treturn result;\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n@@ -216,25 +293,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NULL_SHA1, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOT, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NOT_SORTED, \"not properly sorted\");\n \treturn retval;\n }\n \n@@ -247,15 +324,17 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn options->error_func(obj, FSCK_ERROR,\n-\t\t\t\t\"unterminated header: NUL at offset %d\", i);\n+\t\t\treturn report(options, obj,\n+\t\t\t\tFSCK_MSG_NUL_IN_HEADER,\n+\t\t\t\t\"unterminated header: NUL at offset %ld\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n \t\t\t\treturn 0;\n \t\t}\n \t}\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn report(options, obj,\n+\t\tFSCK_MSG_UNTERMINATED_HEADER, \"unterminated header\");\n }\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n@@ -263,28 +342,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -292,7 +371,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n@@ -309,13 +388,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -325,23 +404,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn report(options, &commit->object, FSCK_MSG_INVALID_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n@@ -373,11 +452,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_MISSING_TAG_OBJECT,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_TAG_OBJECT_NOT_TAG,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n@@ -388,47 +469,47 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %s\", buffer);\n+\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME, \"invalid 'tag' name: %s\", buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \n@@ -444,7 +525,7 @@ static int fsck_tag(struct tag *tag, const char *data,\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn report(options, &tag->object, FSCK_MSG_INVALID_TAG_OBJECT, \"could not load tagged object\");\n \n \treturn fsck_tag_buffer(tag, data, size, options);\n }\n@@ -453,7 +534,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n@@ -466,22 +547,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n \t\t\tsize, options);\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn report(options, obj, FSCK_MSG_UNKNOWN_TYPE, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...)\n+int fsck_error_function(struct object *obj, int type, const char *message)\n {\n-\tva_list ap;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tstrbuf_addf(&sb, \"object %s:\", sha1_to_hex(obj->sha1));\n-\n-\tva_start(ap, fmt);\n-\tstrbuf_vaddf(&sb, fmt, ap);\n-\tva_end(ap);\n-\n-\terror(\"%s\", sb.buf);\n-\tstrbuf_release(&sb);\n+\terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex 84a337c..a18e9a6 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -17,10 +17,9 @@ struct fsck_options;\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 *err, ...);\n+typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n \n-__attribute__((format (printf, 3, 4)))\n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n+int fsck_error_function(struct object *obj, int type, const char *message);\n \n struct fsck_options {\n \tfsck_walk_func walk;\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253347","messageId":"e217caa9c30b72004eca9b15be3aa1c6674dc278.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 03/18] Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:14:24Z","receivedAt":"2014-12-08T16:14:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This function will be used in the next commits to allow the user to\nask fsck to handle specific problems differently, e.g. demoting certain\nerrors to warnings. It has to handle partial strings because we would\nlike to be able to parse, say, '--strict=missing-email=warn' command\nlines.\n\nTo make the parsing robust, we generate strings from the enum keys, and we\nwill match both lower-case, dash-separated values as well as camelCased\nones (e.g. both \"missing-email\" and \"missingEmail\" will match the\n\"MISSING_EMAIL\" key).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 32 ++++++++++++++++++++++++++++++++\n 1 file changed, 32 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex 3cea034..05b146c 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -63,6 +63,38 @@ enum fsck_msg_id {\n \tFSCK_MSG_MAX\n };\n \n+#define STR(x) #x\n+#define MSG_ID_STR(x) STR(x),\n+static const char *msg_id_str[FSCK_MSG_MAX + 1] = {\n+\tFOREACH_MSG_ID(MSG_ID_STR)\n+\tNULL\n+};\n+\n+static int parse_msg_id(const char *text, int len)\n+{\n+\tint i, j;\n+\n+\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n+\t\tconst char *key = msg_id_str[i];\n+\t\t/* msg_id_str is upper-case, with underscores */\n+\t\tfor (j = 0; j < len; j++) {\n+\t\t\tchar c = *(key++);\n+\t\t\tif (c == '_') {\n+\t\t\t\tif (isalpha(text[j]))\n+\t\t\t\t\tc = *(key++);\n+\t\t\t\telse if (text[j] != '_')\n+\t\t\t\t\tc = '-';\n+\t\t\t}\n+\t\t\tif (toupper(text[j]) != c)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (j == len && !*key)\n+\t\t\treturn i;\n+\t}\n+\n+\tdie(\"Unhandled type: %.*s\", len, text);\n+}\n+\n int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n {\n \treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253356","messageId":"2a0c4cd4c5d3aaceff8a6ffa49d2f3597d26086d.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:14:30Z","receivedAt":"2014-12-08T16:14:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are legacy repositories out there whose older commits and tags\nhave issues that prevent pushing them when 'receive.fsckObjects' is set.\nOne real-life example is a commit object that has been hand-crafted to\nlist two authors.\n\nOften, it is not possible to fix those issues without disrupting the\nwork with said repositories, yet it is still desirable to perform checks\nby setting `receive.fsckObjects = true`. This commit is the first step\nto allow demoting specific fsck issues to mere warnings.\n\nThe function added by this commit parses a list of settings in the form:\n\n\tmissing-email=warn,bad-name=warn,...\n\nUnfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\ngit fsck so far, but other call paths (e.g. git index-pack --strict)\nerror out *always* no matter what type was specified. Therefore, we\nneed to take extra care to default to all FSCK_ERROR in those cases.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n fsck.h |  7 +++++--\n 2 files changed, 63 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 05b146c..9e6d70f 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -97,9 +97,63 @@ static int parse_msg_id(const char *text, int len)\n \n int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n {\n+\tif (options->strict_mode && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n+\t\treturn options->strict_mode[msg_id];\n+\tif (options->strict)\n+\t\treturn FSCK_ERROR;\n \treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n }\n \n+static inline int substrcmp(const char *string, int len, const char *match)\n+{\n+\tint match_len = strlen(match);\n+\tif (match_len != len)\n+\t\treturn -1;\n+\treturn memcmp(string, match, len);\n+}\n+\n+void fsck_strict_mode(struct fsck_options *options, const char *mode)\n+{\n+\tint type = FSCK_ERROR;\n+\n+\tif (!options->strict_mode) {\n+\t\tint i;\n+\t\tint *strict_mode = malloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\t\tstrict_mode[i] = fsck_msg_type(i, options);\n+\t\toptions->strict_mode = strict_mode;\n+\t}\n+\n+\twhile (*mode) {\n+\t\tint len = strcspn(mode, \" ,|\"), equal, msg_id;\n+\n+\t\tif (!len) {\n+\t\t\tmode++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (equal = 0; equal < len; equal++)\n+\t\t\tif (mode[equal] == '=')\n+\t\t\t\tbreak;\n+\n+\t\tif (equal < len) {\n+\t\t\tconst char *type_str = mode + equal + 1;\n+\t\t\tint type_len = len - equal - 1;\n+\t\t\tif (!substrcmp(type_str, type_len, \"error\"))\n+\t\t\t\ttype = FSCK_ERROR;\n+\t\t\telse if (!substrcmp(type_str, type_len, \"warn\"))\n+\t\t\t\ttype = FSCK_WARN;\n+\t\t\telse\n+\t\t\t\tdie(\"Unknown fsck message type: '%.*s'\",\n+\t\t\t\t\tlen - equal - 1, type_str);\n+\t\t}\n+\n+\t\tmsg_id = parse_msg_id(mode, equal);\n+\t\toptions->strict_mode[msg_id] = type;\n+\t\tmode += len;\n+\t}\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -585,6 +639,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \n int fsck_error_function(struct object *obj, int type, const char *message)\n {\n+\tif (type == FSCK_WARN) {\n+\t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n+\t\treturn 0;\n+\t}\n \terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex a18e9a6..9d67ea2 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,8 @@\n \n struct fsck_options;\n \n+void fsck_strict_mode(struct fsck_options *options, const char *mode);\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -25,10 +27,11 @@ struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n \tint strict:1;\n+\tint *strict_mode;\n };\n \n-#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n-#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }\n \n /* descend in all linked child objects\n  * the return value is:\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253349","messageId":"d81653ce9ef73a54a71613c3e9828ad21f2391d7.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 05/18] Allow demoting errors to warnings via receive.fsck.<key> = warn","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:14:36Z","receivedAt":"2014-12-08T16:14:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For example, missing emails in commit and tag objects can be demoted to\nmere warnings with\n\n\tgit config receive.fsck.missing-email warn\n\nAs git receive-pack does not actually perform the checks, it hands off\nthe setting to index-pack or unpack-objects in the form of an optional\nargument to the --strict option.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/index-pack.c     |  4 ++++\n builtin/receive-pack.c   | 27 +++++++++++++++++++++++----\n builtin/unpack-objects.c |  5 +++++\n 3 files changed, 32 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 1c17c3f..34a11b3 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1565,6 +1565,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(arg, \"--strict\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n+\t\t\t} else if (starts_with(arg, \"--strict=\")) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tdo_fsck_object = 1;\n+\t\t\t\tfsck_strict_mode(&fsck_options, arg + 9);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex e908d07..111e514 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -35,6 +35,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n+static struct strbuf fsck_strict_mode = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int unpack_limit = 100;\n@@ -109,6 +110,14 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (starts_with(var, \"receive.fsck.\")) {\n+\t\tif (fsck_strict_mode.len)\n+\t\t\tstrbuf_addch(&fsck_strict_mode, ',');\n+\t\tstrbuf_addf(&fsck_strict_mode,\n+\t\t\t\"%s=%s\", var + 13, value ? value : \"error\");\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsckobjects\") == 0) {\n \t\treceive_fsck_objects = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1266,8 +1275,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"unpack-objects\", hdr_arg, NULL);\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n-\t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\tif (fsck_objects) {\n+\t\t\tif (fsck_strict_mode.len)\n+\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n+\t\t\t\t\tfsck_strict_mode.buf);\n+\t\t\telse\n+\t\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t}\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1284,8 +1298,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n-\t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\tif (fsck_objects) {\n+\t\t\tif (fsck_strict_mode.len)\n+\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n+\t\t\t\t\tfsck_strict_mode.buf);\n+\t\t\telse\n+\t\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t}\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex e9e8bec..916616f 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrict = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (starts_with(arg, \"--strict=\")) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tfsck_strict_mode(&fsck_options, arg + 9);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\n \t\t\t\tstruct pack_header *hdr;\n \t\t\t\tchar *c;\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253350","messageId":"2bc462ca50abdc07a89d1a8c58ac1633748b3b91.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 06/18] fsck: report the ID of the error/warning","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:14:42Z","receivedAt":"2014-12-08T16:14:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some legacy code has objects with non-fatal fsck issues; To enable the\nuser to ignore those issues, let's print out the ID (e.g. when\nencountering \"missing-email\", the user might want to call `git config\nreceive.fsck.missing-email warn`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 19 +++++++++++++++++++\n 1 file changed, 19 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex 9e6d70f..ff50a87 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -154,6 +154,23 @@ void fsck_strict_mode(struct fsck_options *options, const char *mode)\n \t}\n }\n \n+static void append_msg_id(struct strbuf *sb, const char *msg_id)\n+{\n+\tfor (;;) {\n+\t\tchar c = *(msg_id)++;\n+\n+\t\tif (!c)\n+\t\t\tbreak;\n+\t\tif (c == '_')\n+\t\t\tc = '-';\n+\t\telse\n+\t\t\tc = tolower(c);\n+\t\tstrbuf_addch(sb, c);\n+\t}\n+\n+\tstrbuf_addstr(sb, \": \");\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -162,6 +179,8 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tappend_msg_id(&sb, msg_id_str[id]);\n+\n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n \tresult = options->error_func(object, msg_type, sb.buf);\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253351","messageId":"6d500fe557df189b419b846cb911205915b31222.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 07/18] Make fsck_ident() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:14:49Z","receivedAt":"2014-12-08T16:14:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_ident() identifies a problem with the ident, it should still\nadvance the pointer to the next line so that fsck can continue in the\ncase of a mere warning.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 49 +++++++++++++++++++++++++++----------------------\n 1 file changed, 27 insertions(+), 22 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex ff50a87..256f567 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -444,40 +444,45 @@ static int require_end_of_header(const void *data, unsigned long size,\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n+\tconst char *p = *ident;\n \tchar *end;\n \n-\tif (**ident == '<')\n+\t*ident = strchrnul(*ident, '\\n');\n+\tif (**ident == '\\n')\n+\t\t(*ident)++;\n+\n+\tif (*p == '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident == '>')\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p == '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n-\tif (**ident != '<')\n+\tif (*p != '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n-\tif ((*ident)[-1] != ' ')\n+\tif (p[-1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t(*ident)++;\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident != '>')\n+\tp++;\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p != '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n-\t(*ident)++;\n-\tif (**ident != ' ')\n+\tp++;\n+\tif (*p != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n-\t(*ident)++;\n-\tif (**ident == '0' && (*ident)[1] != ' ')\n+\tp++;\n+\tif (*p == '0' && p[1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n-\tif (date_overflows(strtoul(*ident, &end, 10)))\n+\tif (date_overflows(strtoul(p, &end, 10)))\n \t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n-\tif (end == *ident || *end != ' ')\n+\tif ((end == p || *end != ' '))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n-\t*ident = end + 1;\n-\tif ((**ident != '+' && **ident != '-') ||\n-\t    !isdigit((*ident)[1]) ||\n-\t    !isdigit((*ident)[2]) ||\n-\t    !isdigit((*ident)[3]) ||\n-\t    !isdigit((*ident)[4]) ||\n-\t    ((*ident)[5] != '\\n'))\n+\tp = end + 1;\n+\tif ((*p != '+' && *p != '-') ||\n+\t    !isdigit(p[1]) ||\n+\t    !isdigit(p[2]) ||\n+\t    !isdigit(p[3]) ||\n+\t    !isdigit(p[4]) ||\n+\t    (p[5] != '\\n'))\n \t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n-\t(*ident) += 6;\n+\tp += 6;\n \treturn 0;\n }\n \n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253353","messageId":"eb66b08bc0092431021fc594b4ff3108dc3b5246.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 08/18] Make fsck_commit() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:14:54Z","receivedAt":"2014-12-08T16:14:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_commit() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nNote that some problems are too problematic to simply ignore. For\nexample, when the header lines are mixed up, we punt after encountering\nan incorrect line. Therefore, demoting certain warnings to errors can\nhide other problems. Example: demoting the missing-author error to\na warning would hide a problematic committer line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 28 ++++++++++++++++++++--------\n 1 file changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 256f567..a63654c 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -499,12 +499,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n-\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n+\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n-\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -513,11 +519,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \tif (graft) {\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n-\t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\telse if (graft->nr_parent != parent_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t} else {\n-\t\tif (parent_count != parent_line_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\tif (parent_count != parent_line_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253352","messageId":"43faa41d4cc98d6c40a393ec590af73ec5c94246.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 09/18] fsck: handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:00Z","receivedAt":"2014-12-08T16:15:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This problem has been detected in the wild, and is the primary reason\nto introduce an option to demote certain fsck errors to warnings. Let's\noffer to ignore this particular problem specifically.\n\nTechnically, we could handle such repositories by setting\nmissing-committer = warn, but that could hide missing tree objects in the\nsame commit because we cannot continue verifying any commit object after\nencountering a missing committer line, while we can continue in the case\nof multiple author lines.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex a63654c..21ff35b 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -37,6 +37,7 @@\n \tFUNC(MISSING_TREE) \\\n \tFUNC(MISSING_TYPE) \\\n \tFUNC(MISSING_TYPE_ENTRY) \\\n+\tFUNC(MULTIPLE_AUTHORS) \\\n \tFUNC(NOT_SORTED) \\\n \tFUNC(NUL_IN_HEADER) \\\n \tFUNC(TAG_OBJECT_NOT_TAG) \\\n@@ -536,6 +537,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n+\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n+\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\t/* require_end_of_header() ensured that there is a newline */\n+\t\tbuffer = strchr(buffer, '\\n') + 1;\n+\t}\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253354","messageId":"1654f9b634e4e21191c92199563dc42faf2e3d40.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 10/18] Make fsck_tag() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:06Z","receivedAt":"2014-12-08T16:15:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_tag() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nJust like fsck_commit(), there are certain problems that could hide other\nissues with the same tag object. For example, if the 'type' line is not\nencountered in the correct position, the 'tag' line – if there is any –\nwould not be handled at all.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 21ff35b..c1e7a85 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -604,7 +604,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n \t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n-\t\tgoto done;\n+\t\tif (ret)\n+\t\t\tgoto done;\n \t}\n \tbuffer += 41;\n \n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253357","messageId":"243e5190b58d0cceeb1e98735253d6506f18ba45.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 11/18] Add a simple test for receive.fsck.*","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:11Z","receivedAt":"2014-12-08T16:15:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5504-fetch-receive-strict.sh | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 69ee13c..db79e56 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -115,4 +115,24 @@ test_expect_success 'push with transfer.fsckobjects' '\n \ttest_cmp exp act\n '\n \n+cat >bogus-commit << EOF\n+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+author Bugs Bunny 1234567890 +0000\n+committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n+\n+This commit object intentionally broken\n+EOF\n+\n+test_expect_success 'push with receive.fsck.missing-mail = warn' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.missing-email warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\tgrep \"missing-email\" act\n+'\n+\n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253355","messageId":"fcf343ed359ca47d6154f0d1e1850738eb7768ac.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 12/18] Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:16Z","receivedAt":"2014-12-08T16:15:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some kinds of errors are intrinsically unrecoverable (e.g. errors while\nuncompressing objects). It does not make sense to allow demoting them to\nmere warnings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 8 ++++++--\n t/t5504-fetch-receive-strict.sh | 9 +++++++++\n 2 files changed, 15 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex c1e7a85..f8339af 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,9 @@\n #include \"refs.h\"\n \n #define FOREACH_MSG_ID(FUNC) \\\n+\t/* fatal errors */ \\\n+\tFUNC(NUL_IN_HEADER) \\\n+\tFUNC(UNTERMINATED_HEADER) \\\n \t/* errors */ \\\n \tFUNC(BAD_DATE) \\\n \tFUNC(BAD_EMAIL) \\\n@@ -39,10 +42,8 @@\n \tFUNC(MISSING_TYPE_ENTRY) \\\n \tFUNC(MULTIPLE_AUTHORS) \\\n \tFUNC(NOT_SORTED) \\\n-\tFUNC(NUL_IN_HEADER) \\\n \tFUNC(TAG_OBJECT_NOT_TAG) \\\n \tFUNC(UNKNOWN_TYPE) \\\n-\tFUNC(UNTERMINATED_HEADER) \\\n \tFUNC(ZERO_PADDED_DATE) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE) \\\n@@ -56,6 +57,7 @@\n \tFUNC(NULL_SHA1) \\\n \tFUNC(ZERO_PADDED_FILEMODE)\n \n+#define FIRST_NON_FATAL_ERROR FSCK_MSG_BAD_DATE\n #define FIRST_WARNING FSCK_MSG_BAD_FILEMODE\n \n #define MSG_ID(x) FSCK_MSG_##x,\n@@ -150,6 +152,8 @@ void fsck_strict_mode(struct fsck_options *options, const char *mode)\n \t\t}\n \n \t\tmsg_id = parse_msg_id(mode, equal);\n+\t\tif (type != FSCK_ERROR && msg_id < FIRST_NON_FATAL_ERROR)\n+\t\t\tdie(\"Cannot demote %.*s\", len, mode);\n \t\toptions->strict_mode[msg_id] = type;\n \t\tmode += len;\n \t}\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex db79e56..8a47db2 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -135,4 +135,13 @@ test_expect_success 'push with receive.fsck.missing-mail = warn' '\n \tgrep \"missing-email\" act\n '\n \n+test_expect_success 'receive.fsck.unterminated-header = warn triggers error' '\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\tgit --git-dir=dst/.git config receive.fsck.unterminated-header warn &&\n+\ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n+\tgrep \"Cannot demote unterminated-header=warn\" act\n+'\n+\n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253359","messageId":"d278090ef5a49b887e329dc16aaeb7b6c69dcaac.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 13/18] Optionally ignore specific fsck issues completely","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:21Z","receivedAt":"2014-12-08T16:15:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An fsck issue in a legacy repository might be so common that one would\nlike not to bother the user with mentioning it at all. With this change,\nthat is possible by setting the respective error to \"ignore\".\n\nThis change \"abuses\" the missing-email=warn test to verify that \"ignore\"\nis also accepted and works correctly. And while at it, it makes sure\nthat multiple options work, too (they are passed to unpack-objects or\nindex-pack as a comma-separated list via the --strict=... command-line\noption).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 5 +++++\n fsck.h                          | 1 +\n t/t5504-fetch-receive-strict.sh | 7 ++++++-\n 3 files changed, 12 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex f8339af..abfd3af 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -146,6 +146,8 @@ void fsck_strict_mode(struct fsck_options *options, const char *mode)\n \t\t\t\ttype = FSCK_ERROR;\n \t\t\telse if (!substrcmp(type_str, type_len, \"warn\"))\n \t\t\t\ttype = FSCK_WARN;\n+\t\t\telse if (!substrcmp(type_str, type_len, \"ignore\"))\n+\t\t\t\ttype = FSCK_IGNORE;\n \t\t\telse\n \t\t\t\tdie(\"Unknown fsck message type: '%.*s'\",\n \t\t\t\t\tlen - equal - 1, type_str);\n@@ -184,6 +186,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tif (msg_type == FSCK_IGNORE)\n+\t\treturn 0;\n+\n \tappend_msg_id(&sb, msg_id_str[id]);\n \n \tva_start(ap, fmt);\ndiff --git a/fsck.h b/fsck.h\nindex 9d67ea2..82bedf9 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -3,6 +3,7 @@\n \n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n+#define FSCK_IGNORE 3\n \n struct fsck_options;\n \ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 8a47db2..0e521d9 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -132,7 +132,12 @@ test_expect_success 'push with receive.fsck.missing-mail = warn' '\n \ttest_must_fail git push --porcelain dst bogus &&\n \tgit --git-dir=dst/.git config receive.fsck.missing-email warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missing-email\" act\n+\tgrep \"missing-email\" act &&\n+\tgit --git-dir=dst/.git branch -D bogus &&\n+\tgit  --git-dir=dst/.git config receive.fsck.missing-email ignore &&\n+\tgit  --git-dir=dst/.git config receive.fsck.bad-date warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\ttest_must_fail grep \"missing-email\" act\n '\n \n test_expect_success 'receive.fsck.unterminated-header = warn triggers error' '\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253358","messageId":"c70409e8e6a42bdc7cacd19cbd49d5d1adbedd1a.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 14/18] fsck: allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:25Z","receivedAt":"2014-12-08T16:15:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'invalid tag name' and 'missing tagger entry' warnings can now be\nupgraded to errors by setting receive.fsck.invalid-tag-name and\nreceive.fsck.missing-tagger-entry to 'error'.\n\nIncidentally, the missing tagger warning is now really shown as a warning\n(as opposed to being reported with the \"error:\" prefix, as it used to be\nthe case before this commit).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                | 24 ++++++++++++++++--------\n t/t5302-pack-index.sh |  2 +-\n 2 files changed, 17 insertions(+), 9 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex abfd3af..154f361 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -52,13 +52,15 @@\n \tFUNC(HAS_DOT) \\\n \tFUNC(HAS_DOTDOT) \\\n \tFUNC(HAS_DOTGIT) \\\n-\tFUNC(INVALID_TAG_NAME) \\\n-\tFUNC(MISSING_TAGGER_ENTRY) \\\n \tFUNC(NULL_SHA1) \\\n-\tFUNC(ZERO_PADDED_FILEMODE)\n+\tFUNC(ZERO_PADDED_FILEMODE) \\\n+\t/* infos (reported as warnings, but ignored by default) */ \\\n+\tFUNC(INVALID_TAG_NAME) \\\n+\tFUNC(MISSING_TAGGER_ENTRY)\n \n #define FIRST_NON_FATAL_ERROR FSCK_MSG_BAD_DATE\n #define FIRST_WARNING FSCK_MSG_BAD_FILEMODE\n+#define FIRST_INFO FSCK_MSG_INVALID_TAG_NAME\n \n #define MSG_ID(x) FSCK_MSG_##x,\n enum fsck_msg_id {\n@@ -103,7 +105,7 @@ int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n \tif (options->strict_mode && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n \t\treturn options->strict_mode[msg_id];\n \tif (options->strict)\n-\t\treturn FSCK_ERROR;\n+\t\treturn msg_id < FIRST_INFO ? FSCK_ERROR : FSCK_WARN;\n \treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n }\n \n@@ -643,13 +645,19 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n-\tif (check_refname_format(sb.buf, 0))\n-\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME, \"invalid 'tag' name: %s\", buffer);\n+\tif (check_refname_format(sb.buf, 0)) {\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME, \"invalid 'tag' name: %s\", buffer);\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \tbuffer = eol + 1;\n \n-\tif (!skip_prefix(buffer, \"tagger \", &buffer))\n+\tif (!skip_prefix(buffer, \"tagger \", &buffer)) {\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 61bc8da..3dc5ec4 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -259,7 +259,7 @@ EOF\n     thirtyeight=${tag#??} &&\n     rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&\n     git index-pack --strict tag-test-${pack1}.pack 2>err &&\n-    grep \"^error:.* expected .tagger. line\" err\n+    grep \"^warning:.* expected .tagger. line\" err\n '\n \n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253363","messageId":"78b9db79bd0b2c17e74b18d689d473861d44e405.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 15/18] Document the new receive.fsck.* options.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:29Z","receivedAt":"2014-12-08T16:15:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 7deae0b..b3276ee 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2109,6 +2109,20 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n+receive.fsck.*::\n+\tWhen `receive.fsckObjects is set to true, errors can be switched\n+\tto warnings and vice versa by setting e.g. `receive.fsck.bad-name`\n+\tto `warn` or `error` (or `ignore` to hide those errors\n+\tcompletely). For convenience, fsck prefixes the error/warning\n+\twith the name of the option, e.g. \"missing-email: invalid\n+\tauthor/committer line - missing email\" means that setting\n+\t`receive.fsck.missing-email` to `ignore` will hide that issue.\n+\tFor convenience, camelCased options are accepted, too (e.g.\n+\t`receive.fsck.missingEmail`).\n++\n+This feature is intended to support working with legacy repositories\n+which would not pass pushing when `receive.fsckObjects = true`.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253360","messageId":"cdd5730d0003a7220f659804e9e286e77619b57c.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:32Z","receivedAt":"2014-12-08T16:15:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already have support in `git receive-pack` to deal with some legacy\nrepositories which have non-fatal issues.\n\nLet's make `git fsck` itself useful with such repositories, too, by\nallowing users to ignore known issues, or at least demote those issues\nto mere warnings.\n\nExample: `git -c fsck.missing-email=ignore fsck` would hide problems with\nmissing emails in author, committer and tagger lines.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 13 +++++++++++++\n builtin/fsck.c           | 15 +++++++++++++++\n t/t1450-fsck.sh          | 11 +++++++++++\n 3 files changed, 39 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex b3276ee..fa58c26 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1192,6 +1192,19 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n+fsck.*::\n+\tWith these options, fsck errors can be switched to warnings and\n+\tvice versa by setting e.g. `fsck.bad-name` to `warn` or `error`\n+\t(or `ignore` to hide those errors completely). For convenience,\n+\tfsck prefixes the error/warning with the name of the option, e.g.\n+\t\"missing-email: invalid author/committer line - missing email\"\n+\tmeans that setting `fsck.missing-email` to `ignore` will hide that\n+\tissue.  For convenience, camelCased options are accepted, too (e.g.\n+\t`fsck.missingEmail`).\n++\n+This feature is intended to support working with legacy repositories\n+which cannot be repaired without disruptive changes.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 99d4538..2b8faa4 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,6 +46,19 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n+static int fsck_config(const char *var, const char *value, void *cb)\n+{\n+\tif (starts_with(var, \"fsck.\")) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"%s=%s\", var + 5, value ? value : \"error\");\n+\t\tfsck_strict_mode(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n static void objreport(struct object *obj, const char *severity,\n                       const char *err)\n {\n@@ -638,6 +651,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tgit_config(fsck_config, NULL);\n+\n \tfsck_head_link();\n \tfsck_object_dir(get_object_directory());\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 019fddd..d74df19 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -283,6 +283,17 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '\n \tgrep -q \"error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff\" out\n '\n \n+test_expect_success 'force fsck to ignore double author' '\n+\tgit cat-file commit HEAD >basis &&\n+\tsed \"s/^author .*/&,&/\" <basis | tr , \\\\n >multiple-authors &&\n+\tnew=$(git hash-object -t commit -w --stdin <multiple-authors) &&\n+\ttest_when_finished \"remove_object $new\" &&\n+\tgit update-ref refs/heads/bogus \"$new\" &&\n+\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n+\ttest_must_fail git fsck &&\n+\tgit -c fsck.multiple-authors=ignore fsck\n+'\n+\n _bz='\\0'\n _bz5=\"$_bz$_bz$_bz$_bz$_bz\"\n _bz20=\"$_bz5$_bz5$_bz5$_bz5\"\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253362","messageId":"7ee3ddb3d4235a8fd5ce11fe454cd4c230ff3434.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 17/18] Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:36Z","receivedAt":"2014-12-08T16:15:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This option avoids unpacking each and all objects, and just verifies the\nconnectivity. In particular with large repositories, this speeds up the\noperation, at the expense of missing corrupt blobs and ignoring\nunreachable objects, if any.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  7 ++++++-\n builtin/fsck.c             |  7 ++++++-\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 3 files changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 25c431d..b98fb43 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n-\t [--[no-]full] [--strict] [--verbose] [--lost-found]\n+\t [--[no-]full] [--quick] [--strict] [--verbose] [--lost-found]\n \t [--[no-]dangling] [--[no-]progress] [<object>*]\n \n DESCRIPTION\n@@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \tobject pools.  This is now default; you can turn it off\n \twith --no-full.\n \n+--quick::\n+\tCheck only the connectivity of tags, commits and tree objects. By\n+\tavoiding to unpack blobs, this speeds up the operation, at the\n+\texpense of missing corrupt objects.\n+\n --strict::\n \tEnable more strict checking, namely to catch a file mode\n \trecorded with g+w bit set, which was created by older\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2b8faa4..dcea9b0 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -23,6 +23,7 @@ static int show_tags;\n static int show_unreachable;\n static int include_reflogs = 1;\n static int check_full = 1;\n+static int quick;\n static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n@@ -184,6 +185,8 @@ static void check_reachable_object(struct object *obj)\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (has_sha1_pack(obj->sha1))\n \t\t\treturn; /* it is in pack - forget about it */\n+\t\tif (quick && has_sha1_file(obj->sha1))\n+\t\t\treturn;\n \t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n@@ -618,6 +621,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"cache\", &keep_cache_objects, N_(\"make index objects head nodes\")),\n \tOPT_BOOL(0, \"reflogs\", &include_reflogs, N_(\"make reflogs head nodes (default)\")),\n \tOPT_BOOL(0, \"full\", &check_full, N_(\"also consider packs and alternate objects\")),\n+\tOPT_BOOL(0, \"quick\", &quick, N_(\"check only connectivity\")),\n \tOPT_BOOL(0, \"strict\", &check_strict, N_(\"enable more strict checking\")),\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n@@ -654,7 +658,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tfsck_object_dir(get_object_directory());\n+\tif (!quick)\n+\t\tfsck_object_dir(get_object_directory());\n \n \tprepare_alt_odb();\n \tfor (alt = alt_odb_list; alt; alt = alt->next) {\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex d74df19..d389d4a 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -407,4 +407,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n \ttest_must_fail git -C missing fsck\n '\n \n+test_expect_success 'fsck --quick' '\n+\trm -rf quick &&\n+\tgit init quick &&\n+\t(\n+\t\tcd quick &&\n+\t\ttouch empty &&\n+\t\tgit add empty &&\n+\t\ttest_commit empty &&\n+\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n+\t\trm -f $empty &&\n+\t\techo invalid >$empty &&\n+\t\ttest_must_fail git fsck --strict &&\n+\t\tgit fsck --strict --quick &&\n+\t\ttree=$(git rev-parse HEAD:) &&\n+\t\tsuffix=${tree#??} &&\n+\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n+\t\trm -f $tree &&\n+\t\techo invalid >$tree &&\n+\t\ttest_must_fail git fsck --strict --quick\n+\t)\n+'\n+\n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253361","messageId":"bafa42210f8885335aeacac41a81380a9a7eef24.1418055173.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"[PATCH 18/18] git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-08T16:15:40Z","receivedAt":"2014-12-08T16:15:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The optional new config option `receive.fsck.skip-list` specifies the path\nto a file listing the names, i.e. SHA-1s, one per line, of objects that\nare to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nThe intended use case is for server administrators to inspect objects\nthat are reported by `git push` as being too problematic to enter the\nrepository, and to add the objects' SHA-1 to a (preferably sorted) file\nwhen the objects are legitimate, i.e. when it is determined that those\nproblematic objects should be allowed to enter the server.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/receive-pack.c          |  9 +++++++\n fsck.c                          | 59 +++++++++++++++++++++++++++++++++++++++--\n fsck.h                          |  2 ++\n t/t5504-fetch-receive-strict.sh | 12 +++++++++\n 4 files changed, 80 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 111e514..5169f1f 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -110,6 +110,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (starts_with(var, \"receive.fsck.skip-list\")) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tif (fsck_strict_mode.len)\n+\t\t\tstrbuf_addch(&fsck_strict_mode, ',');\n+\t\tstrbuf_addf(&fsck_strict_mode, \"skip-list=%s\", path);\n+\t\treturn 0;\n+\t}\n+\n \tif (starts_with(var, \"receive.fsck.\")) {\n \t\tif (fsck_strict_mode.len)\n \t\t\tstrbuf_addch(&fsck_strict_mode, ',');\ndiff --git a/fsck.c b/fsck.c\nindex 154f361..00693f2 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -7,6 +7,7 @@\n #include \"tag.h\"\n #include \"fsck.h\"\n #include \"refs.h\"\n+#include \"sha1-array.h\"\n \n #define FOREACH_MSG_ID(FUNC) \\\n \t/* fatal errors */ \\\n@@ -56,7 +57,9 @@\n \tFUNC(ZERO_PADDED_FILEMODE) \\\n \t/* infos (reported as warnings, but ignored by default) */ \\\n \tFUNC(INVALID_TAG_NAME) \\\n-\tFUNC(MISSING_TAGGER_ENTRY)\n+\tFUNC(MISSING_TAGGER_ENTRY) \\\n+\t/* special value */ \\\n+\tFUNC(SKIP_LIST)\n \n #define FIRST_NON_FATAL_ERROR FSCK_MSG_BAD_DATE\n #define FIRST_WARNING FSCK_MSG_BAD_FILEMODE\n@@ -109,6 +112,43 @@ int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n \treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n }\n \n+static void init_skip_list(struct fsck_options *options, const char *path)\n+{\n+\tstatic struct sha1_array skip_list = SHA1_ARRAY_INIT;\n+\tint sorted, fd;\n+\tchar buffer[41];\n+\tunsigned char sha1[20];\n+\n+\tif (options->skip_list)\n+\t\tsorted = options->skip_list->sorted;\n+\telse {\n+\t\tsorted = 1;\n+\t\toptions->skip_list = &skip_list;\n+\t}\n+\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Could not open skip list: %s\", path);\n+\tfor (;;) {\n+\t\tint result = read_in_full(fd, buffer, sizeof(buffer));\n+\t\tif (result < 0)\n+\t\t\tdie_errno(\"Could not read '%s'\", path);\n+\t\tif (!result)\n+\t\t\tbreak;\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n+\t\t\tdie(\"Invalid SHA-1: %s\", buffer);\n+\t\tsha1_array_append(&skip_list, sha1);\n+\t\tif (sorted && skip_list.nr > 1 &&\n+\t\t\t\thashcmp(skip_list.sha1[skip_list.nr - 2],\n+\t\t\t\t\tsha1) > 0)\n+\t\t\tsorted = 0;\n+\t}\n+\tclose(fd);\n+\n+\tif (sorted)\n+\t\tskip_list.sorted = 1;\n+}\n+\n static inline int substrcmp(const char *string, int len, const char *match)\n {\n \tint match_len = strlen(match);\n@@ -141,6 +181,18 @@ void fsck_strict_mode(struct fsck_options *options, const char *mode)\n \t\t\tif (mode[equal] == '=')\n \t\t\t\tbreak;\n \n+\t\tmsg_id = parse_msg_id(mode, equal);\n+\t\tif (msg_id == FSCK_MSG_SKIP_LIST) {\n+\t\t\tchar *path = xstrndup(mode + equal + 1, len - equal - 1);\n+\n+\t\t\tif (equal == len)\n+\t\t\t\tdie(\"skip-list requires a path\");\n+\t\t\tinit_skip_list(options, path);\n+\t\t\tfree(path);\n+\t\t\tmode += len;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (equal < len) {\n \t\t\tconst char *type_str = mode + equal + 1;\n \t\t\tint type_len = len - equal - 1;\n@@ -155,7 +207,6 @@ void fsck_strict_mode(struct fsck_options *options, const char *mode)\n \t\t\t\t\tlen - equal - 1, type_str);\n \t\t}\n \n-\t\tmsg_id = parse_msg_id(mode, equal);\n \t\tif (type != FSCK_ERROR && msg_id < FIRST_NON_FATAL_ERROR)\n \t\t\tdie(\"Cannot demote %.*s\", len, mode);\n \t\toptions->strict_mode[msg_id] = type;\n@@ -681,6 +732,10 @@ static int fsck_tag(struct tag *tag, const char *data,\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n+\tif (options->skip_list &&\n+\t\t\tsha1_array_lookup(options->skip_list, obj->sha1) >= 0)\n+\t\treturn 0;\n+\n \tif (!obj)\n \t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \ndiff --git a/fsck.h b/fsck.h\nindex 82bedf9..74d11cd 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -29,6 +29,8 @@ struct fsck_options {\n \tfsck_error error_func;\n \tint strict:1;\n \tint *strict_mode;\n+\t/* TODO: consider reading into a hashmap */\n+\tstruct sha1_array *skip_list;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 0e521d9..cf6cd5d 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,6 +123,18 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n+test_expect_success 'push with receive.fsck.skip-list' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.skip-list SKIP &&\n+\techo $commit > dst/.git/SKIP &&\n+\tgit push --porcelain dst bogus\n+'\n+\n test_expect_success 'push with receive.fsck.missing-mail = warn' '\n \tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"253504","messageId":"xmqqk31zjz8o.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"56e9445d6e8a746356146bc565512f53bf8a0c8d.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 01/18] Introduce fsck options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T15:33:43Z","receivedAt":"2014-12-10T15:33:43Z","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\nTHis is not limited to this step, but\n\n> Subject: Re: [PATCH 01/18] Introduce fsck options\n\nplease make it easier to cluster and spot the series in the eventual\nshortlog by giving a common prefix to the patches, e.g.\n\n\tfsck: introduce fsck_options struct\n\n> +static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n> +static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n\nIs it a good idea to allow walker to be strict but obj verifier to\nbe not (or vice versa)?  I am wondering why this is not a single\nstruct with two callback function pointers.\n\n> +struct fsck_options {\n> +\tfsck_walk_func walk;\n> +\tfsck_error error_func;\n> +\tint strict:1;\n\nA signed 1-bit-wide bitfield can hold its sign-bit and nothing else,\nno?\n\n    unsigned strict:1;\n"},{"id":"253514","messageId":"xmqqzjavie9l.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"d81653ce9ef73a54a71613c3e9828ad21f2391d7.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 05/18] Allow demoting errors to warnings via receive.fsck.<key> = warn","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T17:52:06Z","receivedAt":"2014-12-10T17:52:06Z","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> For example, missing emails in commit and tag objects can be demoted to\n> mere warnings with\n>\n> \tgit config receive.fsck.missing-email warn\n\nNo punctuations in the first and the last level of configuration\nvariable names, please.  I.e. s/missing-email/missingEmail/ or\nsomething.\n"},{"id":"253515","messageId":"xmqqsignie2k.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"e217caa9c30b72004eca9b15be3aa1c6674dc278.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 03/18] Provide a function to parse fsck message IDs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T17:56:19Z","receivedAt":"2014-12-10T17:56:19Z","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> This function will be used in the next commits to allow the user to\n> ask fsck to handle specific problems differently, e.g. demoting certain\n> errors to warnings. It has to handle partial strings because we would\n> like to be able to parse, say, '--strict=missing-email=warn' command\n> lines.\n>\n> To make the parsing robust, we generate strings from the enum keys, and we\n> will match both lower-case, dash-separated values as well as camelCased\n> ones (e.g. both \"missing-email\" and \"missingEmail\" will match the\n> \"MISSING_EMAIL\" key).\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c | 32 ++++++++++++++++++++++++++++++++\n>  1 file changed, 32 insertions(+)\n>\n> diff --git a/fsck.c b/fsck.c\n> index 3cea034..05b146c 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -63,6 +63,38 @@ enum fsck_msg_id {\n>  \tFSCK_MSG_MAX\n>  };\n>  \n> +#define STR(x) #x\n> +#define MSG_ID_STR(x) STR(x),\n> +static const char *msg_id_str[FSCK_MSG_MAX + 1] = {\n> +\tFOREACH_MSG_ID(MSG_ID_STR)\n> +\tNULL\n> +};\n\nI wondered what the ugly macro was in the previous step, but as a\nway to keep these two lists in sync it makes sense.\n"},{"id":"253516","messageId":"xmqqoarbidv7.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"2a0c4cd4c5d3aaceff8a6ffa49d2f3597d26086d.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T18:00:44Z","receivedAt":"2014-12-10T18:00:44Z","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> There are legacy repositories out there whose older commits and tags\n> have issues that prevent pushing them when 'receive.fsckObjects' is set.\n> One real-life example is a commit object that has been hand-crafted to\n> list two authors.\n>\n> Often, it is not possible to fix those issues without disrupting the\n> work with said repositories, yet it is still desirable to perform checks\n> by setting `receive.fsckObjects = true`. This commit is the first step\n> to allow demoting specific fsck issues to mere warnings.\n>\n> The function added by this commit parses a list of settings in the form:\n>\n> \tmissing-email=warn,bad-name=warn,...\n>\n> Unfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\n> git fsck so far, but other call paths (e.g. git index-pack --strict)\n> error out *always* no matter what type was specified. Therefore, we\n> need to take extra care to default to all FSCK_ERROR in those cases.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n>  fsck.h |  7 +++++--\n>  2 files changed, 63 insertions(+), 2 deletions(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index 05b146c..9e6d70f 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -97,9 +97,63 @@ static int parse_msg_id(const char *text, int len)\n>  \n>  int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n>  {\n> +\tif (options->strict_mode && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n> +\t\treturn options->strict_mode[msg_id];\n> +\tif (options->strict)\n> +\t\treturn FSCK_ERROR;\n>  \treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n>  }\n\nHmm, if you are later going to allow demoting (hopefully also promoting)\nerror to warn, etc., would the comparison between msg_id and FIRST_WARNING\nmake much sense?\n\nIn other words, at some point wouldn't we be better off with\nsomething like this\n\n\tstruct {\n        \tenum id;\n                const char *id_string;\n                enum error_level { FSCK_PASS, FSCK_WARN, FSCK_ERROR };\n\t} possible_fsck_errors[];\n"},{"id":"253517","messageId":"xmqqk31zidp5.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"43faa41d4cc98d6c40a393ec590af73ec5c94246.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 09/18] fsck: handle multiple authors in commits specially","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T18:04:22Z","receivedAt":"2014-12-10T18:04:22Z","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> This problem has been detected in the wild, and is the primary reason\n> to introduce an option to demote certain fsck errors to warnings. Let's\n> offer to ignore this particular problem specifically.\n> ...\n> +\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n> +\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n> +\t\tif (err)\n> +\t\t\treturn err;\n\nIf we have an option to demote this to a warning, wouldn't we want\nto do the same fsck_ident() on that secondary author line?\n\n> +\t\t/* require_end_of_header() ensured that there is a newline */\n> +\t\tbuffer = strchr(buffer, '\\n') + 1;\n> +\t}\n>  \tif (!skip_prefix(buffer, \"committer \", &buffer))\n>  \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n>  \terr = fsck_ident(&buffer, &commit->object, options);\n"},{"id":"253518","messageId":"xmqqd27ridl0.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"fcf343ed359ca47d6154f0d1e1850738eb7768ac.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 12/18] Disallow demoting grave fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T18:06:51Z","receivedAt":"2014-12-10T18:06:51Z","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> Some kinds of errors are intrinsically unrecoverable (e.g. errors while\n> uncompressing objects). It does not make sense to allow demoting them to\n> mere warnings.\n\nMakes sense, but the patch makes it look as if this is an \"oops, we\nshould have done the list in patch 02/18 in this order from the\nbeginning\".  Can we reorder the patches?\n\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c                          | 8 ++++++--\n>  t/t5504-fetch-receive-strict.sh | 9 +++++++++\n>  2 files changed, 15 insertions(+), 2 deletions(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index c1e7a85..f8339af 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -9,6 +9,9 @@\n>  #include \"refs.h\"\n>  \n>  #define FOREACH_MSG_ID(FUNC) \\\n> +\t/* fatal errors */ \\\n> +\tFUNC(NUL_IN_HEADER) \\\n> +\tFUNC(UNTERMINATED_HEADER) \\\n>  \t/* errors */ \\\n>  \tFUNC(BAD_DATE) \\\n>  \tFUNC(BAD_EMAIL) \\\n> @@ -39,10 +42,8 @@\n>  \tFUNC(MISSING_TYPE_ENTRY) \\\n>  \tFUNC(MULTIPLE_AUTHORS) \\\n>  \tFUNC(NOT_SORTED) \\\n> -\tFUNC(NUL_IN_HEADER) \\\n>  \tFUNC(TAG_OBJECT_NOT_TAG) \\\n>  \tFUNC(UNKNOWN_TYPE) \\\n> -\tFUNC(UNTERMINATED_HEADER) \\\n>  \tFUNC(ZERO_PADDED_DATE) \\\n>  \t/* warnings */ \\\n>  \tFUNC(BAD_FILEMODE) \\\n> @@ -56,6 +57,7 @@\n>  \tFUNC(NULL_SHA1) \\\n>  \tFUNC(ZERO_PADDED_FILEMODE)\n>  \n> +#define FIRST_NON_FATAL_ERROR FSCK_MSG_BAD_DATE\n>  #define FIRST_WARNING FSCK_MSG_BAD_FILEMODE\n>  \n>  #define MSG_ID(x) FSCK_MSG_##x,\n> @@ -150,6 +152,8 @@ void fsck_strict_mode(struct fsck_options *options, const char *mode)\n>  \t\t}\n>  \n>  \t\tmsg_id = parse_msg_id(mode, equal);\n> +\t\tif (type != FSCK_ERROR && msg_id < FIRST_NON_FATAL_ERROR)\n> +\t\t\tdie(\"Cannot demote %.*s\", len, mode);\n>  \t\toptions->strict_mode[msg_id] = type;\n>  \t\tmode += len;\n>  \t}\n> diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\n> index db79e56..8a47db2 100755\n> --- a/t/t5504-fetch-receive-strict.sh\n> +++ b/t/t5504-fetch-receive-strict.sh\n> @@ -135,4 +135,13 @@ test_expect_success 'push with receive.fsck.missing-mail = warn' '\n>  \tgrep \"missing-email\" act\n>  '\n>  \n> +test_expect_success 'receive.fsck.unterminated-header = warn triggers error' '\n> +\trm -rf dst &&\n> +\tgit init dst &&\n> +\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n> +\tgit --git-dir=dst/.git config receive.fsck.unterminated-header warn &&\n> +\ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n> +\tgrep \"Cannot demote unterminated-header=warn\" act\n> +'\n> +\n>  test_done\n"},{"id":"253519","messageId":"xmqq8uifidkd.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"d278090ef5a49b887e329dc16aaeb7b6c69dcaac.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 13/18] Optionally ignore specific fsck issues completely","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T18:07:14Z","receivedAt":"2014-12-10T18:07:14Z","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> An fsck issue in a legacy repository might be so common that one would\n> like not to bother the user with mentioning it at all. With this change,\n> that is possible by setting the respective error to \"ignore\".\n\nMakes sense.\n"},{"id":"253520","messageId":"xmqq4mt3idho.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"c70409e8e6a42bdc7cacd19cbd49d5d1adbedd1a.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 14/18] fsck: allow upgrading fsck warnings to errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T18:08:51Z","receivedAt":"2014-12-10T18:08:51Z","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> The 'invalid tag name' and 'missing tagger entry' warnings can now be\n> upgraded to errors by setting receive.fsck.invalid-tag-name and\n> receive.fsck.missing-tagger-entry to 'error'.\n\nHmm, why can't all warnings promotable to errors, or are the above\ntwo mentioned only as examples?\n\n>\n> Incidentally, the missing tagger warning is now really shown as a warning\n> (as opposed to being reported with the \"error:\" prefix, as it used to be\n> the case before this commit).\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c                | 24 ++++++++++++++++--------\n>  t/t5302-pack-index.sh |  2 +-\n>  2 files changed, 17 insertions(+), 9 deletions(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index abfd3af..154f361 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -52,13 +52,15 @@\n>  \tFUNC(HAS_DOT) \\\n>  \tFUNC(HAS_DOTDOT) \\\n>  \tFUNC(HAS_DOTGIT) \\\n> -\tFUNC(INVALID_TAG_NAME) \\\n> -\tFUNC(MISSING_TAGGER_ENTRY) \\\n>  \tFUNC(NULL_SHA1) \\\n> -\tFUNC(ZERO_PADDED_FILEMODE)\n> +\tFUNC(ZERO_PADDED_FILEMODE) \\\n> +\t/* infos (reported as warnings, but ignored by default) */ \\\n> +\tFUNC(INVALID_TAG_NAME) \\\n> +\tFUNC(MISSING_TAGGER_ENTRY)\n>  \n>  #define FIRST_NON_FATAL_ERROR FSCK_MSG_BAD_DATE\n>  #define FIRST_WARNING FSCK_MSG_BAD_FILEMODE\n> +#define FIRST_INFO FSCK_MSG_INVALID_TAG_NAME\n>  \n>  #define MSG_ID(x) FSCK_MSG_##x,\n>  enum fsck_msg_id {\n> @@ -103,7 +105,7 @@ int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n>  \tif (options->strict_mode && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n>  \t\treturn options->strict_mode[msg_id];\n>  \tif (options->strict)\n> -\t\treturn FSCK_ERROR;\n> +\t\treturn msg_id < FIRST_INFO ? FSCK_ERROR : FSCK_WARN;\n>  \treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n>  }\n>  \n> @@ -643,13 +645,19 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n>  \t\tgoto done;\n>  \t}\n>  \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n> -\tif (check_refname_format(sb.buf, 0))\n> -\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME, \"invalid 'tag' name: %s\", buffer);\n> +\tif (check_refname_format(sb.buf, 0)) {\n> +\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME, \"invalid 'tag' name: %s\", buffer);\n> +\t\tif (ret)\n> +\t\t\tgoto done;\n> +\t}\n>  \tbuffer = eol + 1;\n>  \n> -\tif (!skip_prefix(buffer, \"tagger \", &buffer))\n> +\tif (!skip_prefix(buffer, \"tagger \", &buffer)) {\n>  \t\t/* early tags do not contain 'tagger' lines; warn only */\n> -\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n> +\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n> +\t\tif (ret)\n> +\t\t\tgoto done;\n> +\t}\n>  \telse\n>  \t\tret = fsck_ident(&buffer, &tag->object, options);\n>  \n> diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\n> index 61bc8da..3dc5ec4 100755\n> --- a/t/t5302-pack-index.sh\n> +++ b/t/t5302-pack-index.sh\n> @@ -259,7 +259,7 @@ EOF\n>      thirtyeight=${tag#??} &&\n>      rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&\n>      git index-pack --strict tag-test-${pack1}.pack 2>err &&\n> -    grep \"^error:.* expected .tagger. line\" err\n> +    grep \"^warning:.* expected .tagger. line\" err\n>  '\n>  \n>  test_done\n"},{"id":"253521","messageId":"xmqqzjavgym5.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"cdd5730d0003a7220f659804e9e286e77619b57c.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T18:15:30Z","receivedAt":"2014-12-10T18:15:30Z","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> We already have support in `git receive-pack` to deal with some legacy\n> repositories which have non-fatal issues.\n>\n> Let's make `git fsck` itself useful with such repositories, too, by\n> allowing users to ignore known issues, or at least demote those issues\n> to mere warnings.\n>\n> Example: `git -c fsck.missing-email=ignore fsck` would hide problems with\n> missing emails in author, committer and tagger lines.\n\nHopefully I do not have to repeat myself, but please do not have\npunctuations in the first and the last level of configuration\nvariable names, i.e. fsck.missingEmail, not mising-email.\n\nShould these be tied to receive-pack ones in any way?  E.g. if you\nset fsck.missingEmail to ignore, you do not have to do the same for\nreceive and accept a push with the specific error turned off?\n\nNot a rhetorical question.  I can see it argued both ways.  The\njustification to defend the position of not tying these two I would\nhave is so that I can be more strict to newer breakages (i.e. not\naccepting a push that introduce a new breakage by not ignoring with\nreceive.fsck.*) while allowing breakages that are already present.\nThe justification for the opposite position is to make it more\nconvenient to write a consistent policy.  Whichever way is chosen,\nwe would want to see the reason left in the log message so that\npeople do not have to wonder what the original motivation was when\nthey decide if it is a good idea to change this part of the code.\n\nThanks.\n"},{"id":"253523","messageId":"xmqqvbljgy8y.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"bafa42210f8885335aeacac41a81380a9a7eef24.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 18/18] git receive-pack: support excluding objects from fsck'ing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T18:23:25Z","receivedAt":"2014-12-10T18:23:25Z","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> The optional new config option `receive.fsck.skip-list` specifies the path\n> to a file listing the names, i.e. SHA-1s, one per line, of objects that\n> are to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n>\n> This is extremely handy in case of legacy repositories where it would\n> cause more pain to change incorrect objects than to live with them\n> (e.g. a duplicate 'author' line in an early commit object).\n>\n> The intended use case is for server administrators to inspect objects\n> that are reported by `git push` as being too problematic to enter the\n> repository, and to add the objects' SHA-1 to a (preferably sorted) file\n> when the objects are legitimate, i.e. when it is determined that those\n> problematic objects should be allowed to enter the server.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  builtin/receive-pack.c          |  9 +++++++\n>  fsck.c                          | 59 +++++++++++++++++++++++++++++++++++++++--\n>  fsck.h                          |  2 ++\n>  t/t5504-fetch-receive-strict.sh | 12 +++++++++\n>  4 files changed, 80 insertions(+), 2 deletions(-)\n>\n> diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\n> index 111e514..5169f1f 100644\n> --- a/builtin/receive-pack.c\n> +++ b/builtin/receive-pack.c\n> @@ -110,6 +110,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n>  \t\treturn 0;\n>  \t}\n>  \n> +\tif (starts_with(var, \"receive.fsck.skip-list\")) {\n\ns/skip-list/skiplist/;\n\n> +\t\tconst char *path = is_absolute_path(value) ?\n> +\t\t\tvalue : git_path(\"%s\", value);\n> +\t\tif (fsck_strict_mode.len)\n> +\t\t\tstrbuf_addch(&fsck_strict_mode, ',');\n> +\t\tstrbuf_addf(&fsck_strict_mode, \"skip-list=%s\", path);\n> +\t\treturn 0;\n> +\t}\n> +\n>  \tif (starts_with(var, \"receive.fsck.\")) {\n>  \t\tif (fsck_strict_mode.len)\n>  \t\t\tstrbuf_addch(&fsck_strict_mode, ',');\n> diff --git a/fsck.c b/fsck.c\n> index 154f361..00693f2 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -7,6 +7,7 @@\n>  #include \"tag.h\"\n>  #include \"fsck.h\"\n>  #include \"refs.h\"\n> +#include \"sha1-array.h\"\n>  \n>  #define FOREACH_MSG_ID(FUNC) \\\n>  \t/* fatal errors */ \\\n> @@ -56,7 +57,9 @@\n>  \tFUNC(ZERO_PADDED_FILEMODE) \\\n>  \t/* infos (reported as warnings, but ignored by default) */ \\\n>  \tFUNC(INVALID_TAG_NAME) \\\n> -\tFUNC(MISSING_TAGGER_ENTRY)\n> +\tFUNC(MISSING_TAGGER_ENTRY) \\\n> +\t/* special value */ \\\n> +\tFUNC(SKIP_LIST)\n\nThis feels like a kludge to me without comment on what \"special\nvalue\" means.  Does it mean \"this object has an error (which by\ndefault is ignored) of being on the skip list?\"  Should we be able\nto optionally warn an object on the skip-list exists with the same\nmechansim the rest of the series uses to tweak the error level?\n"},{"id":"253524","messageId":"xmqqr3w7gxr4.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"cover.1418055173.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 00/18] Introduce an internal API to interact with the fsck machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-10T18:34:07Z","receivedAt":"2014-12-10T18:34:07Z","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> At the moment, the git-fsck's integrity checks are targeted toward the\n> end user, i.e. the error messages are really just messages, intended for\n> human consumption.\n>\n> Under certain circumstances, some of those errors should be allowed to\n> be turned into mere warnings, though, because the cost of fixing the\n> issues might well be larger than the cost of carrying those flawed\n> objects.\n\nOverall I very much like what this series aims to do.\nThanks for working on this.\n"},{"id":"253929","messageId":"alpine.DEB.1.00.1412221822310.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqk31zjz8o.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 01/18] Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T17:26:10Z","receivedAt":"2014-12-22T17:26:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Subject: Re: [PATCH 01/18] Introduce fsck options\n> \n> please make it easier to cluster and spot the series in the eventual\n> shortlog by giving a common prefix to the patches, e.g.\n> \n> \tfsck: introduce fsck_options struct\n\nI use the fsck: prefix consistently now.\n\n> > +static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n> > +static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n> \n> Is it a good idea to allow walker to be strict but obj verifier to\n> be not (or vice versa)?  I am wondering why this is not a single\n> struct with two callback function pointers.\n\nUnfortunately not. There are two different walkers used, and in fact,\nfsck_walk_options() is only used to walk the objects, not to fsck them.\n\nNow, I could use only one struct and set the walker, but that is not\nthread-safe, and while code is not threaded yet AFAICT, it might be in the\nfuture. That is why I decided to be rather safe than sorry. If you want it\ndifferently, please just say the word, I will make it so.\n\n> > +struct fsck_options {\n> > +\tfsck_walk_func walk;\n> > +\tfsck_error error_func;\n> > +\tint strict:1;\n> \n> A signed 1-bit-wide bitfield can hold its sign-bit and nothing else,\n> no?\n> \n>     unsigned strict:1;\n\nOops. Right. For some reason, it worked here, though... Fixed!\n\nCiao,\nDscho\n"},{"id":"253930","messageId":"xmqq61d3bnf9.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412221822310.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 01/18] Introduce fsck options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-22T17:32:26Z","receivedAt":"2014-12-22T17:32:26Z","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>> Is it a good idea to allow walker to be strict but obj verifier to\n>> be not (or vice versa)?  I am wondering why this is not a single\n>> struct with two callback function pointers.\n>\n> Unfortunately not. There are two different walkers used, and in fact,\n> fsck_walk_options() is only used to walk the objects, not to fsck them.\n>\n> Now, I could use only one struct and set the walker, but that is not\n> thread-safe, and while code is not threaded yet AFAICT, it might be in the\n> future. That is why I decided to be rather safe than sorry. If you want it\n> differently, please just say the word, I will make it so.\n\nThanks for explaining; I just found that the reason behind the\ndesign choice was unclear and wanted to know.\n"},{"id":"253946","messageId":"alpine.DEB.1.00.1412221836480.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqsignie2k.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 03/18] Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T21:27:29Z","receivedAt":"2014-12-22T21:27:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > This function will be used in the next commits to allow the user to\n> > ask fsck to handle specific problems differently, e.g. demoting certain\n> > errors to warnings. It has to handle partial strings because we would\n> > like to be able to parse, say, '--strict=missing-email=warn' command\n> > lines.\n> >\n> > To make the parsing robust, we generate strings from the enum keys, and we\n> > will match both lower-case, dash-separated values as well as camelCased\n> > ones (e.g. both \"missing-email\" and \"missingEmail\" will match the\n> > \"MISSING_EMAIL\" key).\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  fsck.c | 32 ++++++++++++++++++++++++++++++++\n> >  1 file changed, 32 insertions(+)\n> >\n> > diff --git a/fsck.c b/fsck.c\n> > index 3cea034..05b146c 100644\n> > --- a/fsck.c\n> > +++ b/fsck.c\n> > @@ -63,6 +63,38 @@ enum fsck_msg_id {\n> >  \tFSCK_MSG_MAX\n> >  };\n> >  \n> > +#define STR(x) #x\n> > +#define MSG_ID_STR(x) STR(x),\n> > +static const char *msg_id_str[FSCK_MSG_MAX + 1] = {\n> > +\tFOREACH_MSG_ID(MSG_ID_STR)\n> > +\tNULL\n> > +};\n> \n> I wondered what the ugly macro was in the previous step, but as a\n> way to keep these two lists in sync it makes sense.\n\nI added a comment to the commit message.\n\nCiao,\nDscho\n"},{"id":"253947","messageId":"alpine.DEB.1.00.1412222232270.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqoarbidv7.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T21:43:21Z","receivedAt":"2014-12-22T21:43:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > There are legacy repositories out there whose older commits and tags\n> > have issues that prevent pushing them when 'receive.fsckObjects' is set.\n> > One real-life example is a commit object that has been hand-crafted to\n> > list two authors.\n> >\n> > Often, it is not possible to fix those issues without disrupting the\n> > work with said repositories, yet it is still desirable to perform checks\n> > by setting `receive.fsckObjects = true`. This commit is the first step\n> > to allow demoting specific fsck issues to mere warnings.\n> >\n> > The function added by this commit parses a list of settings in the form:\n> >\n> > \tmissing-email=warn,bad-name=warn,...\n> >\n> > Unfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\n> > git fsck so far, but other call paths (e.g. git index-pack --strict)\n> > error out *always* no matter what type was specified. Therefore, we\n> > need to take extra care to default to all FSCK_ERROR in those cases.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  fsck.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n> >  fsck.h |  7 +++++--\n> >  2 files changed, 63 insertions(+), 2 deletions(-)\n> >\n> > diff --git a/fsck.c b/fsck.c\n> > index 05b146c..9e6d70f 100644\n> > --- a/fsck.c\n> > +++ b/fsck.c\n> > @@ -97,9 +97,63 @@ static int parse_msg_id(const char *text, int len)\n> >  \n> >  int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n> >  {\n> > +\tif (options->strict_mode && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n> > +\t\treturn options->strict_mode[msg_id];\n> > +\tif (options->strict)\n> > +\t\treturn FSCK_ERROR;\n> >  \treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n> >  }\n> \n> Hmm, if you are later going to allow demoting (hopefully also promoting)\n> error to warn, etc., would the comparison between msg_id and FIRST_WARNING\n> make much sense?\n\nA later patch indeed adds that option. The reason the comparison still\nmakes sense is that the pure infos do not return at all so far, but all of\nthe reported warnings are fatal in strict mode (i.e. when\nreceive.fsckObjects = true). In another later patch it is made possible to\npromote even infos (such as 'missing tagger') to warnings or even errors,\nand that is when the \"return FSCK_ERROR\" is changed to \"return msg_id <\nFIRST_INFO ? FSCK_ERROR : FSCK_WARN\".\n\n> In other words, at some point wouldn't we be better off with\n> something like this\n> \n> \tstruct {\n>         \tenum id;\n>                 const char *id_string;\n>                 enum error_level { FSCK_PASS, FSCK_WARN, FSCK_ERROR };\n> \t} possible_fsck_errors[];\n\nI considered that, and Michael Haggerty also suggested that in a private\nmail. However, I find that there is a clear hierarchy in the default\nmessages: fatal errors, errors, warnings and infos. This should be\nreflected by the order IMHO.\n\nBut I guess it would make a lot of sense to insert those levels as special\nenum values to make it harder to forget to adjust, say, \"#define\nFIRST_WARNING FSCK_MSG_BAD_FILEMODE\" when introducing another warning that\nsorts before said ID alphabetically. In other words, I think that we can\nreally afford to put something like\n\n\t...\n        FUNC(UNKNOWN_TYPE) \\\n        FUNC(ZERO_PADDED_DATE) \\\n        FUNC(___WARNINGS) \\\n        FUNC(BAD_FILEMODE) \\\n        FUNC(EMPTY_NAME) \\\n\t...\n\nat the price of making the parsing a little more complicated and wasting a\nslight bit of more space for the msg_id_str array.\n\nWhat do you think?\n\nCiao,\nDscho\nCiao,\nDscho\n"},{"id":"253948","messageId":"alpine.DEB.1.00.1412222244100.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqzjavie9l.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 05/18] Allow demoting errors to warnings via receive.fsck.<key> = warn","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T21:44:18Z","receivedAt":"2014-12-22T21:44:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > For example, missing emails in commit and tag objects can be demoted to\n> > mere warnings with\n> >\n> > \tgit config receive.fsck.missing-email warn\n> \n> No punctuations in the first and the last level of configuration\n> variable names, please.  I.e. s/missing-email/missingEmail/ or\n> something.\n\nFixed.\n\nCiao,\nDscho\n"},{"id":"253950","messageId":"alpine.DEB.1.00.1412222251160.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqk31zidp5.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 09/18] fsck: handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T21:53:49Z","receivedAt":"2014-12-22T21:53:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > This problem has been detected in the wild, and is the primary reason\n> > to introduce an option to demote certain fsck errors to warnings. Let's\n> > offer to ignore this particular problem specifically.\n> > ...\n> > +\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n> > +\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n> > +\t\tif (err)\n> > +\t\t\treturn err;\n> \n> If we have an option to demote this to a warning, wouldn't we want\n> to do the same fsck_ident() on that secondary author line?\n\nGood point! I changed the following to use fsck_ident() instead:\n\n> > +\t\t/* require_end_of_header() ensured that there is a newline */\n> > +\t\tbuffer = strchr(buffer, '\\n') + 1;\n> > +\t}\n> >  \tif (!skip_prefix(buffer, \"committer \", &buffer))\n> >  \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n> >  \terr = fsck_ident(&buffer, &commit->object, options);\n\nCiao,\nDscho\n"},{"id":"253951","messageId":"alpine.DEB.1.00.1412222254250.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqd27ridl0.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 12/18] Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T21:56:02Z","receivedAt":"2014-12-22T21:56:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Some kinds of errors are intrinsically unrecoverable (e.g. errors while\n> > uncompressing objects). It does not make sense to allow demoting them to\n> > mere warnings.\n> \n> Makes sense, but the patch makes it look as if this is an \"oops, we\n> should have done the list in patch 02/18 in this order from the\n> beginning\".  Can we reorder the patches?\n\nI considered this already, but it would more be like a squash than a\nreordering. And when I squashed the patches, the story did not read as\nclearly to me as it does now. However, if you think this argument is too\nweak, I will squash them.\n\nIs it too weak?\n\nCiao,\nDscho\n"},{"id":"253952","messageId":"xmqqfvc78hwq.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412222232270.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-22T21:59:49Z","receivedAt":"2014-12-22T21:59:49Z","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 other words, at some point wouldn't we be better off with\n>> something like this\n>> \n>> \tstruct {\n>>         \tenum id;\n>>                 const char *id_string;\n>>                 enum error_level { FSCK_PASS, FSCK_WARN, FSCK_ERROR };\n>> \t} possible_fsck_errors[];\n>\n> I considered that, and Michael Haggerty also suggested that in a private\n> mail. However, I find that there is a clear hierarchy in the default\n> messages: fatal errors, errors, warnings and infos.\n\nI am glad I am not alone ;-)\n\nThese classes are ordered from more severe to less, but I do not\nthink it makes much sense to force the default view of \"if you\ncustomize to demote a questionable Q that is classified as an error\nby default as an warning, you must demote all the other ones that we\ndeem less serious than Q, which come earlier (or later---I do not\nremember which) in our predefined list\".  So in that sense, I do not\nconsider that various kinds of questionables fsck can detect are\nhierarchical at all.\n\nI do agree that it makes it easier to code the initialization of\nsuch an array to have \"up to this point we assign the level 'fatal\nerror' by default\" constants.  Then the initialization can become\n\n\tfor (i = 0; i < FIRST_WARN; i++)\n        \tpossible_fsck_errors[i].error_level = FSCK_INFO;\n\twhile (i < FIRST_ERROR)\n        \tpossible_fsck_errors[i++].error_level = FSCK_WARN;\n\twhile (i < ARRAY_SIZE(possible_fsck_errors))\n        \tpossible_fsck_errors[i++].error_level = FSCK_ERROR;\n\nor something.  So I am not against the FIRST_WARNING constant at\nall, but I find it very questionable in a fully customizable system\nto use such a constant anywhere other than the initialization time.\n"},{"id":"253953","messageId":"alpine.DEB.1.00.1412222259070.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqq4mt3idho.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 14/18] fsck: allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T22:01:11Z","receivedAt":"2014-12-22T22:01:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > The 'invalid tag name' and 'missing tagger entry' warnings can now be\n> > upgraded to errors by setting receive.fsck.invalid-tag-name and\n> > receive.fsck.missing-tagger-entry to 'error'.\n> \n> Hmm, why can't all warnings promotable to errors, or are the above\n> two mentioned only as examples?\n\nThose were the only ones that were always shown as warnings but never\ntreated as errors.\n\nThere is a third one coming, as part of the patches that will let fsck\nwarn about NTFS-incompatible file names, but I want to get this patch\nseries integrated into git.git first.\n\nCiao,\nDscho\n"},{"id":"253956","messageId":"xmqqbnmv8h6c.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412222259070.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 14/18] fsck: allow upgrading fsck warnings to errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-22T22:15:39Z","receivedAt":"2014-12-22T22:15:39Z","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> Hi Junio,\n>\n> On Wed, 10 Dec 2014, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> \n>> > The 'invalid tag name' and 'missing tagger entry' warnings can now be\n>> > upgraded to errors by setting receive.fsck.invalid-tag-name and\n>> > receive.fsck.missing-tagger-entry to 'error'.\n>> \n>> Hmm, why can't all warnings promotable to errors, or are the above\n>> two mentioned only as examples?\n>\n> Those were the only ones that were always shown as warnings but never\n> treated as errors.\n\nSorry but I don't quite understand this comment; I suspect the root\ncause might be that we have different mental models on these\ntweakable error severities.\n\nBecause I come from the school \"To these N kinds of events you can\nindependently assign different (i.e. info, warn, error) outcomes\",\nmoving the FIRST_{INFO,WARNING,...} position in the array would only\naffect what happens by default, never hindering the user's ability\nto tweak (in other words, there is no linkage between \"now you can\ntweak\" and the order of events in the list, the latter of which only\nwould affect what the default severity of each event is).\n\nIt appears that your design is from a different mental model and the\norder and position in that list has more significance than what the\ndefault severity of each event is but how much the severity can be\ntweaked, or something, which I somehow find incomprehensible.\n\nPuzzled...\n"},{"id":"253958","messageId":"alpine.DEB.1.00.1412222319040.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqvbljgy8y.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 18/18] git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T22:19:32Z","receivedAt":"2014-12-22T22:19:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > The optional new config option `receive.fsck.skip-list` specifies the path\n> > to a file listing the names, i.e. SHA-1s, one per line, of objects that\n> > are to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n> >\n> > This is extremely handy in case of legacy repositories where it would\n> > cause more pain to change incorrect objects than to live with them\n> > (e.g. a duplicate 'author' line in an early commit object).\n> >\n> > The intended use case is for server administrators to inspect objects\n> > that are reported by `git push` as being too problematic to enter the\n> > repository, and to add the objects' SHA-1 to a (preferably sorted) file\n> > when the objects are legitimate, i.e. when it is determined that those\n> > problematic objects should be allowed to enter the server.\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  builtin/receive-pack.c          |  9 +++++++\n> >  fsck.c                          | 59 +++++++++++++++++++++++++++++++++++++++--\n> >  fsck.h                          |  2 ++\n> >  t/t5504-fetch-receive-strict.sh | 12 +++++++++\n> >  4 files changed, 80 insertions(+), 2 deletions(-)\n> >\n> > diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\n> > index 111e514..5169f1f 100644\n> > --- a/builtin/receive-pack.c\n> > +++ b/builtin/receive-pack.c\n> > @@ -110,6 +110,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n> >  \t\treturn 0;\n> >  \t}\n> >  \n> > +\tif (starts_with(var, \"receive.fsck.skip-list\")) {\n> \n> s/skip-list/skiplist/;\n> \n> > +\t\tconst char *path = is_absolute_path(value) ?\n> > +\t\t\tvalue : git_path(\"%s\", value);\n> > +\t\tif (fsck_strict_mode.len)\n> > +\t\t\tstrbuf_addch(&fsck_strict_mode, ',');\n> > +\t\tstrbuf_addf(&fsck_strict_mode, \"skip-list=%s\", path);\n> > +\t\treturn 0;\n> > +\t}\n> > +\n> >  \tif (starts_with(var, \"receive.fsck.\")) {\n> >  \t\tif (fsck_strict_mode.len)\n> >  \t\t\tstrbuf_addch(&fsck_strict_mode, ',');\n> > diff --git a/fsck.c b/fsck.c\n> > index 154f361..00693f2 100644\n> > --- a/fsck.c\n> > +++ b/fsck.c\n> > @@ -7,6 +7,7 @@\n> >  #include \"tag.h\"\n> >  #include \"fsck.h\"\n> >  #include \"refs.h\"\n> > +#include \"sha1-array.h\"\n> >  \n> >  #define FOREACH_MSG_ID(FUNC) \\\n> >  \t/* fatal errors */ \\\n> > @@ -56,7 +57,9 @@\n> >  \tFUNC(ZERO_PADDED_FILEMODE) \\\n> >  \t/* infos (reported as warnings, but ignored by default) */ \\\n> >  \tFUNC(INVALID_TAG_NAME) \\\n> > -\tFUNC(MISSING_TAGGER_ENTRY)\n> > +\tFUNC(MISSING_TAGGER_ENTRY) \\\n> > +\t/* special value */ \\\n> > +\tFUNC(SKIP_LIST)\n> \n> This feels like a kludge to me without comment on what \"special\n> value\" means.  Does it mean \"this object has an error (which by\n> default is ignored) of being on the skip list?\"  Should we be able\n> to optionally warn an object on the skip-list exists with the same\n> mechansim the rest of the series uses to tweak the error level?\n\nI addressed both concerns – I hope... ;-)\n\nCiao,\nDscho"},{"id":"253959","messageId":"alpine.DEB.1.00.1412222319370.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqzjavgym5.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T22:25:08Z","receivedAt":"2014-12-22T22:25:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Wed, 10 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > We already have support in `git receive-pack` to deal with some legacy\n> > repositories which have non-fatal issues.\n> >\n> > Let's make `git fsck` itself useful with such repositories, too, by\n> > allowing users to ignore known issues, or at least demote those issues\n> > to mere warnings.\n> >\n> > Example: `git -c fsck.missing-email=ignore fsck` would hide problems with\n> > missing emails in author, committer and tagger lines.\n> \n> Hopefully I do not have to repeat myself, but please do not have\n> punctuations in the first and the last level of configuration variable\n> names, i.e. fsck.missingEmail, not mising-email.\n\nI vetted the complete patch series and think I caught them all.\n\nOr do you want the error messages to also use camelCased IDs, i.e.\n\n\twarning in tag $tag: missingTaggerEntry: invalid format ...\n\ninstead of\n\n\twarning in tag $tag: missing-tagger-entry: invalid format ...\n\n? It is easy to do, but looks slightly uglier to this developer's eyes...\n\n> Should these be tied to receive-pack ones in any way?  E.g. if you\n> set fsck.missingEmail to ignore, you do not have to do the same for\n> receive and accept a push with the specific error turned off?\n> \n> Not a rhetorical question.  I can see it argued both ways.  The\n> justification to defend the position of not tying these two I would\n> have is so that I can be more strict to newer breakages (i.e. not\n> accepting a push that introduce a new breakage by not ignoring with\n> receive.fsck.*) while allowing breakages that are already present.\n> The justification for the opposite position is to make it more\n> convenient to write a consistent policy.  Whichever way is chosen,\n> we would want to see the reason left in the log message so that\n> people do not have to wonder what the original motivation was when\n> they decide if it is a good idea to change this part of the code.\n\nHmm. I really tried very hard to separate the fsck.* from the receive.*\nsettings because the two code paths already behave differently: many\nwarnings reported (and ignored) by fsck are fatal errors when pushing with\nreceive.fsckObjects=true. My understanding was that these differences are\ndeliberate, and my interpretation was that the fsck and the receive\nsettings were considered to be fundamentally different, even if the same\nfsck machinery performs the validation.\n\nIf you agree, I would rephrase this line of argument and add it to the\ncommit message. Do you agree?\n\nCiao,\nDscho\n"},{"id":"253961","messageId":"alpine.DEB.1.00.1412222330080.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqfvc78hwq.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T22:32:44Z","receivedAt":"2014-12-22T22:32:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 22 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> In other words, at some point wouldn't we be better off with\n> >> something like this\n> >> \n> >> \tstruct {\n> >>         \tenum id;\n> >>                 const char *id_string;\n> >>                 enum error_level { FSCK_PASS, FSCK_WARN, FSCK_ERROR };\n> >> \t} possible_fsck_errors[];\n> >\n> > I considered that, and Michael Haggerty also suggested that in a private\n> > mail. However, I find that there is a clear hierarchy in the default\n> > messages: fatal errors, errors, warnings and infos.\n> \n> I am glad I am not alone ;-)\n> \n> These classes are ordered from more severe to less, but I do not\n> think it makes much sense to force the default view of \"if you\n> customize to demote a questionable Q that is classified as an error\n> by default as an warning, you must demote all the other ones that we\n> deem less serious than Q, which come earlier (or later---I do not\n> remember which) in our predefined list\".  So in that sense, I do not\n> consider that various kinds of questionables fsck can detect are\n> hierarchical at all.\n\nOh, but please understand that this hierarchy only applies to the default\nsettings. All of these settings can be overridden individually – and the\nfirst override will initialize a full array with the default settings.\n\nSo the order really only plays a role for the defaults, no more.\n\n> I do agree that it makes it easier to code the initialization of\n> such an array to have \"up to this point we assign the level 'fatal\n> error' by default\" constants.  Then the initialization can become\n> \n> \tfor (i = 0; i < FIRST_WARN; i++)\n>         \tpossible_fsck_errors[i].error_level = FSCK_INFO;\n> \twhile (i < FIRST_ERROR)\n>         \tpossible_fsck_errors[i++].error_level = FSCK_WARN;\n> \twhile (i < ARRAY_SIZE(possible_fsck_errors))\n>         \tpossible_fsck_errors[i++].error_level = FSCK_ERROR;\n> \n> or something.  So I am not against the FIRST_WARNING constant at\n> all, but I find it very questionable in a fully customizable system\n> to use such a constant anywhere other than the initialization time.\n\nThis is indeed the case. The code we are discussing comes after the\n\n\tif (options->strict_mode)\n\t\treturn options->strict_mode[msg_id];\n\nIn other words, once the overrides are in place, the default settings are\nskipped entirely.\n\nCiao,\nDscho"},{"id":"253962","messageId":"xmqq38878gao.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412222319370.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-22T22:34:39Z","receivedAt":"2014-12-22T22:34:39Z","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> Or do you want the error messages to also use camelCased IDs, i.e.\n>\n> \twarning in tag $tag: missingTaggerEntry: invalid format ...\n>\n> instead of\n>\n> \twarning in tag $tag: missing-tagger-entry: invalid format ...\n>\n> ? It is easy to do, but looks slightly uglier to this developer's eyes...\n\nDo you really need to know what I think?  Can I say \"The latter is\nprobably slightly better, but both look ugly to me\"?\n\nIf we want a human readable message\n\n    \"warning: tag object lacks tagger field '$tag'\"\n\nwould be my preference.\n\nBut I personally think it may not be necessary to give a pretty\nmessage that later can go through l10n here.  If we take that\nposition, it is just a machine-readble error token, so I'd say\nwhichever way you find more readable is OK.\n\n>> Should these be tied to receive-pack ones in any way?  E.g. if you\n>> set fsck.missingEmail to ignore, you do not have to do the same for\n>> receive and accept a push with the specific error turned off?\n>> \n>> Not a rhetorical question.  I can see it argued both ways.  The\n>> justification to defend the position of not tying these two I would\n>> have is so that I can be more strict to newer breakages (i.e. not\n>> accepting a push that introduce a new breakage by not ignoring with\n>> receive.fsck.*) while allowing breakages that are already present.\n>> The justification for the opposite position is to make it more\n>> convenient to write a consistent policy.  Whichever way is chosen,\n>> we would want to see the reason left in the log message so that\n>> people do not have to wonder what the original motivation was when\n>> they decide if it is a good idea to change this part of the code.\n>\n> Hmm. I really tried very hard to separate the fsck.* from the receive.*\n> settings because the two code paths already behave differently:...\n>\n> If you agree, I would rephrase this line of argument and add it to the\n> commit message. Do you agree?\n\nYeah, that reasoning sounds sensible.\n\nThanks.\n"},{"id":"253963","messageId":"alpine.DEB.1.00.1412222333340.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqbnmv8h6c.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 14/18] fsck: allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T22:39:48Z","receivedAt":"2014-12-22T22:39:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 22 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Wed, 10 Dec 2014, Junio C Hamano wrote:\n> >\n> >> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> >> \n> >> > The 'invalid tag name' and 'missing tagger entry' warnings can now be\n> >> > upgraded to errors by setting receive.fsck.invalid-tag-name and\n> >> > receive.fsck.missing-tagger-entry to 'error'.\n> >> \n> >> Hmm, why can't all warnings promotable to errors, or are the above\n> >> two mentioned only as examples?\n> >\n> > Those were the only ones that were always shown as warnings but never\n> > treated as errors.\n> \n> Sorry but I don't quite understand this comment; I suspect the root\n> cause might be that we have different mental models on these\n> tweakable error severities.\n> \n> Because I come from the school \"To these N kinds of events you can\n> independently assign different (i.e. info, warn, error) outcomes\",\n> moving the FIRST_{INFO,WARNING,...} position in the array would only\n> affect what happens by default, never hindering the user's ability\n> to tweak (in other words, there is no linkage between \"now you can\n> tweak\" and the order of events in the list, the latter of which only\n> would affect what the default severity of each event is).\n\nWe agree on this mental model.\n\nThe only problem this patch tries to fix is that the warnings about a\nmissing tagger and about invalid tag names were never leading to an error.\nThey were purely printed, but then ignored. So what this patch does is to\nadd \"if (err) return err;\" handling for those two warnings.\n\nAs a consequence, the ordering of message IDs needs to be fixed because\nthe non-fatal warnings were ordered alphabetically before, but now the\nnon-fatal warnings are extracted so that we can give them the appropriate\nFSCK_WARN by defauly – even in the git-receive-pack case.\n\nIn other words, the value assigned to those two warnings was completely\nignored before, which was the reason why it did not matter that we\nassigned them to report FSCK_ERRORs in the git-receive-pack case before:\nthey were still only printed out and never stopped any tag from entering\nthe host's repository.\n\nI could change the ordering in the patch that introduces the message IDs,\nof course, but it would be even more puzzling if those two messages, of\nall, were not ordered alphabetically with the others.\n\nCiao,\nDscho"},{"id":"253964","messageId":"xmqqy4pz71g7.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412222330080.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-22T22:40:40Z","receivedAt":"2014-12-22T22:40:40Z","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> Hi Junio,\n>\n> On Mon, 22 Dec 2014, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>> \n>> >> In other words, at some point wouldn't we be better off with\n>> >> something like this\n>> >> \n>> >> \tstruct {\n>> >>         \tenum id;\n>> >>                 const char *id_string;\n>> >>                 enum error_level { FSCK_PASS, FSCK_WARN, FSCK_ERROR };\n>> >> \t} possible_fsck_errors[];\n>> >\n>> > I considered that, and Michael Haggerty also suggested that in a private\n>> > mail. However, I find that there is a clear hierarchy in the default\n>> > messages: fatal errors, errors, warnings and infos.\n>> \n>> I am glad I am not alone ;-)\n>> ...\n> Oh, but please understand that this hierarchy only applies to the default\n> settings. All of these settings can be overridden individually – and the\n> first override will initialize a full array with the default settings.\n\nBut that means that the runtime needs to switch between two code\nwith and without override, no?\n\n> \tif (options->strict_mode)\n> \t\treturn options->strict_mode[msg_id];\n\nIn other words, I think this is misleading and unnecessary\noptimization for the \"full array\" allocation.  A code that uses an\narray of a struct like the above that Michael and I independently\nsuggested would initialize once with or without an override and then\nat the runtime there is no \"if the array is there use it\"\nconditional.\n\nI do not know why Michael suggested the same thing, but the reason\nwhy I prefer that arrangement is because I think it would be easier\nto read and maintain.\n\nThanks.\n"},{"id":"253965","messageId":"alpine.DEB.1.00.1412222344250.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqq38878gao.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T22:46:32Z","receivedAt":"2014-12-22T22:46:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 22 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > Or do you want the error messages to also use camelCased IDs, i.e.\n> >\n> > \twarning in tag $tag: missingTaggerEntry: invalid format ...\n> >\n> > instead of\n> >\n> > \twarning in tag $tag: missing-tagger-entry: invalid format ...\n> >\n> > ? It is easy to do, but looks slightly uglier to this developer's eyes...\n> \n> Do you really need to know what I think?\n\nWell, but yes ;-)\n\n> Can I say \"The latter is probably slightly better, but both look ugly to\n> me\"?\n\nOf course you can say that! ;-) The problem these ugly messages try to\nsolve is to give the user a hint which setting to change if they want to\noverride the default behavior, though...\n\n> If we want a human readable message\n> \n>     \"warning: tag object lacks tagger field '$tag'\"\n> \n> would be my preference.\n> \n> But I personally think it may not be necessary to give a pretty\n> message that later can go through l10n here.  If we take that\n> position, it is just a machine-readble error token, so I'd say\n> whichever way you find more readable is OK.\n\nOkay, I will leave it as-is, then.\n\n> >> Should these be tied to receive-pack ones in any way?  E.g. if you\n> >> set fsck.missingEmail to ignore, you do not have to do the same for\n> >> receive and accept a push with the specific error turned off?\n> >> \n> >> Not a rhetorical question.  I can see it argued both ways.  The\n> >> justification to defend the position of not tying these two I would\n> >> have is so that I can be more strict to newer breakages (i.e. not\n> >> accepting a push that introduce a new breakage by not ignoring with\n> >> receive.fsck.*) while allowing breakages that are already present.\n> >> The justification for the opposite position is to make it more\n> >> convenient to write a consistent policy.  Whichever way is chosen,\n> >> we would want to see the reason left in the log message so that\n> >> people do not have to wonder what the original motivation was when\n> >> they decide if it is a good idea to change this part of the code.\n> >\n> > Hmm. I really tried very hard to separate the fsck.* from the receive.*\n> > settings because the two code paths already behave differently:...\n> >\n> > If you agree, I would rephrase this line of argument and add it to the\n> > commit message. Do you agree?\n> \n> Yeah, that reasoning sounds sensible.\n\nI added this paragraph:\n\n    In the same spirit that `git receive-pack`'s usage of the fsck machinery\n    differs from `git fsck`'s – some of the non-fatal warnings in `git fsck`\n    are fatal with `git receive-pack` when receive.fsckObjects = true, for\n    example – we strictly separate the fsck.* from the receive.fsck.*\n    settings.\n\nCiao,\nDscho"},{"id":"253966","messageId":"xmqqtx0n710m.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412222344250.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-22T22:50:01Z","receivedAt":"2014-12-22T22:50: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> Of course you can say that! ;-) The problem these ugly messages try to\n> solve is to give the user a hint which setting to change if they want to\n> override the default behavior, though...\n\nAhh, OK, then dashed form would not work as a configuration variable\nnames, so missingTaggerEntry would be the only usable option.\n\nThanks.\n"},{"id":"253968","messageId":"alpine.DEB.1.00.1412222348040.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqy4pz71g7.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T22:55:16Z","receivedAt":"2014-12-22T22:55:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 22 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Mon, 22 Dec 2014, Junio C Hamano wrote:\n> >\n> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> >> \n> >> >> In other words, at some point wouldn't we be better off with\n> >> >> something like this\n> >> >> \n> >> >> \tstruct {\n> >> >>         \tenum id;\n> >> >>                 const char *id_string;\n> >> >>                 enum error_level { FSCK_PASS, FSCK_WARN, FSCK_ERROR };\n> >> >> \t} possible_fsck_errors[];\n> >> >\n> >> > I considered that, and Michael Haggerty also suggested that in a private\n> >> > mail. However, I find that there is a clear hierarchy in the default\n> >> > messages: fatal errors, errors, warnings and infos.\n> >> \n> >> I am glad I am not alone ;-)\n> >> ...\n> > Oh, but please understand that this hierarchy only applies to the default\n> > settings. All of these settings can be overridden individually – and the\n> > first override will initialize a full array with the default settings.\n> \n> But that means that the runtime needs to switch between two code\n> with and without override, no?\n> \n> > \tif (options->strict_mode)\n> > \t\treturn options->strict_mode[msg_id];\n> \n> In other words, I think this is misleading and unnecessary\n> optimization for the \"full array\" allocation.  A code that uses an\n> array of a struct like the above that Michael and I independently\n> suggested would initialize once with or without an override and then\n> at the runtime there is no \"if the array is there use it\"\n> conditional.\n> \n> I do not know why Michael suggested the same thing, but the reason\n> why I prefer that arrangement is because I think it would be easier\n> to read and maintain.\n\nWell, I disagree that it would be easier to maintain, because it appears\nto me that the clear hierarchy keeps things simple. For example if some\nclearly fatal error is clustered with non-fatal ones due to alphabetical\nordering, it is much harder to spot when it is marked as a demoteable\nerror by mistake.\n\nFor example, try to spot the error here:\n\n\t...\n\tF(ALMOST_HAPPY, INFO) \\\n\tF(CANNOT_RECOVER, ERROR) \\\n\tF(COFFEE_IS_EMPTY, WARN) \\\n\tF(JUST_BEING_CHATTY, INFO) \\\n\tF(LIFE_IS_GOOD, INFO) \\\n\tF(MISSING_SOMETHING_VITAL, FATAL_ERROR) \\\n\tF(NEED_TO_SLEEP, WARN) \\\n\tF(SOMETHING_WENT_WRONG, ERROR) \\\n\t...\n\nPersonally, I find it very, very hard to spot that CANNOT_RECOVER is\nmarked as a mere ERROR instead of a FATAL_ERROR. Even if it is nicely\nalphabetically ordered.\n\nI will sleep over this, though. Maybe I can come up with a solution that\nmakes all three of us happy.\n\nCiao,\nDscho"},{"id":"253969","messageId":"alpine.DEB.1.00.1412222356400.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqtx0n710m.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-22T22:57:13Z","receivedAt":"2014-12-22T22:57:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 22 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > Of course you can say that! ;-) The problem these ugly messages try to\n> > solve is to give the user a hint which setting to change if they want to\n> > override the default behavior, though...\n> \n> Ahh, OK, then dashed form would not work as a configuration variable\n> names, so missingTaggerEntry would be the only usable option.\n\nExcept that cunning me has made it so that both missing-tagger-entry *and*\nmissingTaggerEntry work...\n\nCiao,\nDscho\n"},{"id":"253971","messageId":"xmqqppbb6zx9.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412222356400.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-22T23:13:38Z","receivedAt":"2014-12-22T23:13: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> Hi Junio,\n>\n> On Mon, 22 Dec 2014, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>> \n>> > Of course you can say that! ;-) The problem these ugly messages try to\n>> > solve is to give the user a hint which setting to change if they want to\n>> > override the default behavior, though...\n>> \n>> Ahh, OK, then dashed form would not work as a configuration variable\n>> names, so missingTaggerEntry would be the only usable option.\n>\n> Except that cunning me has made it so that both missing-tagger-entry *and*\n> missingTaggerEntry work...\n\nThen the missing-tagger-entry side needs to be dropped.  The naming\ndoes not conform to the way how we name our officially supported\nconfiguration variables.\n"},{"id":"253972","messageId":"xmqqlhlz6zti.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412222348040.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-22T23:15:53Z","receivedAt":"2014-12-22T23:15:53Z","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> For example, try to spot the error here:\n>\n> \t...\n> \tF(ALMOST_HAPPY, INFO) \\\n> \tF(CANNOT_RECOVER, ERROR) \\\n> \tF(COFFEE_IS_EMPTY, WARN) \\\n> \tF(JUST_BEING_CHATTY, INFO) \\\n> \tF(LIFE_IS_GOOD, INFO) \\\n> \tF(MISSING_SOMETHING_VITAL, FATAL_ERROR) \\\n> \tF(NEED_TO_SLEEP, WARN) \\\n> \tF(SOMETHING_WENT_WRONG, ERROR) \\\n> \t...\n\nBut that is not what is being suggested at all.  I already said that\nFIRST_SOMETHING is fine as a measure to initialize, didn't I?\n\nI am only saying that if you have a place to store customized level,\nyou should initialize that part with default levels and always look\nit up from that place at runtime.  It is perfectly fine for the\ninitialization step to take advantage of the ordering and\nFIRST_SOMETHING constants.\n"},{"id":"253997","messageId":"alpine.DEB.1.00.1412231049250.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqppbb6zx9.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T09:50:50Z","receivedAt":"2014-12-23T09:50:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 22 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Mon, 22 Dec 2014, Junio C Hamano wrote:\n> >\n> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> >> \n> >> > Of course you can say that! ;-) The problem these ugly messages try to\n> >> > solve is to give the user a hint which setting to change if they want to\n> >> > override the default behavior, though...\n> >> \n> >> Ahh, OK, then dashed form would not work as a configuration variable\n> >> names, so missingTaggerEntry would be the only usable option.\n> >\n> > Except that cunning me has made it so that both missing-tagger-entry *and*\n> > missingTaggerEntry work...\n> \n> Then the missing-tagger-entry side needs to be dropped.  The naming\n> does not conform to the way how we name our officially supported\n> configuration variables.\n\nBut it does conform with the way we do our command-line parameters, and it\nwould actually cause *more* work (and more complicated code) to have two\nseparate parsers, or even to force the parser to accept only one way to\nspecify settings...\n\nShould I really introduce more complexity just to disallow non-camelCased\nconfig variables?\n\nCiao,\nDscho\n"},{"id":"253998","messageId":"alpine.DEB.1.00.1412231051320.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqlhlz6zti.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T10:53:36Z","receivedAt":"2014-12-23T10:53:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 22 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > For example, try to spot the error here:\n> >\n> > \t...\n> > \tF(ALMOST_HAPPY, INFO) \\\n> > \tF(CANNOT_RECOVER, ERROR) \\\n> > \tF(COFFEE_IS_EMPTY, WARN) \\\n> > \tF(JUST_BEING_CHATTY, INFO) \\\n> > \tF(LIFE_IS_GOOD, INFO) \\\n> > \tF(MISSING_SOMETHING_VITAL, FATAL_ERROR) \\\n> > \tF(NEED_TO_SLEEP, WARN) \\\n> > \tF(SOMETHING_WENT_WRONG, ERROR) \\\n> > \t...\n> \n> But that is not what is being suggested at all.  I already said that\n> FIRST_SOMETHING is fine as a measure to initialize, didn't I?\n> \n> I am only saying that if you have a place to store customized level,\n> you should initialize that part with default levels and always look\n> it up from that place at runtime.  It is perfectly fine for the\n> initialization step to take advantage of the ordering and\n> FIRST_SOMETHING constants.\n\nThanks for clarifying, I was worried that you wanted to encode the\nseverity levels explicitly (F(ID, LEVEL) instead of F(ID) in the correct\norder). The DRY principle also suggests that we should not encode the\nseverity level in two ways (which would leave the door open for\ninconsistencies). That means that we should not initialize a static array\nof severity levels, but initialize the array using a loop.\n\nOkay, now that we have established that the initial ordering by severity\nmakes sense, let's examine the initialization step.\n\nBasically, our approaches differ only in *when* to initialize that array\nof severity levels: you want to initialize it always, and I want to\ninitialize it only when the severity levels are customized by the caller.\n\nNow, let's have a look how the fsck_options are currently initialized. My\ncode follows the convention established with strbufs, argv_arrays, etc:\nthere is a preprocessor definition (_DEFAULT, imitating the _INIT\ndefinitions) that allows us to initialize such structs very conveniently.\nPlease note that no loop is required, and certainly no extra code has to\nbe called to initialize the struct. We get away with initializing that\narray lazily in the fsck_strict_mode() function when we detect that it\nneeds to be initialized, being populated by the very same function that\nprovides the default settings before customization. This is a very robust\nsetup as the knowledge about, say, the size of that array is confined\nstrictly to fsck.c.\n\nHowever, if we had to change the lookup such that it uses an array always,\nwe would have to introduce a function to initialize the struct, always, in\nparticular we would have to find a place to call that initialization\nfunction in, say, builtin/fsck.c (actually, in every code path that calls\ninto the fsck machinery). Arguably, the code would get more complex –\nintroducing new call paths just to initialize the fsck_options struct –\nand I would argue further that there is no gain from an elegance,\nreadability and maintenance point of view: whether the array is\ninitialized lazily or not, it will be initialized the exact same way. All\nit means is that we have to introduce separate code paths because we would\nseparate explicitly the initialization from the configuration step.\n\nTherefore, I do not believe that introducing an fsck_options_init() is\nwhat you would really want.\n\nAn alternative would be to initialize the array at compile time – we would\nhave to violate the DRY principle for that, repeating the severity levels\nmany times over, and we could no longer confine the visibility to the\nmessage IDs to fsck.c because not only the size of the array of severity\nlevels would have to be known to every user of fsck.h, but the exact\ndefault severity levels themselves, to be able to initialize the struct.\nBut we could initialize the struct with a known set of settings via the\n_DEFAULTS definition that way.\n\nHowever, you already expressed slight disagreement with the preprocessor\nmagic needed to initialize both the enum and the array of message ID\nstrings from the same list in a way that lets the compiler ensure\nconsistency; I am afraid that if I were to modify _DEFAULTS to populate the\nentire severity level array, the resulting code would find your utter\ncontempt.\n\nI believe, therefore, that this is also not what you want.\n\nSo that leaves only one alternative: to initialize a global array with the\ndefault severity levels at *some* stage. I have no idea what that stage\nwould be, therefore we would have to either establish ugly, and\nDRY-violating, compile time initialization, or we would have to call a\nfunction before using any of the fsck machinery – but that is fragile: it\nis too easy to forget one call path and access an uninitialized array!\nWorse, even if we *had* a fully initialized array of default severity\nlevels, we would still have to have an on-demand copy (i.e. lazy\ninitialization) of said array lest we modify the global defaults in\nfsck_strict_mode()! Essentially, we would just *add* complexity to the\ncurrent solution, not replace it with anything simpler.\n\nTherefore, I believe that you cannot be a fan of this alternative, either.\n\nIn short, I still find it much more elegant to determine the default\nseverity levels from the numeric enum values, and initialize the severity\nlevel array only on demand, than to introduce a separate call to\ninitialize the array always, in particular since we would have to execute\nthat initialization loop *all* the time in the latter case – even if we do\nnot customize, let alone look up, any value – or clutter the code with\nugly, ugly preprocessor constructs.\n\nAnd I hope that my arguments convinced you, too!\n\nCiao,\nDscho"},{"id":"254011","messageId":"xmqqr3vq49w5.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412231051320.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T16:18:50Z","receivedAt":"2014-12-23T16:18:50Z","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> However, if we had to change the lookup such that it uses an array always,\n> we would have to introduce a function to initialize the struct, always, in\n> particular we would have to find a place to call that initialization\n> function in, say, builtin/fsck.c (actually, in every code path that calls\n> into the fsck machinery).\n\nYou would need to call a function to \"initialize\" the table if you\nsupport customization by reading the configuration files anyway.  I\nam not sure why you think finding such a place is hard.  Puzzled.\n\nAlso I suspect that you can tell the compiler to initialize the\narray in place with default values, perhaps like this?\n\n-- >8 --\n#include <stdio.h>\n\n/* sorted by the default severity (lowest impact first) */\n#define EVENT_LIST(F) \\\n\tF(EVENT_A), \\\n\tF(EVENT_B), \\\n\tF(EVENT_C), \\\n\tF(EVENT_D)\n\n#define ID_(event) ID_ ## event\nenum event_id {\n\tEVENT_LIST(ID_)\n};\n\n\nenum severity_level {\n\tseverity_info, severity_warn, severity_error\n};\n\n/* below this one are INFO */\n#define FIRST_WARN_EVENT_ID\t\tID_EVENT_B\n/* below this one are WARN */\n#define FIRST_ERROR_EVENT_ID\t\tID_EVENT_C\n\n#define STRING_(s) #s\n#define DESC_(event) \\\n\t{ \\\n\t\tID_ ## event, \\\n\t\tSTRING_(event), \\\n\t\t(ID_ ## event < FIRST_WARN_EVENT_ID \\\n\t\t? severity_info \\\n\t\t: ID_ ## event < FIRST_ERROR_EVENT_ID \\\n\t\t? severity_warn \\\n\t\t: severity_error) \\\n\t}\n\nstruct event_config {\n\tenum event_id id;\n\tconst char * name;\n\tenum severity_level level;\n} event[] = {\n\tEVENT_LIST(DESC_)\n};\n\nint main(int ac, char **av)\n{\n\tint i;\n\n\tfor (i = 0; i < sizeof(event) / sizeof(event[0]); i++) {\n\t\tprintf(\"%d, %s, %d\\n\",\n\t\t       event[i].id, event[i].name, event[i].level);\n\t}\n\treturn 0;\n}\n"},{"id":"254012","messageId":"alpine.DEB.1.00.1412231723480.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqr3vq49w5.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T16:30:02Z","receivedAt":"2014-12-23T16:30:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 23 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > However, if we had to change the lookup such that it uses an array\n> > always, we would have to introduce a function to initialize the\n> > struct, always, in particular we would have to find a place to call\n> > that initialization function in, say, builtin/fsck.c (actually, in\n> > every code path that calls into the fsck machinery).\n> \n> You would need to call a function to \"initialize\" the table if you\n> support customization by reading the configuration files anyway.\n\nYes, this is the config machinery. But I need employ that only if I want\nto let the caller customize the severity levels. However, the fsck\nmachinery is also called from places where such a customization is not\noffered. They would now need to be changed, too.\n\n> Also I suspect that you can tell the compiler to initialize the\n> array in place with default values, perhaps like this?\n> \n> -- >8 --\n> #include <stdio.h>\n> \n> /* sorted by the default severity (lowest impact first) */\n> #define EVENT_LIST(F) \\\n> \tF(EVENT_A), \\\n> \tF(EVENT_B), \\\n> \tF(EVENT_C), \\\n> \tF(EVENT_D)\n> \n> #define ID_(event) ID_ ## event\n> enum event_id {\n> \tEVENT_LIST(ID_)\n> };\n> \n> \n> enum severity_level {\n> \tseverity_info, severity_warn, severity_error\n> };\n> \n> /* below this one are INFO */\n> #define FIRST_WARN_EVENT_ID\t\tID_EVENT_B\n> /* below this one are WARN */\n> #define FIRST_ERROR_EVENT_ID\t\tID_EVENT_C\n> \n> #define STRING_(s) #s\n> #define DESC_(event) \\\n> \t{ \\\n> \t\tID_ ## event, \\\n> \t\tSTRING_(event), \\\n> \t\t(ID_ ## event < FIRST_WARN_EVENT_ID \\\n> \t\t? severity_info \\\n> \t\t: ID_ ## event < FIRST_ERROR_EVENT_ID \\\n> \t\t? severity_warn \\\n> \t\t: severity_error) \\\n> \t}\n\nThis is exactly the ugly, ugly preprocessor construct I thought you would\nmeet with contempt. I mean, compared to this, my FUNC() hack is outright\npretty ;-)\n\nAnd *still*, this is *just* a global table with defaults. I would *still*\nneed to copy-on-write when the first customization of the severity level\ntakes place because I cannot allow the global defaults to be modified by\none caller (that would defeat the whole purpose of having per-caller\nsettings bundled in the fsck_options struct).\n\nYou see, I still would need to have a lazy initialization, the complexity\nin that part would not be reduced at all.\n\nSo I am afraid that this approach really adds complexity rather than\nreplacing it with something simpler than my current code.\n\nCiao,\nDscho\n"},{"id":"254013","messageId":"xmqqmw6e499u.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412231049250.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T16:32:13Z","receivedAt":"2014-12-23T16:32:13Z","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>> >> > Of course you can say that! ;-) The problem these ugly messages try to\n>> >> > solve is to give the user a hint which setting to change if they want to\n>> >> > override the default behavior, though...\n>> >> \n>> >> Ahh, OK, then dashed form would not work as a configuration variable\n>> >> names, so missingTaggerEntry would be the only usable option.\n>> >\n>> > Except that cunning me has made it so that both missing-tagger-entry *and*\n>> > missingTaggerEntry work...\n>> \n>> Then the missing-tagger-entry side needs to be dropped.  The naming\n>> does not conform to the way how we name our officially supported\n>> configuration variables.\n>\n> But it does conform with the way we do our command-line parameters,\n\nHmmm....  What is the expected user interaction?  The way I read the\nseries was ($MISSING_TAGGER stands for the \"token\" we choose to show):\n\n    (1) The user runs fsck without customization, and may see a\n\twarning (or error):\n\n        $ git fsck\n        error in tag d6602ec5194c87b0fc87103ca4d67251c76f233a: $MISSING_TAGGER\n\n    (2) The user demotes it to warning and runs fsck again:\n\n\t$ git config fsck.$MISSING_TAGGER warn\n        $ git fsck\n        warning: tag d6602ec5194c87b0fc87103ca4d67251c76f233a: $MISSING_TAGGER\n\nI suspect that it would be much better if the configuration\nvariables were organized the other way around, e.g.\n\n\t$ git config fsck.warn missingTagger,someOtherKindOfError\n\nThen a one-shot override would make sense and easier to give as\ncommand line option, e.g.\n\n\t$ git fsck --warn=missingTagger,someOtherKindOfError\n\nBut the proposed organization to use one variable per questionable\nevent type (as opposed to one variable per severity level) would\nlead to a one-shot override of this form, e.g.\n\n\t$ git fsck --missing-tagger=warn --some-other-kind-of-error=warn\n\nwhich I think is insane to require us to support unbound number of\ndashed options.\n\nOr are you saying that we allow \"git config core.file-mode true\"\nfrom the command line to set core.fileMode configuration?\n\nPuzzled...\n"},{"id":"254015","messageId":"alpine.DEB.1.00.1412231736490.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqmw6e499u.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T16:47:07Z","receivedAt":"2014-12-23T16:47:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 23 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> >> > Of course you can say that! ;-) The problem these ugly messages\n> >> >> > try to solve is to give the user a hint which setting to change\n> >> >> > if they want to override the default behavior, though...\n> >> >> \n> >> >> Ahh, OK, then dashed form would not work as a configuration\n> >> >> variable names, so missingTaggerEntry would be the only usable\n> >> >> option.\n> >> >\n> >> > Except that cunning me has made it so that both\n> >> > missing-tagger-entry *and* missingTaggerEntry work...\n> >> \n> >> Then the missing-tagger-entry side needs to be dropped.  The naming\n> >> does not conform to the way how we name our officially supported\n> >> configuration variables.\n> >\n> > But it does conform with the way we do our command-line parameters,\n> \n> Hmmm....  What is the expected user interaction?  The way I read the\n> series was ($MISSING_TAGGER stands for the \"token\" we choose to show):\n> \n>     (1) The user runs fsck without customization, and may see a\n> \twarning (or error):\n> \n>         $ git fsck\n>         error in tag d6602ec5194c87b0fc87103ca4d67251c76f233a: $MISSING_TAGGER\n> \n>     (2) The user demotes it to warning and runs fsck again:\n> \n> \t$ git config fsck.$MISSING_TAGGER warn\n>         $ git fsck\n>         warning: tag d6602ec5194c87b0fc87103ca4d67251c76f233a: $MISSING_TAGGER\n\nThe intended use case is actually when receive.fsckObjects = true and you\ncall `git push`, seeing 'remote: error: $MULTIPLE_AUTHORS: ...'.\n\nNow, the $MULTIPLE_AUTHORS *config* setting is parsed by `git\nreceive-pack`, but that is not the command that needs to customize the\nfsck call: it is either `git index-pack` or `git unpack-objects`. So what\n`git receive-pack` does is to pass the config options as command-line\noptions to the called command. For consistency with the rest of Git, the\ncommand-line options were *not* camel-cased, but lower-case,\ndash-separated.\n\nThe parser I wrote actually accepts both versions, allowing me to skip the\ntedious step to convert the camelCased config setting into a\nlower-case-dashed version to pass to `index-pack` or `unpack-objects`,\nonly to be parsed by the same parser as `fsck` would use directly.\n\nSo I am rather happy with the fact that the parser handles both camelCased\nand lower-case-dashed versions.\n\n> I suspect that it would be much better if the configuration variables\n> were organized the other way around, e.g.\n> \n> \t$ git config fsck.warn missingTagger,someOtherKindOfError\n\nI had something similar in an earlier version of my patch series, but it\nwas shot down rightfully: if you want to allow inheriting defaults from\n$HOME/.gitconfig, you have to configure the severity levels individually.\n\n(The current solution also sidesteps the problematic situation when both\nfsck.warn *and* fsck.error contain, say, missingTagger.)\n\n> Then a one-shot override would make sense and easier to give as\n> command line option, e.g.\n> \n> \t$ git fsck --warn=missingTagger,someOtherKindOfError\n\nYep, my first implementation actually used\n`--strict=missing-tagger,-some-demoted-error`. But as I mentioned above,\nthat approach is not as flexible as the current one.\n\n> But the proposed organization to use one variable per questionable\n> event type (as opposed to one variable per severity level) would\n> lead to a one-shot override of this form, e.g.\n> \n> \t$ git fsck --missing-tagger=warn --some-other-kind-of-error=warn\n> \n> which I think is insane to require us to support unbound number of\n> dashed options.\n\nThe intended use case is actually *not* the command-line, but the config\nfile, in particular allowing /etc/gitconfig, $HOME/.gitconfig *and*\n.git/config to customize the settings.\n\n> Or are you saying that we allow \"git config core.file-mode true\"\n> from the command line to set core.fileMode configuration?\n\nI do not understand this reference. I did not suggest to change `git\nconfig`, did I? If I did, I apologize; it was definitely *not* my\nintention to change long-standing customs.\n\nCiao,\nDscho\n"},{"id":"254017","messageId":"xmqqegrq47n1.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"xmqqmw6e499u.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T17:07:30Z","receivedAt":"2014-12-23T17:07:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I suspect that it would be much better if the configuration\n> variables were organized the other way around, e.g.\n>\n> \t$ git config fsck.warn missingTagger,someOtherKindOfError\n\nBy the way, I think I like this organization is much better than the\nother way around, i.e. \"fsck.missingTagger=warn\", as we do not want\nthe namespace under fsck.* for variables that control the behaviour\nof fsck that are *NOT* kinds of questionable conditions fsck can\nfind.\n"},{"id":"254019","messageId":"xmqqa92e47ap.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412231736490.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T17:14:54Z","receivedAt":"2014-12-23T17:14:54Z","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> The parser I wrote actually accepts both versions, allowing me to skip the\n> tedious step to convert the camelCased config setting into a\n> lower-case-dashed version to pass to `index-pack` or `unpack-objects`,\n> only to be parsed by the same parser as `fsck` would use directly.\n>\n> So I am rather happy with the fact that the parser handles both camelCased\n> and lower-case-dashed versions.\n\nThat is myopic view of the world that ignores maintainability and\nteachability, doing disservice to our user base.\n\nWhat message does it send to an unsuspecting new user that\nfsck.random-error is silently accepted (because we will never\ndocument it) as an alias for fsck.randomError, while most of the\nconfiguration variables will not take such an alias?\n\n>> I suspect that it would be much better if the configuration variables\n>> were organized the other way around, e.g.\n>> \n>> \t$ git config fsck.warn missingTagger,someOtherKindOfError\n>\n> I had something similar in an earlier version of my patch series, but it\n> was shot down rightfully: if you want to allow inheriting defaults from\n> $HOME/.gitconfig, you have to configure the severity levels individually.\n\nHmmm.  What's wrong with \"fsck.warn -missingTagger\" that overrides\nthe earlier one, or even \"fsck.info missingTagger\" after having\n\"fsck.warn other,missingTagger,yetanother\", with the usual \"last one\nwins\" rule?\n\nWhoever shot it down \"rightfully\" is wrong here, I would think.\n\n>> But the proposed organization to use one variable per questionable\n>> event type (as opposed to one variable per severity level) would\n>> lead to a one-shot override of this form, e.g.\n>> \n>> \t$ git fsck --missing-tagger=warn --some-other-kind-of-error=warn\n>> \n>> which I think is insane to require us to support unbound number of\n>> dashed options.\n>\n> The intended use case is actually *not* the command-line, but the config\n> file, in particular allowing /etc/gitconfig, $HOME/.gitconfig *and*\n> .git/config to customize the settings.\n\nBut we do need to worry about one-shot override from the command\nline.  A configuration that sticks without a way to override is a\nno-no.\n\n>> Or are you saying that we allow \"git config core.file-mode true\"\n>> from the command line to set core.fileMode configuration?\n>\n> I do not understand this reference.\n\nI was puzzled by your \"command line\" and wondering if you meant\n\"from the command line, aVariable can be spelled a-variable\".\n\n> I did not suggest to change `git\n> config`, did I? If I did, I apologize; it was definitely *not* my\n> intention to change long-standing customs.\n\nThen fsck.missing-tagger is definitely out.  Long standing customs\nis that a multi-word token at the first and the last level is not\ndashed-multi-word.\n"},{"id":"254020","messageId":"xmqq61d24724.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412231723480.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T17:20:03Z","receivedAt":"2014-12-23T17:20:03Z","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> And *still*, this is *just* a global table with defaults. I would *still*\n> need to copy-on-write when the first customization of the severity level\n> takes place because I cannot allow the global defaults to be modified by\n> one caller (that would defeat the whole purpose of having per-caller\n> settings bundled in the fsck_options struct).\n\nIf you allocate a per-invocation fsck_options struct, then the\ninitialization the default with code is dead easy---you can just do\nit immediately after you x[cm]alloc()---no?\n\nWhat am I missing?\n"},{"id":"254021","messageId":"alpine.DEB.1.00.1412231828050.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqq61d24724.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T17:28:57Z","receivedAt":"2014-12-23T17:28:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 23 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > And *still*, this is *just* a global table with defaults. I would *still*\n> > need to copy-on-write when the first customization of the severity level\n> > takes place because I cannot allow the global defaults to be modified by\n> > one caller (that would defeat the whole purpose of having per-caller\n> > settings bundled in the fsck_options struct).\n> \n> If you allocate a per-invocation fsck_options struct, then the\n> initialization the default with code is dead easy---you can just do\n> it immediately after you x[cm]alloc()---no?\n\nThere is no alloc. Right now, the initialization reads:\n\n\tstruct fsck_options options = strict ?\n\t\tFSCK_OPTIONS_STRICT : FSCK_OPTIONS_DEFAULT;\n\nCiao,\nDscho\n"},{"id":"254022","messageId":"alpine.DEB.1.00.1412231829020.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqa92e47ap.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T17:41:38Z","receivedAt":"2014-12-23T17:41:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 23 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > The parser I wrote actually accepts both versions, allowing me to skip\n> > the tedious step to convert the camelCased config setting into a\n> > lower-case-dashed version to pass to `index-pack` or `unpack-objects`,\n> > only to be parsed by the same parser as `fsck` would use directly.\n> >\n> > So I am rather happy with the fact that the parser handles both\n> > camelCased and lower-case-dashed versions.\n> \n> That is myopic view of the world that ignores maintainability and\n> teachability, doing disservice to our user base.\n\nOkay, so just to clarify: you want me to\n\n- split the parser into\n\n\t- a parser that accepts only camelCased variable names when they\n\t  come from the config (for use in fsck and receive-pack), and\n\n\t- another parser that rejects camelCased variable names and only\n\t  accepts lower-case-dashed, intended for command-line parsing\n\t  in fsck, index-pack and unpack-objects, and\n\n- consequently have a converter from the camelCased variable names we\n  receive from the config in receive-pack so we can pass lower-case-dashed\n  settings to index-pack and unpack-objects.\n\nIf you want it this way, I will do it this way.\n\n> What message does it send to an unsuspecting new user that\n> fsck.random-error is silently accepted (because we will never document\n> it) as an alias for fsck.randomError, while most of the configuration\n> variables will not take such an alias?\n\nI will not participate in a discussion about consistency again. There is\nnothing that can be done about it; what matters is what you will accept\nand what not. I will make the code stricter (and consequently more\ncomplex) if that is what you want.\n\n> >> I suspect that it would be much better if the configuration variables\n> >> were organized the other way around, e.g.\n> >> \n> >> \t$ git config fsck.warn missingTagger,someOtherKindOfError\n> >\n> > I had something similar in an earlier version of my patch series, but\n> > it was shot down rightfully: if you want to allow inheriting defaults\n> > from $HOME/.gitconfig, you have to configure the severity levels\n> > individually.\n> \n> Hmmm.  What's wrong with \"fsck.warn -missingTagger\" that overrides\n> the earlier one, or even \"fsck.info missingTagger\" after having\n> \"fsck.warn other,missingTagger,yetanother\", with the usual \"last one\n> wins\" rule?\n\nI will change the code (next year...).\n\n> >> But the proposed organization to use one variable per questionable\n> >> event type (as opposed to one variable per severity level) would lead\n> >> to a one-shot override of this form, e.g.\n> >> \n> >> \t$ git fsck --missing-tagger=warn --some-other-kind-of-error=warn\n> >> \n> >> which I think is insane to require us to support unbound number of\n> >> dashed options.\n> >\n> > The intended use case is actually *not* the command-line, but the config\n> > file, in particular allowing /etc/gitconfig, $HOME/.gitconfig *and*\n> > .git/config to customize the settings.\n> \n> But we do need to worry about one-shot override from the command\n> line.  A configuration that sticks without a way to override is a\n> no-no.\n\nAnd of course you can, by specifying the config setting via the -c\ncommand-line option. The only inconsistency here is that all other\ncommand-line options are lower-case-dashed, while the config settings are\ncamelCased.\n\n> >> Or are you saying that we allow \"git config core.file-mode true\" from\n> >> the command line to set core.fileMode configuration?\n> >\n> > I do not understand this reference.\n> \n> I was puzzled by your \"command line\" and wondering if you meant\n> \"from the command line, aVariable can be spelled a-variable\".\n\nWell, of course, if you call `git -c aVariable command\n--option=a-variable` you have a nice accumulation of styles right there\n;-)\n\n> > I did not suggest to change `git config`, did I? If I did, I\n> > apologize; it was definitely *not* my intention to change\n> > long-standing customs.\n> \n> Then fsck.missing-tagger is definitely out.  Long standing customs\n> is that a multi-word token at the first and the last level is not\n> dashed-multi-word.\n\nBut I already changed all of the patches to fsck.missingTagger.\n\nThe only thing I did not do yet is to split the parser into two, one\naccepting only camelCased, one accepting only lower-case-dashed options,\nand a translator to convert from camelCase to lower-case-dashed versions\n(because it is a lot of work and additional complexity, as well as\nopportunity for bugs to hide because we'll have three code paths). I asked\nyou above whether you want that, and I will do it if you say that you do.\n\nCiao,\nDscho\n"},{"id":"254025","messageId":"xmqqwq5i2qtl.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412231829020.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T17:56:06Z","receivedAt":"2014-12-23T17:56:06Z","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> Okay, so just to clarify: you want me to\n>\n> - split the parser into\n>\n> \t- a parser that accepts only camelCased variable names when they\n> \t  come from the config (for use in fsck and receive-pack), and\n\nOK.\n\n> \t- another parser that rejects camelCased variable names and only\n> \t  accepts lower-case-dashed, intended for command-line parsing\n> \t  in fsck, index-pack and unpack-objects, and\n>\n> - consequently have a converter from the camelCased variable names we\n>   receive from the config in receive-pack so we can pass lower-case-dashed\n>   settings to index-pack and unpack-objects.\n\nI am not sure about the latter two.  This needs a design discussion\nwhat the command line options should be.\n\nI think the command line should be like this:\n\n\tgit cmd --warn=missingTags,missingAuthor\n\nin the first place, i.e. \"we may invent tokens to denote new kinds\nof errors as we improve fsck\", not with \"we may add options for new\nkinds of errors\", i.e. the command line should not look like this:\n\n\tgit cmd --missing-tags=warn --missing-author=warn\n\nAnd from that point of view, I see no reason to support the dashed\nvariant anywhere in the code, neither in the config parser or in the\ncommand line parser.\n"},{"id":"254027","messageId":"alpine.DEB.1.00.1412231903050.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqwq5i2qtl.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T18:06:57Z","receivedAt":"2014-12-23T18:06:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 23 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > Okay, so just to clarify: you want me to\n> >\n> > - split the parser into\n> >\n> > \t- a parser that accepts only camelCased variable names when they\n> > \tcome from the config (for use in fsck and receive-pack), and\n> \n> OK.\n> \n> > \t- another parser that rejects camelCased variable names and only\n> > \t  accepts lower-case-dashed, intended for command-line parsing\n> > \t  in fsck, index-pack and unpack-objects, and\n> >\n> > - consequently have a converter from the camelCased variable names we\n> >   receive from the config in receive-pack so we can pass lower-case-dashed\n> >   settings to index-pack and unpack-objects.\n> \n> I am not sure about the latter two.  This needs a design discussion\n> what the command line options should be.\n> \n> I think the command line should be like this:\n> \n> \tgit cmd --warn=missingTags,missingAuthor\n\nOkay. This contradicts the convention where Git uses lower-case-dashed\ncommand-line option values (e.g. on-demand, error-all, etc) and no\ncamelCased options were present so far. But your wish is my command.\n\nCiao,\nDscho\n> \n> in the first place, i.e. \"we may invent tokens to denote new kinds\n> of errors as we improve fsck\", not with \"we may add options for new\n> kinds of errors\", i.e. the command line should not look like this:\n> \n> \tgit cmd --missing-tags=warn --missing-author=warn\n> \n> And from that point of view, I see no reason to support the dashed\n> variant anywhere in the code, neither in the config parser or in the\n> command line parser.\n> \n"},{"id":"254028","messageId":"xmqqsig62q78.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"xmqqwq5i2qtl.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T18:09:31Z","receivedAt":"2014-12-23T18:09:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n>> Okay, so just to clarify: you want me to\n>>\n>> - split the parser into\n>>\n>> \t- a parser that accepts only camelCased variable names when they\n>> \t  come from the config (for use in fsck and receive-pack), and\n>\n> OK.\n>\n>> \t- another parser that rejects camelCased variable names and only\n>> \t  accepts lower-case-dashed, intended for command-line parsing\n>> \t  in fsck, index-pack and unpack-objects, and\n>>\n>> - consequently have a converter from the camelCased variable names we\n>>   receive from the config in receive-pack so we can pass lower-case-dashed\n>>   settings to index-pack and unpack-objects.\n>\n> I am not sure about the latter two.  This needs a design discussion\n> what the command line options should be.\n>\n> I think the command line should be like this:\n>\n> \tgit cmd --warn=missingTags,missingAuthor\n>\n> in the first place, i.e. \"we may invent tokens to denote new kinds\n> of errors as we improve fsck\", not with \"we may add options for new\n> kinds of errors\", i.e. the command line should not look like this:\n>\n> \tgit cmd --missing-tags=warn --missing-author=warn\n>\n> And from that point of view, I see no reason to support the dashed\n> variant anywhere in the code, neither in the config parser or in the\n> command line parser.\n\nHaving said that, I think \"missingTags\" etc. should not be\nconfiguration variable names (instead, they should be values).\n\nBecause of that, I do not think we need consistency between the way\nthese \"tokens that denote kinds of errors fsck denotes\" are spelled\nand the way \"configuration variable names\" are spelled.\n\nIn other words, I do not think there is nothing that comes from how\nour configuration variable names are spelled that gives preference\nto one over the other between the two styles:\n\n(1) Tokens are camelCased\n\n\t[fsck]\n\t\twarn = missingTagger,missingAuthor\n                error = zeroPadTreeEntry\n\n\t$ git cmd --warn=missingTagger,missingAuthor\n\n(2) Tokens are dashed-multi-words\n\n\t[fsck]\n\t\twarn = missing-tagger,missing-author\n                error = zero-pad-tree-entry\n\n\t$ git cmd --warn=missing-tagger,missing-author\n\nDo I have a strong preference between these two?\n\nNot really.  My gut reaction is that (2) may be easier to read, but\nI can be persuaded either way.\n\nWho else has/had opinions on this?  Earlier you said that the\nconfiguration the other way, i.e. \"[fsck] warn = missingTag\", was\nshot down---who did shoot it?  Perhaps that person may be able to\npoint out where in my thinking above I am going in the wrong\ndirection.\n\nThanks.\n\n[Footnote]\n\nIn either case, I'd recommend that we take [ ,]+ as inter-token\nseparator to ease the use on the command line and config file, to\nallow these:\n\n\t[fsck]\n\t\twarn = missingTagger missingAuthor\n\t\twarn = missingTagger,missingAuthor\n\n\t$ git cmd --warn missingTagger,missingAuthor\n\t$ git cmd --warn \"missingTagger missingAuthor\"\n"},{"id":"254029","messageId":"CAPc5daWBL89KpYqONNyvJ-t8K1P-nMJVAU1bLWkqzHGdb2q-gA@mail.gmail.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412231828050.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T18:14:14Z","receivedAt":"2014-12-23T18:14:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Tue, Dec 23, 2014 at 9:28 AM, Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>>\n>> > And *still*, this is *just* a global table with defaults. I would *still*\n>> > need to copy-on-write when the first customization of the severity level\n>> > takes place because I cannot allow the global defaults to be modified by\n>> > one caller (that would defeat the whole purpose of having per-caller\n>> > settings bundled in the fsck_options struct).\n>\n> There is no alloc. Right now, the initialization reads:\n>\n>         struct fsck_options options = strict ?\n>                 FSCK_OPTIONS_STRICT : FSCK_OPTIONS_DEFAULT;\n\nThen it is just the matter of having\n\n   fsck_options_init(&options);\n   if (strict)\n    options.some_field = make_it_strict;\n\nas the first few statements, no?\n\nI am not sure why it is so difficult....\n"},{"id":"254030","messageId":"alpine.DEB.1.00.1412231911470.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqqsig62q78.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T18:14:56Z","receivedAt":"2014-12-23T18:14:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 23 Dec 2014, Junio C Hamano wrote:\n\n> Having said that, I think \"missingTags\" etc. should not be configuration\n> variable names (instead, they should be values).\n> \n> Because of that, I do not think we need consistency between the way\n> these \"tokens that denote kinds of errors fsck denotes\" are spelled and\n> the way \"configuration variable names\" are spelled.\n\nOkay. That makes more sense.\n\nNow I can remove the complexity introduced by teaching the parser to\naccept camelCased values, and we're golden.\n\n> In either case, I'd recommend that we take [ ,]+ as inter-token\n> separator to ease the use on the command line and config file\n\nAnd this is indeed the case:\n\n+void fsck_strict_mode(struct fsck_options *options, const char *mode)\n+...\n+       while (*mode) {\n+               int len = strcspn(mode, \" ,|\"), equal, msg_id;\n+...\n\nIn other words, I even allowed the pipe symbol as separator.\n\nCiao,\nDscho\n"},{"id":"254033","messageId":"alpine.DEB.1.00.1412231922120.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"CAPc5daWBL89KpYqONNyvJ-t8K1P-nMJVAU1bLWkqzHGdb2q-gA@mail.gmail.com","subject":"Re: [PATCH 04/18] Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T18:23:19Z","receivedAt":"2014-12-23T18:23:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 23 Dec 2014, Junio C Hamano wrote:\n\n> On Tue, Dec 23, 2014 at 9:28 AM, Johannes Schindelin\n> <Johannes.Schindelin@gmx.de> wrote:\n> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> >>\n> >> > And *still*, this is *just* a global table with defaults. I would *still*\n> >> > need to copy-on-write when the first customization of the severity level\n> >> > takes place because I cannot allow the global defaults to be modified by\n> >> > one caller (that would defeat the whole purpose of having per-caller\n> >> > settings bundled in the fsck_options struct).\n> >\n> > There is no alloc. Right now, the initialization reads:\n> >\n> >         struct fsck_options options = strict ?\n> >                 FSCK_OPTIONS_STRICT : FSCK_OPTIONS_DEFAULT;\n> \n> Then it is just the matter of having\n> \n>    fsck_options_init(&options);\n>    if (strict)\n>     options.some_field = make_it_strict;\n> \n> as the first few statements, no?\n> \n> I am not sure why it is so difficult....\n\nIt is not difficult. But I try to avoid complexity when I can. Since you\nasked specifically, I will introduce it, though. Hopefully still this year\n(I'll not be available for a while starting tomorrow).\n\nCiao,\nDscho\n"},{"id":"254040","messageId":"xmqq4msm2o1n.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412231911470.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T18:56:04Z","receivedAt":"2014-12-23T18:56:04Z","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> Hi Junio,\n>\n> On Tue, 23 Dec 2014, Junio C Hamano wrote:\n>\n>> Having said that, I think \"missingTags\" etc. should not be configuration\n>> variable names (instead, they should be values).\n>> \n>> Because of that, I do not think we need consistency between the way\n>> these \"tokens that denote kinds of errors fsck denotes\" are spelled and\n>> the way \"configuration variable names\" are spelled.\n>\n> Okay. That makes more sense.\n\nI am sorry that I didn't step back and think about it earlier to\nnotice that we shouldn't be talking about configuration variable\nname syntax.  I could have saved us time going back and forth if\nI did so earlier.\n"},{"id":"254045","messageId":"alpine.DEB.1.00.1412232109120.21312@s15462909.onlinehome-server.info","threadId":"38144","inReplyTo":"xmqq4msm2o1n.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2014-12-23T20:12:06Z","receivedAt":"2014-12-23T20:12:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 23 Dec 2014, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > On Tue, 23 Dec 2014, Junio C Hamano wrote:\n> >\n> >> Having said that, I think \"missingTags\" etc. should not be\n> >> configuration variable names (instead, they should be values).\n> >> \n> >> Because of that, I do not think we need consistency between the way\n> >> these \"tokens that denote kinds of errors fsck denotes\" are spelled\n> >> and the way \"configuration variable names\" are spelled.\n> >\n> > Okay. That makes more sense.\n> \n> I am sorry that I didn't step back and think about it earlier to notice\n> that we shouldn't be talking about configuration variable name syntax.\n> I could have saved us time going back and forth if I did so earlier.\n\nDo not worry. You were just trying to make this software better, same as\nI tried.\n\nUnfortunately, I will not be able to submit v2 of this patch series this\nyear, but I will do so in the second week of January (including the change\nto the global array with the default severity levels because I do want to\nsee this feature integrated).\n\nCiao,\nDscho\n"},{"id":"254052","messageId":"xmqqmw6e12y8.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"alpine.DEB.1.00.1412232109120.21312@s15462909.onlinehome-server.info","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-12-23T21:17:03Z","receivedAt":"2014-12-23T21:17:03Z","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> Unfortunately, I will not be able to submit v2 of this patch series this\n> year, but I will do so in the second week of January (including the change\n> to the global array with the default severity levels because I do want to\n> see this feature integrated).\n\nHeh, we are not in a hurry.  Enjoy your holidays.\n\nA happy new year to you in advance ;-)\n"},{"id":"254893","messageId":"cover.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"xmqqr3w7gxr4.fsf@gitster.dls.corp.google.com","subject":"[PATCH v2 00/18] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:49:55Z","receivedAt":"2015-01-19T15:49:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At the moment, the git-fsck's integrity checks are targeted toward the\nend user, i.e. the error messages are really just messages, intended for\nhuman consumption.\n\nUnder certain circumstances, some of those errors should be allowed to\nbe turned into mere warnings, though, because the cost of fixing the\nissues might well be larger than the cost of carrying those flawed\nobjects. For example, when an already-public repository contains a\ncommit object with two authors for years, it does not make sense to\nforce the maintainer to rewrite the history, affecting all contributors\nnegatively by forcing them to update.\n\nThis branch introduces an internal fsck API to be able to turn some of\nthe errors into warnings, and to make it easier to call the fsck\nmachinery from elsewhere in general.\n\nI am proud to report that this work has been sponsored by GitHub.\n\nInterdiff vs v1 below the diffstat. Sorry for the size; the comments I\nreceived for v1 made it necessary to change the patch series rather\nextensively (I rebased the branch twenty-five times since sending off\nthe first version of the patch series, which might also serve as an apology\nfor not getting v2 out sooner).\n\nJohannes Schindelin (19):\n  fsck: Introduce fsck options\n  fsck: Introduce identifiers for fsck messages\n  fsck: Provide a function to parse fsck message IDs\n  fsck: Offer a function to demote fsck errors to warnings\n  fsck: Allow demoting errors to warnings via receive.fsck.warn = <key>\n  fsck: Report the ID of the error/warning\n  fsck: Make fsck_ident() warn-friendly\n  fsck: Make fsck_commit() warn-friendly\n  fsck: Handle multiple authors in commits specially\n  fsck: Make fsck_tag() warn-friendly\n  fsck: Add a simple test for receive.fsck.*\n  fsck: Disallow demoting grave fsck errors to warnings\n  fsck: Optionally ignore specific fsck issues completely\n  fsck: Allow upgrading fsck warnings to errors\n  fsck: Document the new receive.fsck.* options.\n  fsck: Support demoting errors to warnings\n  fsck: Introduce `git fsck --quick`\n  fsck: git receive-pack: support excluding objects from fsck'ing\n\n Documentation/config.txt        |  38 +++\n Documentation/git-fsck.txt      |   7 +-\n builtin/fsck.c                  |  66 +++--\n builtin/index-pack.c            |  13 +-\n builtin/receive-pack.c          |  35 ++-\n builtin/unpack-objects.c        |  16 +-\n fsck.c                          | 525 +++++++++++++++++++++++++++++++---------\n fsck.h                          |  27 ++-\n t/t1450-fsck.sh                 |  37 ++-\n t/t5302-pack-index.sh           |   2 +-\n t/t5504-fetch-receive-strict.sh |  46 ++++\n 11 files changed, 648 insertions(+), 164 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 4f86d3f..0daba8a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1209,14 +1209,14 @@ filter.<driver>.smudge::\n \tlinkgit:gitattributes[5] for details.\n \n fsck.*::\n-\tWith these options, fsck errors can be switched to warnings and\n-\tvice versa by setting e.g. `fsck.bad-name` to `warn` or `error`\n-\t(or `ignore` to hide those errors completely). For convenience,\n-\tfsck prefixes the error/warning with the name of the option, e.g.\n-\t\"missing-email: invalid author/committer line - missing email\"\n-\tmeans that setting `fsck.missing-email` to `ignore` will hide that\n-\tissue.  For convenience, camelCased options are accepted, too (e.g.\n-\t`fsck.missingEmail`).\n+\tThe `fsck.error`, `fsck.warn` and `fsck.ignore` settings specify\n+\tcomma-separated lists of fsck message IDs which should trigger\n+\tfsck to error out, to print the message and continue, or to ignore\n+\tsaid messages, respectively.\n++\n+For convenience, fsck prefixes the error/warning with the name of the option,\n+e.g.  \"missing-email: invalid author/committer line - missing email\" means\n+that setting `fsck.ignore = missing-email` will hide that issue.\n +\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n@@ -2144,18 +2144,29 @@ receive.fsckObjects::\n \tis used instead.\n \n receive.fsck.*::\n-\tWhen `receive.fsckObjects is set to true, errors can be switched\n-\tto warnings and vice versa by setting e.g. `receive.fsck.bad-name`\n-\tto `warn` or `error` (or `ignore` to hide those errors\n-\tcompletely). For convenience, fsck prefixes the error/warning\n-\twith the name of the option, e.g. \"missing-email: invalid\n-\tauthor/committer line - missing email\" means that setting\n-\t`receive.fsck.missing-email` to `ignore` will hide that issue.\n-\tFor convenience, camelCased options are accepted, too (e.g.\n-\t`receive.fsck.missingEmail`).\n+\tWhen `receive.fsckObjects` is set to true, errors can be switched\n+\tto warnings and vice versa by configuring the `receive.fsck.*`\n+\tsettings. These settings contain comma-separated lists of fsck\n+\tmessage IDs. For convenience, fsck prefixes the error/warning with\n+\tthe message ID, e.g. \"missing-email: invalid author/committer line\n+\t- missing email\" means that setting `receive.fsck.ignore =\n+\tmissing-email` will hide that issue.\n++\n+--\n+\terror::\n+\t\ta comma-separated list of fsck message IDs that should be\n+\t\ttrigger fsck to error out.\n+\twarn::\n+\t\ta comma-separated list of fsck message IDs that should be\n+\t\tdisplayed, but fsck should continue to error out.\n+\tignore::\n+\t\ta comma-separated list of fsck message IDs that should be\n+\t\tignored completely.\n +\n This feature is intended to support working with legacy repositories\n-which would not pass pushing when `receive.fsckObjects = true`.\n+which would not pass pushing when `receive.fsckObjects = true`, allowing\n+the host to accept repositories certain known issues but still catch\n+other issues.\n \n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex dcea9b0..c767909 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -51,8 +51,8 @@ static int fsck_config(const char *var, const char *value, void *cb)\n {\n \tif (starts_with(var, \"fsck.\")) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tstrbuf_addf(&sb, \"%s=%s\", var + 5, value ? value : \"error\");\n-\t\tfsck_strict_mode(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_addf(&sb, \"%s=%s\", var + 5, value);\n+\t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n \t\tstrbuf_release(&sb);\n \t\treturn 0;\n \t}\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 2efcb6d..f464ca0 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1568,7 +1568,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (starts_with(arg, \"--strict=\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n-\t\t\t\tfsck_strict_mode(&fsck_options, arg + 9);\n+\t\t\t\tfsck_set_severity(&fsck_options, arg + 9);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 86bcda2..40514c2 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -36,7 +36,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n-static struct strbuf fsck_strict_mode = STRBUF_INIT;\n+static struct strbuf fsck_severity = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int advertise_atomic_push = 1;\n@@ -116,20 +116,19 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n-\tif (starts_with(var, \"receive.fsck.skip-list\")) {\n+\tif (starts_with(var, \"receive.fsck.skiplist\")) {\n \t\tconst char *path = is_absolute_path(value) ?\n \t\t\tvalue : git_path(\"%s\", value);\n-\t\tif (fsck_strict_mode.len)\n-\t\t\tstrbuf_addch(&fsck_strict_mode, ',');\n-\t\tstrbuf_addf(&fsck_strict_mode, \"skip-list=%s\", path);\n+\t\tif (fsck_severity.len)\n+\t\t\tstrbuf_addch(&fsck_severity, ',');\n+\t\tstrbuf_addf(&fsck_severity, \"skiplist=%s\", path);\n \t\treturn 0;\n \t}\n \n \tif (starts_with(var, \"receive.fsck.\")) {\n-\t\tif (fsck_strict_mode.len)\n-\t\t\tstrbuf_addch(&fsck_strict_mode, ',');\n-\t\tstrbuf_addf(&fsck_strict_mode,\n-\t\t\t\"%s=%s\", var + 13, value ? value : \"error\");\n+\t\tif (fsck_severity.len)\n+\t\t\tstrbuf_addch(&fsck_severity, ',');\n+\t\tstrbuf_addf(&fsck_severity, \"%s=%s\", var + 13, value);\n \t\treturn 0;\n \t}\n \n@@ -1489,9 +1488,9 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects) {\n-\t\t\tif (fsck_strict_mode.len)\n+\t\t\tif (fsck_severity.len)\n \t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n-\t\t\t\t\tfsck_strict_mode.buf);\n+\t\t\t\t\tfsck_severity.buf);\n \t\t\telse\n \t\t\t\targv_array_push(&child.args, \"--strict\");\n \t\t}\n@@ -1512,9 +1511,9 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects) {\n-\t\t\tif (fsck_strict_mode.len)\n+\t\t\tif (fsck_severity.len)\n \t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n-\t\t\t\t\tfsck_strict_mode.buf);\n+\t\t\t\t\tfsck_severity.buf);\n \t\t\telse\n \t\t\t\targv_array_push(&child.args, \"--strict\");\n \t\t}\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 179a960..82f2d62 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -532,7 +532,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t}\n \t\t\tif (starts_with(arg, \"--strict=\")) {\n \t\t\t\tstrict = 1;\n-\t\t\t\tfsck_strict_mode(&fsck_options, arg + 9);\n+\t\t\t\tfsck_set_severity(&fsck_options, arg + 9);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\ndiff --git a/fsck.c b/fsck.c\nindex 4f8a754..dbf9fa1 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -10,121 +10,126 @@\n #include \"utf8.h\"\n #include \"sha1-array.h\"\n \n+#define FSCK_FATAL -1\n+#define FSCK_INFO -2\n+\n #define FOREACH_MSG_ID(FUNC) \\\n \t/* fatal errors */ \\\n-\tFUNC(NUL_IN_HEADER) \\\n-\tFUNC(UNTERMINATED_HEADER) \\\n+\tFUNC(NUL_IN_HEADER, FATAL) \\\n+\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n \t/* errors */ \\\n-\tFUNC(BAD_DATE) \\\n-\tFUNC(BAD_EMAIL) \\\n-\tFUNC(BAD_NAME) \\\n-\tFUNC(BAD_PARENT_SHA1) \\\n-\tFUNC(BAD_TIMEZONE) \\\n-\tFUNC(BAD_TREE_SHA1) \\\n-\tFUNC(DATE_OVERFLOW) \\\n-\tFUNC(DUPLICATE_ENTRIES) \\\n-\tFUNC(INVALID_OBJECT_SHA1) \\\n-\tFUNC(INVALID_TAG_OBJECT) \\\n-\tFUNC(INVALID_TREE) \\\n-\tFUNC(INVALID_TYPE) \\\n-\tFUNC(MISSING_AUTHOR) \\\n-\tFUNC(MISSING_COMMITTER) \\\n-\tFUNC(MISSING_EMAIL) \\\n-\tFUNC(MISSING_GRAFT) \\\n-\tFUNC(MISSING_NAME_BEFORE_EMAIL) \\\n-\tFUNC(MISSING_OBJECT) \\\n-\tFUNC(MISSING_PARENT) \\\n-\tFUNC(MISSING_SPACE_BEFORE_DATE) \\\n-\tFUNC(MISSING_SPACE_BEFORE_EMAIL) \\\n-\tFUNC(MISSING_TAG) \\\n-\tFUNC(MISSING_TAG_ENTRY) \\\n-\tFUNC(MISSING_TAG_OBJECT) \\\n-\tFUNC(MISSING_TREE) \\\n-\tFUNC(MISSING_TYPE) \\\n-\tFUNC(MISSING_TYPE_ENTRY) \\\n-\tFUNC(MULTIPLE_AUTHORS) \\\n-\tFUNC(NOT_SORTED) \\\n-\tFUNC(TAG_OBJECT_NOT_TAG) \\\n-\tFUNC(UNKNOWN_TYPE) \\\n-\tFUNC(ZERO_PADDED_DATE) \\\n+\tFUNC(BAD_DATE, ERROR) \\\n+\tFUNC(BAD_EMAIL, ERROR) \\\n+\tFUNC(BAD_NAME, ERROR) \\\n+\tFUNC(BAD_PARENT_SHA1, ERROR) \\\n+\tFUNC(BAD_TIMEZONE, ERROR) \\\n+\tFUNC(BAD_TREE_SHA1, ERROR) \\\n+\tFUNC(DATE_OVERFLOW, ERROR) \\\n+\tFUNC(DUPLICATE_ENTRIES, ERROR) \\\n+\tFUNC(INVALID_OBJECT_SHA1, ERROR) \\\n+\tFUNC(INVALID_TAG_OBJECT, ERROR) \\\n+\tFUNC(INVALID_TREE, ERROR) \\\n+\tFUNC(INVALID_TYPE, ERROR) \\\n+\tFUNC(MISSING_AUTHOR, ERROR) \\\n+\tFUNC(MISSING_COMMITTER, ERROR) \\\n+\tFUNC(MISSING_EMAIL, ERROR) \\\n+\tFUNC(MISSING_GRAFT, ERROR) \\\n+\tFUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_OBJECT, ERROR) \\\n+\tFUNC(MISSING_PARENT, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_TAG, ERROR) \\\n+\tFUNC(MISSING_TAG_ENTRY, ERROR) \\\n+\tFUNC(MISSING_TAG_OBJECT, ERROR) \\\n+\tFUNC(MISSING_TREE, ERROR) \\\n+\tFUNC(MISSING_TYPE, ERROR) \\\n+\tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n+\tFUNC(NOT_SORTED, ERROR) \\\n+\tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n+\tFUNC(UNKNOWN_TYPE, ERROR) \\\n+\tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n-\tFUNC(BAD_FILEMODE) \\\n-\tFUNC(EMPTY_NAME) \\\n-\tFUNC(FULL_PATHNAME) \\\n-\tFUNC(HAS_DOT) \\\n-\tFUNC(HAS_DOTDOT) \\\n-\tFUNC(HAS_DOTGIT) \\\n-\tFUNC(NULL_SHA1) \\\n-\tFUNC(ZERO_PADDED_FILEMODE) \\\n+\tFUNC(BAD_FILEMODE, WARN) \\\n+\tFUNC(EMPTY_NAME, WARN) \\\n+\tFUNC(FULL_PATHNAME, WARN) \\\n+\tFUNC(HAS_DOT, WARN) \\\n+\tFUNC(HAS_DOTDOT, WARN) \\\n+\tFUNC(HAS_DOTGIT, WARN) \\\n+\tFUNC(NULL_SHA1, WARN) \\\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n \t/* infos (reported as warnings, but ignored by default) */ \\\n-\tFUNC(INVALID_TAG_NAME) \\\n-\tFUNC(MISSING_TAGGER_ENTRY) \\\n-\t/* special value */ \\\n-\tFUNC(SKIP_LIST)\n-\n-#define FIRST_NON_FATAL_ERROR FSCK_MSG_BAD_DATE\n-#define FIRST_WARNING FSCK_MSG_BAD_FILEMODE\n-#define FIRST_INFO FSCK_MSG_INVALID_TAG_NAME\n+\tFUNC(INVALID_TAG_NAME, INFO) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n-#define MSG_ID(x) FSCK_MSG_##x,\n+#define MSG_ID(id, severity) FSCK_MSG_##id,\n enum fsck_msg_id {\n \tFOREACH_MSG_ID(MSG_ID)\n \tFSCK_MSG_MAX\n };\n+#undef MSG_ID\n \n #define STR(x) #x\n-#define MSG_ID_STR(x) STR(x),\n-static const char *msg_id_str[FSCK_MSG_MAX + 1] = {\n-\tFOREACH_MSG_ID(MSG_ID_STR)\n-\tNULL\n+#define MSG_ID(id, severity) { STR(id), FSCK_##severity },\n+static struct {\n+\tconst char *id_string;\n+\tint severity;\n+} msg_id_info[FSCK_MSG_MAX + 1] = {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\t{ NULL, -1 }\n };\n+#undef MSG_ID\n \n static int parse_msg_id(const char *text, int len)\n {\n \tint i, j;\n \n \tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n-\t\tconst char *key = msg_id_str[i];\n-\t\t/* msg_id_str is upper-case, with underscores */\n+\t\tconst char *key = msg_id_info[i].id_string;\n+\t\t/* id_string is upper-case, with underscores */\n \t\tfor (j = 0; j < len; j++) {\n \t\t\tchar c = *(key++);\n-\t\t\tif (c == '_') {\n-\t\t\t\tif (isalpha(text[j]))\n-\t\t\t\t\tc = *(key++);\n-\t\t\t\telse if (text[j] != '_')\n-\t\t\t\t\tc = '-';\n-\t\t\t}\n-\t\t\tif (toupper(text[j]) != c)\n+\t\t\tif (c == '_')\n+\t\t\t\tc = '-';\n+\t\t\tif (text[j] != tolower(c))\n \t\t\t\tbreak;\n \t\t}\n \t\tif (j == len && !*key)\n \t\t\treturn i;\n \t}\n \n-\tdie(\"Unhandled type: %.*s\", len, text);\n+\tdie(\"Unhandled message id: %.*s\", len, text);\n }\n \n-int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options)\n+static int fsck_msg_severity(enum fsck_msg_id msg_id,\n+\tstruct fsck_options *options)\n {\n-\tif (options->strict_mode && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n-\t\treturn options->strict_mode[msg_id];\n-\tif (options->strict)\n-\t\treturn msg_id < FIRST_INFO ? FSCK_ERROR : FSCK_WARN;\n-\treturn msg_id < FIRST_WARNING ? FSCK_ERROR : FSCK_WARN;\n+\tint severity;\n+\n+\tif (options->msg_severity && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n+\t\tseverity = options->msg_severity[msg_id];\n+\telse {\n+\t\tseverity = msg_id_info[msg_id].severity;\n+\t\tif (options->strict && severity == FSCK_WARN)\n+\t\t\tseverity = FSCK_ERROR;\n+\t}\n+\n+\treturn severity;\n }\n \n-static void init_skip_list(struct fsck_options *options, const char *path)\n+static void init_skiplist(struct fsck_options *options, const char *path)\n {\n-\tstatic struct sha1_array skip_list = SHA1_ARRAY_INIT;\n+\tstatic struct sha1_array skiplist = SHA1_ARRAY_INIT;\n \tint sorted, fd;\n \tchar buffer[41];\n \tunsigned char sha1[20];\n \n-\tif (options->skip_list)\n-\t\tsorted = options->skip_list->sorted;\n+\tif (options->skiplist)\n+\t\tsorted = options->skiplist->sorted;\n \telse {\n \t\tsorted = 1;\n-\t\toptions->skip_list = &skip_list;\n+\t\toptions->skiplist = &skiplist;\n \t}\n \n \tfd = open(path, O_RDONLY);\n@@ -138,16 +143,16 @@ static void init_skip_list(struct fsck_options *options, const char *path)\n \t\t\tbreak;\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n \t\t\tdie(\"Invalid SHA-1: %s\", buffer);\n-\t\tsha1_array_append(&skip_list, sha1);\n-\t\tif (sorted && skip_list.nr > 1 &&\n-\t\t\t\thashcmp(skip_list.sha1[skip_list.nr - 2],\n+\t\tsha1_array_append(&skiplist, sha1);\n+\t\tif (sorted && skiplist.nr > 1 &&\n+\t\t\t\thashcmp(skiplist.sha1[skiplist.nr - 2],\n \t\t\t\t\tsha1) > 0)\n \t\t\tsorted = 0;\n \t}\n \tclose(fd);\n \n \tif (sorted)\n-\t\tskip_list.sorted = 1;\n+\t\tskiplist.sorted = 1;\n }\n \n static inline int substrcmp(const char *string, int len, const char *match)\n@@ -158,16 +163,16 @@ static inline int substrcmp(const char *string, int len, const char *match)\n \treturn memcmp(string, match, len);\n }\n \n-void fsck_strict_mode(struct fsck_options *options, const char *mode)\n+void fsck_set_severity(struct fsck_options *options, const char *mode)\n {\n-\tint type = FSCK_ERROR;\n+\tint severity = FSCK_ERROR;\n \n-\tif (!options->strict_mode) {\n+\tif (!options->msg_severity) {\n \t\tint i;\n-\t\tint *strict_mode = malloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tint *msg_severity = malloc(sizeof(int) * FSCK_MSG_MAX);\n \t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n-\t\t\tstrict_mode[i] = fsck_msg_type(i, options);\n-\t\toptions->strict_mode = strict_mode;\n+\t\t\tmsg_severity[i] = fsck_msg_severity(i, options);\n+\t\toptions->msg_severity = msg_severity;\n \t}\n \n \twhile (*mode) {\n@@ -182,35 +187,36 @@ void fsck_strict_mode(struct fsck_options *options, const char *mode)\n \t\t\tif (mode[equal] == '=')\n \t\t\t\tbreak;\n \n-\t\tmsg_id = parse_msg_id(mode, equal);\n-\t\tif (msg_id == FSCK_MSG_SKIP_LIST) {\n-\t\t\tchar *path = xstrndup(mode + equal + 1, len - equal - 1);\n-\n-\t\t\tif (equal == len)\n-\t\t\t\tdie(\"skip-list requires a path\");\n-\t\t\tinit_skip_list(options, path);\n-\t\t\tfree(path);\n-\t\t\tmode += len;\n-\t\t\tcontinue;\n-\t\t}\n-\n \t\tif (equal < len) {\n-\t\t\tconst char *type_str = mode + equal + 1;\n-\t\t\tint type_len = len - equal - 1;\n-\t\t\tif (!substrcmp(type_str, type_len, \"error\"))\n-\t\t\t\ttype = FSCK_ERROR;\n-\t\t\telse if (!substrcmp(type_str, type_len, \"warn\"))\n-\t\t\t\ttype = FSCK_WARN;\n-\t\t\telse if (!substrcmp(type_str, type_len, \"ignore\"))\n-\t\t\t\ttype = FSCK_IGNORE;\n+\t\t\tif (!substrcmp(mode, equal, \"error\"))\n+\t\t\t\tseverity = FSCK_ERROR;\n+\t\t\telse if (!substrcmp(mode, equal, \"warn\"))\n+\t\t\t\tseverity = FSCK_WARN;\n+\t\t\telse if (!substrcmp(mode, equal, \"ignore\"))\n+\t\t\t\tseverity = FSCK_IGNORE;\n+\t\t\telse if (!substrcmp(mode, equal, \"skiplist\")) {\n+\t\t\t\tchar *path = xstrndup(mode + equal + 1,\n+\t\t\t\t\tlen - equal - 1);\n+\n+\t\t\t\tif (equal == len)\n+\t\t\t\t\tdie(\"skiplist requires a path\");\n+\t\t\t\tinit_skiplist(options, path);\n+\t\t\t\tfree(path);\n+\t\t\t\tmode += len;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\telse\n-\t\t\t\tdie(\"Unknown fsck message type: '%.*s'\",\n-\t\t\t\t\tlen - equal - 1, type_str);\n+\t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n+\t\t\t\t\tequal, mode);\n+\t\t\tmode += equal + 1;\n+\t\t\tlen -= equal + 1;\n \t\t}\n \n-\t\tif (type != FSCK_ERROR && msg_id < FIRST_NON_FATAL_ERROR)\n+\t\tmsg_id = parse_msg_id(mode, len);\n+\t\tif (severity != FSCK_ERROR &&\n+\t\t\t\tmsg_id_info[msg_id].severity == FSCK_FATAL)\n \t\t\tdie(\"Cannot demote %.*s\", len, mode);\n-\t\toptions->strict_mode[msg_id] = type;\n+\t\toptions->msg_severity[msg_id] = severity;\n \t\tmode += len;\n \t}\n }\n@@ -238,16 +244,21 @@ static int report(struct fsck_options *options, struct object *object,\n {\n \tva_list ap;\n \tstruct strbuf sb = STRBUF_INIT;\n-\tint msg_type = fsck_msg_type(id, options), result;\n+\tint msg_severity = fsck_msg_severity(id, options), result;\n \n-\tif (msg_type == FSCK_IGNORE)\n+\tif (msg_severity == FSCK_IGNORE)\n \t\treturn 0;\n \n-\tappend_msg_id(&sb, msg_id_str[id]);\n+\tif (msg_severity == FSCK_FATAL)\n+\t\tmsg_severity = FSCK_ERROR;\n+\telse if (msg_severity == FSCK_INFO)\n+\t\tmsg_severity = FSCK_WARN;\n+\n+\tappend_msg_id(&sb, msg_id_info[id].id_string);\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(object, msg_severity, sb.buf);\n \tstrbuf_release(&sb);\n \tva_end(ap);\n \n@@ -606,8 +617,9 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n \t\tif (err)\n \t\t\treturn err;\n-\t\t/* require_end_of_header() ensured that there is a newline */\n-\t\tbuffer = strchr(buffer, '\\n') + 1;\n+\t\terr = fsck_ident(&buffer, &commit->object, options);\n+\t\tif (err)\n+\t\t\treturn err;\n \t}\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n@@ -737,8 +749,8 @@ static int fsck_tag(struct tag *tag, const char *data,\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n-\tif (options->skip_list &&\n-\t\t\tsha1_array_lookup(options->skip_list, obj->sha1) >= 0)\n+\tif (options->skiplist &&\n+\t\t\tsha1_array_lookup(options->skiplist, obj->sha1) >= 0)\n \t\treturn 0;\n \n \tif (!obj)\n@@ -759,9 +771,9 @@ 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 type, const char *message)\n+int fsck_error_function(struct object *obj, int severity, const char *message)\n {\n-\tif (type == FSCK_WARN) {\n+\tif (severity == FSCK_WARN) {\n \t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \t\treturn 0;\n \t}\ndiff --git a/fsck.h b/fsck.h\nindex 74d11cd..5397fbd 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -7,7 +7,7 @@\n \n struct fsck_options;\n \n-void fsck_strict_mode(struct fsck_options *options, const char *mode);\n+void fsck_set_severity(struct fsck_options *options, const char *mode);\n \n /*\n  * callback function for fsck_walk\n@@ -27,10 +27,9 @@ int fsck_error_function(struct object *obj, int type, const char *message);\n struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n-\tint strict:1;\n-\tint *strict_mode;\n-\t/* TODO: consider reading into a hashmap */\n-\tstruct sha1_array *skip_list;\n+\tunsigned strict:1;\n+\tint *msg_severity;\n+\tstruct sha1_array *skiplist;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 3054113..1c624a3 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: invalid-tag-name: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missing-tagger-entry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n@@ -295,7 +295,7 @@ test_expect_success 'force fsck to ignore double author' '\n \tgit update-ref refs/heads/bogus \"$new\" &&\n \ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n \ttest_must_fail git fsck &&\n-\tgit -c fsck.multiple-authors=ignore fsck\n+\tgit -c fsck.ignore=multiple-authors fsck\n '\n \n _bz='\\0'\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex cf6cd5d..21fa9c8 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,42 +123,42 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n-test_expect_success 'push with receive.fsck.skip-list' '\n+test_expect_success 'push with receive.fsck.skiplist' '\n \tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n \ttest_must_fail git push --porcelain dst bogus &&\n-\tgit --git-dir=dst/.git config receive.fsck.skip-list SKIP &&\n+\tgit --git-dir=dst/.git config receive.fsck.skiplist SKIP &&\n \techo $commit > dst/.git/SKIP &&\n \tgit push --porcelain dst bogus\n '\n \n-test_expect_success 'push with receive.fsck.missing-mail = warn' '\n+test_expect_success 'push with receive.fsck.warn = missing-email' '\n \tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n \ttest_must_fail git push --porcelain dst bogus &&\n-\tgit --git-dir=dst/.git config receive.fsck.missing-email warn &&\n+\tgit --git-dir=dst/.git config receive.fsck.warn missing-email &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n \tgrep \"missing-email\" act &&\n \tgit --git-dir=dst/.git branch -D bogus &&\n-\tgit  --git-dir=dst/.git config receive.fsck.missing-email ignore &&\n-\tgit  --git-dir=dst/.git config receive.fsck.bad-date warn &&\n+\tgit  --git-dir=dst/.git config receive.fsck.ignore missing-email &&\n+\tgit  --git-dir=dst/.git config receive.fsck.warn bad-date &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n \ttest_must_fail grep \"missing-email\" act\n '\n \n-test_expect_success 'receive.fsck.unterminated-header = warn triggers error' '\n+test_expect_success 'receive.fsck.warn = unterminated-header triggers error' '\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n-\tgit --git-dir=dst/.git config receive.fsck.unterminated-header warn &&\n+\tgit --git-dir=dst/.git config receive.fsck.warn unterminated-header &&\n \ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n-\tgrep \"Cannot demote unterminated-header=warn\" act\n+\tgrep \"Cannot demote unterminated-header\" act\n '\n \n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254896","messageId":"690b9da7bfa1089fa14e5763822004a5e7ef815c.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 01/18] fsck: Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:50:04Z","receivedAt":"2015-01-19T15:50:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Just like the diff machinery, we are about to introduce more settings,\ntherefore it makes sense to carry them around as a (pointer to a) struct\ncontaining all of them.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c           |  20 +++++--\n builtin/index-pack.c     |   9 +--\n builtin/unpack-objects.c |  11 ++--\n fsck.c                   | 150 +++++++++++++++++++++++------------------------\n fsck.h                   |  17 +++++-\n 5 files changed, 114 insertions(+), 93 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex a27515a..2241e29 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -25,6 +25,8 @@ static int include_reflogs = 1;\n static int check_full = 1;\n static int check_strict;\n static int keep_cache_objects;\n+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n static unsigned char head_sha1[20];\n static const char *head_points_at;\n static int errors_found;\n@@ -76,7 +78,7 @@ static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n \n static struct object_array pending;\n \n-static int mark_object(struct object *obj, int type, void *data)\n+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct object *parent = data;\n \n@@ -119,7 +121,7 @@ static int mark_object(struct object *obj, int type, void *data)\n \n static void mark_object_reachable(struct object *obj)\n {\n-\tmark_object(obj, OBJ_ANY, NULL);\n+\tmark_object(obj, OBJ_ANY, NULL, NULL);\n }\n \n static int traverse_one_object(struct object *obj)\n@@ -132,7 +134,7 @@ static int traverse_one_object(struct object *obj)\n \t\tif (parse_tree(tree) < 0)\n \t\t\treturn 1; /* error already displayed */\n \t}\n-\tresult = fsck_walk(obj, mark_object, obj);\n+\tresult = fsck_walk(obj, obj, &fsck_walk_options);\n \tif (tree)\n \t\tfree_tree_buffer(tree);\n \treturn result;\n@@ -158,7 +160,7 @@ static int traverse_reachable(void)\n \treturn !!result;\n }\n \n-static int mark_used(struct object *obj, int type, void *data)\n+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn 1;\n@@ -296,9 +298,9 @@ static int fsck_obj(struct object *obj)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n \t\t\ttypename(obj->type), sha1_to_hex(obj->sha1));\n \n-\tif (fsck_walk(obj, mark_used, NULL))\n+\tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n-\tif (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))\n+\tif (fsck_object(obj, NULL, 0, &fsck_obj_options))\n \t\treturn -1;\n \n \tif (obj->type == OBJ_TREE) {\n@@ -630,6 +632,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);\n \n+\tfsck_walk_options.walk = mark_object;\n+\tfsck_obj_options.walk = mark_used;\n+\tfsck_obj_options.error_func = fsck_error_func;\n+\tif (check_strict)\n+\t\tfsck_obj_options.strict = 1;\n+\n \tif (show_progress == -1)\n \t\tshow_progress = isatty(2);\n \tif (verbose)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 4632117..925f7b5 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -74,6 +74,7 @@ static int nr_threads;\n static int from_stdin;\n static int strict;\n static int do_fsck_object;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n static int verbose;\n static int show_stat;\n static int check_self_contained_and_connected;\n@@ -191,7 +192,7 @@ static void cleanup_thread(void)\n #endif\n \n \n-static int mark_link(struct object *obj, int type, void *data)\n+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -782,10 +783,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (!obj)\n \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(obj, buf, size, 1,\n-\t\t\t\t    fsck_error_function))\n+\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"Error in object\"));\n-\t\t\tif (fsck_walk(obj, mark_link, NULL))\n+\t\t\tfsck_options.walk = mark_link;\n+\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n \n \t\t\tif (obj->type == OBJ_TREE) {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex ac66672..6d17040 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -20,6 +20,7 @@ static unsigned char buffer[4096];\n static unsigned int offset, len;\n static off_t consumed_bytes;\n static git_SHA_CTX ctx;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n /*\n  * When running under --strict mode, objects whose reachability are\n@@ -178,7 +179,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)\n  * that have reachability requirements and calls this function.\n  * Verify its reachability and validity recursively and write it out.\n  */\n-static int check_object(struct object *obj, int type, void *data)\n+static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct obj_buffer *obj_buf;\n \n@@ -203,10 +204,10 @@ static int check_object(struct object *obj, int type, void *data)\n \tobj_buf = lookup_object_buffer(obj);\n \tif (!obj_buf)\n \t\tdie(\"Whoops! Cannot find object '%s'\", sha1_to_hex(obj->sha1));\n-\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1,\n-\t\t\tfsck_error_function))\n+\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"Error in object\");\n-\tif (fsck_walk(obj, check_object, NULL))\n+\tfsck_options.walk = check_object;\n+\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\tdie(\"Error on reachable objects of %s\", sha1_to_hex(obj->sha1));\n \twrite_cached_object(obj, obj_buf);\n \treturn 0;\n@@ -217,7 +218,7 @@ static void write_rest(void)\n \tunsigned i;\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tif (obj_list[i].obj)\n-\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL);\n+\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);\n \t}\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex 10bcb65..d83b811 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,7 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n-static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\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@@ -25,9 +25,9 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n \t\tif (S_ISDIR(entry.mode))\n-\t\t\tresult = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);\n+\t\t\tresult = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);\n \t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n-\t\t\tresult = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);\n+\t\t\tresult = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n \t\t\t\t\tsha1_to_hex(tree->object.sha1), entry.path, entry.mode);\n@@ -40,7 +40,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \treturn res;\n }\n \n-static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)\n+static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n \tstruct commit_list *parents;\n \tint res;\n@@ -49,14 +49,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \tif (parse_commit(commit))\n \t\treturn -1;\n \n-\tresult = walk((struct object *)commit->tree, OBJ_TREE, data);\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 \twhile (parents) {\n-\t\tresult = walk((struct object *)parents->item, OBJ_COMMIT, data);\n+\t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n@@ -66,14 +66,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \treturn res;\n }\n \n-static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)\n+static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n \tif (parse_tag(tag))\n \t\treturn -1;\n-\treturn walk(tag->tagged, OBJ_ANY, data);\n+\treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -81,11 +81,11 @@ int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n \tcase OBJ_BLOB:\n \t\treturn 0;\n \tcase OBJ_TREE:\n-\t\treturn fsck_walk_tree((struct tree *)obj, walk, data);\n+\t\treturn fsck_walk_tree((struct tree *)obj, data, options);\n \tcase OBJ_COMMIT:\n-\t\treturn fsck_walk_commit((struct commit *)obj, walk, data);\n+\t\treturn fsck_walk_commit((struct commit *)obj, data, options);\n \tcase OBJ_TAG:\n-\t\treturn fsck_walk_tag((struct tag *)obj, walk, data);\n+\t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n \tdefault:\n \t\terror(\"Unknown object type for %s\", sha1_to_hex(obj->sha1));\n \t\treturn -1;\n@@ -138,7 +138,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con\n \treturn c1 < c2 ? 0 : TREE_UNORDERED;\n }\n \n-static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n+static int fsck_tree(struct tree *item, struct fsck_options *options)\n {\n \tint retval;\n \tint has_null_sha1 = 0;\n@@ -194,7 +194,7 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \t\t * bits..\n \t\t */\n \t\tcase S_IFREG | 0664:\n-\t\t\tif (!strict)\n+\t\t\tif (!options->strict)\n \t\t\t\tbreak;\n \t\tdefault:\n \t\t\thas_bad_modes = 1;\n@@ -219,30 +219,30 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n \treturn retval;\n }\n \n static int require_end_of_header(const void *data, unsigned long size,\n-\tstruct object *obj, fsck_error error_func)\n+\tstruct object *obj, struct fsck_options *options)\n {\n \tconst char *buffer = (const char *)data;\n \tunsigned long i;\n@@ -250,7 +250,7 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn error_func(obj, FSCK_ERROR,\n+\t\t\treturn options->error_func(obj, FSCK_ERROR,\n \t\t\t\t\"unterminated header: NUL at offset %d\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n@@ -258,36 +258,36 @@ static int require_end_of_header(const void *data, unsigned long size,\n \t\t}\n \t}\n \n-\treturn error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n }\n \n-static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)\n+static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,30 +295,30 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n \n static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n \tunsigned parent_count, parent_line_count = 0;\n \tint err;\n \n-\tif (require_end_of_header(buffer, size, &commit->object, error_func))\n+\tif (require_end_of_header(buffer, size, &commit->object, options))\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,39 +328,39 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n \n static int fsck_commit(struct commit *commit, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tconst char *buffer = data ?  data : get_commit_buffer(commit, &size);\n-\tint ret = fsck_commit_buffer(commit, buffer, size, error_func);\n+\tint ret = fsck_commit_buffer(commit, buffer, size, options);\n \tif (!data)\n \t\tunuse_commit_buffer(commit, buffer);\n \treturn ret;\n }\n \n static int fsck_tag_buffer(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char sha1[20];\n \tint ret = 0;\n@@ -376,65 +376,65 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n \t\t}\n \t}\n \n-\tif (require_end_of_header(buffer, size, &tag->object, error_func))\n+\tif (require_end_of_header(buffer, size, &tag->object, options))\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n \telse\n-\t\tret = fsck_ident(&buffer, &tag->object, error_func);\n+\t\tret = fsck_ident(&buffer, &tag->object, options);\n \n done:\n \tstrbuf_release(&sb);\n@@ -443,34 +443,34 @@ done:\n }\n \n static int fsck_tag(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n \n-\treturn fsck_tag_buffer(tag, data, size, error_func);\n+\treturn fsck_tag_buffer(tag, data, size, options);\n }\n \n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func)\n+\tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n \tif (obj->type == OBJ_TREE)\n-\t\treturn fsck_tree((struct tree *) obj, strict, error_func);\n+\t\treturn fsck_tree((struct tree *) obj, options);\n \tif (obj->type == OBJ_COMMIT)\n \t\treturn fsck_commit((struct commit *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \tif (obj->type == OBJ_TAG)\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \n-\treturn error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex d1e6387..07d0ab2 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -4,6 +4,8 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n \n+struct fsck_options;\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -12,7 +14,7 @@\n  *     <0\terror signaled and abort\n  *     >0\terror signaled and do not abort\n  */\n-typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);\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 *err, ...);\n@@ -20,6 +22,15 @@ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);\n __attribute__((format (printf, 3, 4)))\n int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n \n+struct fsck_options {\n+\tfsck_walk_func walk;\n+\tfsck_error error_func;\n+\tunsigned strict:1;\n+};\n+\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+\n /* descend in all linked child objects\n  * the return value is:\n  *    -1\terror in processing the object\n@@ -27,9 +38,9 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n  *    >0\treturn value of the first signaled error >0 (in the case of no other errors)\n  *    0\t\teverything OK\n  */\n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n /* If NULL is passed for data, we assume the object is local and read it. */\n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func);\n+\tstruct fsck_options *options);\n \n #endif\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254895","messageId":"83a7e750bdf56a62408875ec718a095753b4487c.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 02/18] fsck: Introduce identifiers for fsck messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:50:12Z","receivedAt":"2015-01-19T15:50:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Instead of specifying whether a message by the fsck machinery constitutes\nan error or a warning, let's specify an identifier relating to the\nconcrete problem that was encountered. This is necessary for upcoming\nsupport to be able to demote certain errors to warnings.\n\nIn the process, simplify the requirements on the calling code: instead of\nhaving to handle full-blown varargs in every callback, we now send a\nstring buffer ready to be used by the callback.\n\nWe could use a simple enum for the message IDs here, but we want to\nguarantee that the enum values are associated with the appropriate\nseverity levels. Besides, we want to introduce a parser in the next commit\nthat maps the string representation to the enum value, hence we use the\nslightly ugly preprocessor construct that is extensible for use with said\nparser.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c |  24 ++-----\n fsck.c         | 201 +++++++++++++++++++++++++++++++++++++++++----------------\n fsck.h         |   5 +-\n 3 files changed, 153 insertions(+), 77 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2241e29..99d4538 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -47,32 +47,22 @@ static int show_dangling = 1;\n #endif\n \n static void objreport(struct object *obj, const char *severity,\n-                      const char *err, va_list params)\n+                      const char *err)\n {\n-\tfprintf(stderr, \"%s in %s %s: \",\n-\t        severity, typename(obj->type), sha1_to_hex(obj->sha1));\n-\tvfprintf(stderr, err, params);\n-\tfputs(\"\\n\", stderr);\n+\tfprintf(stderr, \"%s in %s %s: %s\\n\",\n+\t        severity, typename(obj->type), sha1_to_hex(obj->sha1), err);\n }\n \n-__attribute__((format (printf, 2, 3)))\n-static int objerror(struct object *obj, const char *err, ...)\n+static int objerror(struct object *obj, const char *err)\n {\n-\tva_list params;\n-\tva_start(params, err);\n \terrors_found |= ERROR_OBJECT;\n-\tobjreport(obj, \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, \"error\", err);\n \treturn -1;\n }\n \n-__attribute__((format (printf, 3, 4)))\n-static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n+static int fsck_error_func(struct object *obj, int type, const char *message)\n {\n-\tva_list params;\n-\tva_start(params, err);\n-\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n \treturn (type == FSCK_WARN) ? 0 : 1;\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex d83b811..30f7a48 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,98 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FOREACH_MSG_ID(FUNC) \\\n+\t/* errors */ \\\n+\tFUNC(BAD_DATE, ERROR) \\\n+\tFUNC(BAD_EMAIL, ERROR) \\\n+\tFUNC(BAD_NAME, ERROR) \\\n+\tFUNC(BAD_PARENT_SHA1, ERROR) \\\n+\tFUNC(BAD_TIMEZONE, ERROR) \\\n+\tFUNC(BAD_TREE_SHA1, ERROR) \\\n+\tFUNC(DATE_OVERFLOW, ERROR) \\\n+\tFUNC(DUPLICATE_ENTRIES, ERROR) \\\n+\tFUNC(INVALID_OBJECT_SHA1, ERROR) \\\n+\tFUNC(INVALID_TAG_OBJECT, ERROR) \\\n+\tFUNC(INVALID_TREE, ERROR) \\\n+\tFUNC(INVALID_TYPE, ERROR) \\\n+\tFUNC(MISSING_AUTHOR, ERROR) \\\n+\tFUNC(MISSING_COMMITTER, ERROR) \\\n+\tFUNC(MISSING_EMAIL, ERROR) \\\n+\tFUNC(MISSING_GRAFT, ERROR) \\\n+\tFUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_OBJECT, ERROR) \\\n+\tFUNC(MISSING_PARENT, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_TAG, ERROR) \\\n+\tFUNC(MISSING_TAG_ENTRY, ERROR) \\\n+\tFUNC(MISSING_TAG_OBJECT, ERROR) \\\n+\tFUNC(MISSING_TREE, ERROR) \\\n+\tFUNC(MISSING_TYPE, ERROR) \\\n+\tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(NOT_SORTED, ERROR) \\\n+\tFUNC(NUL_IN_HEADER, ERROR) \\\n+\tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n+\tFUNC(UNKNOWN_TYPE, ERROR) \\\n+\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n+\tFUNC(ZERO_PADDED_DATE, ERROR) \\\n+\t/* warnings */ \\\n+\tFUNC(BAD_FILEMODE, WARN) \\\n+\tFUNC(EMPTY_NAME, WARN) \\\n+\tFUNC(FULL_PATHNAME, WARN) \\\n+\tFUNC(HAS_DOT, WARN) \\\n+\tFUNC(HAS_DOTDOT, WARN) \\\n+\tFUNC(HAS_DOTGIT, WARN) \\\n+\tFUNC(INVALID_TAG_NAME, WARN) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n+\tFUNC(NULL_SHA1, WARN) \\\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\n+#define MSG_ID(id, severity) FSCK_MSG_##id,\n+enum fsck_msg_id {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\tFSCK_MSG_MAX\n+};\n+#undef MSG_ID\n+\n+#define MSG_ID(id, severity) { FSCK_##severity },\n+static struct {\n+\tint severity;\n+} msg_id_info[FSCK_MSG_MAX + 1] = {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\t{ -1 }\n+};\n+#undef MSG_ID\n+\n+static int fsck_msg_severity(enum fsck_msg_id msg_id,\n+\tstruct fsck_options *options)\n+{\n+\tint severity;\n+\n+\tseverity = msg_id_info[msg_id].severity;\n+\tif (options->strict && severity == FSCK_WARN)\n+\t\tseverity = FSCK_ERROR;\n+\n+\treturn severity;\n+}\n+\n+__attribute__((format (printf, 4, 5)))\n+static int report(struct fsck_options *options, struct object *object,\n+\tenum fsck_msg_id id, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint msg_severity = fsck_msg_severity(id, options), result;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tresult = options->error_func(object, msg_severity, sb.buf);\n+\tstrbuf_release(&sb);\n+\tva_end(ap);\n+\n+\treturn result;\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n@@ -219,25 +311,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NULL_SHA1, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOT, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NOT_SORTED, \"not properly sorted\");\n \treturn retval;\n }\n \n@@ -250,15 +342,17 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn options->error_func(obj, FSCK_ERROR,\n-\t\t\t\t\"unterminated header: NUL at offset %d\", i);\n+\t\t\treturn report(options, obj,\n+\t\t\t\tFSCK_MSG_NUL_IN_HEADER,\n+\t\t\t\t\"unterminated header: NUL at offset %ld\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n \t\t\t\treturn 0;\n \t\t}\n \t}\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn report(options, obj,\n+\t\tFSCK_MSG_UNTERMINATED_HEADER, \"unterminated header\");\n }\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n@@ -266,28 +360,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,7 +389,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n@@ -312,13 +406,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,23 +422,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn report(options, &commit->object, FSCK_MSG_INVALID_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n@@ -376,11 +470,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_MISSING_TAG_OBJECT,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_TAG_OBJECT_NOT_TAG,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n@@ -391,48 +487,49 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \n@@ -448,7 +545,7 @@ static int fsck_tag(struct tag *tag, const char *data,\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn report(options, &tag->object, FSCK_MSG_INVALID_TAG_OBJECT, \"could not load tagged object\");\n \n \treturn fsck_tag_buffer(tag, data, size, options);\n }\n@@ -457,7 +554,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n@@ -470,22 +567,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n \t\t\tsize, options);\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn report(options, obj, FSCK_MSG_UNKNOWN_TYPE, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...)\n+int fsck_error_function(struct object *obj, int severity, const char *message)\n {\n-\tva_list ap;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tstrbuf_addf(&sb, \"object %s:\", sha1_to_hex(obj->sha1));\n-\n-\tva_start(ap, fmt);\n-\tstrbuf_vaddf(&sb, fmt, ap);\n-\tva_end(ap);\n-\n-\terror(\"%s\", sb.buf);\n-\tstrbuf_release(&sb);\n+\terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex 07d0ab2..f6f268a 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -17,10 +17,9 @@ struct fsck_options;\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 *err, ...);\n+typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n \n-__attribute__((format (printf, 3, 4)))\n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n+int fsck_error_function(struct object *obj, int type, const char *message);\n \n struct fsck_options {\n \tfsck_walk_func walk;\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254888","messageId":"5181b6a21b1f565c6715d3eb99bd6321c6c3a6f7.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 03/18] fsck: Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:50:19Z","receivedAt":"2015-01-19T15:50:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This function will be used in the next commits to allow the user to\nask fsck to handle specific problems differently, e.g. demoting certain\nerrors to warnings. It has to handle partial strings because we would\nlike to be able to parse, say, 'missing-email,missing-tagger-entry'\ncommand lines.\n\nTo make the parsing robust, we generate strings from the enum keys, and\nusing these keys, we will map lower-case, dash-separated strings values\nto the corresponding enum values.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 27 +++++++++++++++++++++++++--\n 1 file changed, 25 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 30f7a48..2d91e28 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -63,15 +63,38 @@ enum fsck_msg_id {\n };\n #undef MSG_ID\n \n-#define MSG_ID(id, severity) { FSCK_##severity },\n+#define STR(x) #x\n+#define MSG_ID(id, severity) { STR(id), FSCK_##severity },\n static struct {\n+\tconst char *id_string;\n \tint severity;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n-\t{ -1 }\n+\t{ NULL, -1 }\n };\n #undef MSG_ID\n \n+static int parse_msg_id(const char *text, int len)\n+{\n+\tint i, j;\n+\n+\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n+\t\tconst char *key = msg_id_info[i].id_string;\n+\t\t/* id_string is upper-case, with underscores */\n+\t\tfor (j = 0; j < len; j++) {\n+\t\t\tchar c = *(key++);\n+\t\t\tif (c == '_')\n+\t\t\t\tc = '-';\n+\t\t\tif (text[j] != tolower(c))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (j == len && !*key)\n+\t\t\treturn i;\n+\t}\n+\n+\tdie(\"Unhandled message id: %.*s\", len, text);\n+}\n+\n static int fsck_msg_severity(enum fsck_msg_id msg_id,\n \tstruct fsck_options *options)\n {\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254894","messageId":"a749c4a2542c32dfe256c24000e5556ebb3ce26f.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 04/18] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:50:25Z","receivedAt":"2015-01-19T15:50:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are legacy repositories out there whose older commits and tags\nhave issues that prevent pushing them when 'receive.fsckObjects' is set.\nOne real-life example is a commit object that has been hand-crafted to\nlist two authors.\n\nOften, it is not possible to fix those issues without disrupting the\nwork with said repositories, yet it is still desirable to perform checks\nby setting `receive.fsckObjects = true`. This commit is the first step\nto allow demoting specific fsck issues to mere warnings.\n\nThe function added by this commit parses a list of settings in the form:\n\n\tmissing-email=warn,bad-name=warn,...\n\nUnfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\ngit fsck so far, but other call paths (e.g. git index-pack --strict)\nerror out *always* no matter what type was specified. Therefore, we\nneed to take extra care to default to all FSCK_ERROR in those cases.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n fsck.h |  7 +++++--\n 2 files changed, 66 insertions(+), 5 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 2d91e28..7d4c22c 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -100,13 +100,67 @@ static int fsck_msg_severity(enum fsck_msg_id msg_id,\n {\n \tint severity;\n \n-\tseverity = msg_id_info[msg_id].severity;\n-\tif (options->strict && severity == FSCK_WARN)\n-\t\tseverity = FSCK_ERROR;\n+\tif (options->msg_severity && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n+\t\tseverity = options->msg_severity[msg_id];\n+\telse {\n+\t\tseverity = msg_id_info[msg_id].severity;\n+\t\tif (options->strict && severity == FSCK_WARN)\n+\t\t\tseverity = FSCK_ERROR;\n+\t}\n \n \treturn severity;\n }\n \n+static inline int substrcmp(const char *string, int len, const char *match)\n+{\n+\tint match_len = strlen(match);\n+\tif (match_len != len)\n+\t\treturn -1;\n+\treturn memcmp(string, match, len);\n+}\n+\n+void fsck_set_severity(struct fsck_options *options, const char *mode)\n+{\n+\tint severity = FSCK_ERROR;\n+\n+\tif (!options->msg_severity) {\n+\t\tint i;\n+\t\tint *msg_severity = malloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\t\tmsg_severity[i] = fsck_msg_severity(i, options);\n+\t\toptions->msg_severity = msg_severity;\n+\t}\n+\n+\twhile (*mode) {\n+\t\tint len = strcspn(mode, \" ,|\"), equal, msg_id;\n+\n+\t\tif (!len) {\n+\t\t\tmode++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (equal = 0; equal < len; equal++)\n+\t\t\tif (mode[equal] == '=')\n+\t\t\t\tbreak;\n+\n+\t\tif (equal < len) {\n+\t\t\tif (!substrcmp(mode, equal, \"error\"))\n+\t\t\t\tseverity = FSCK_ERROR;\n+\t\t\telse if (!substrcmp(mode, equal, \"warn\"))\n+\t\t\t\tseverity = FSCK_WARN;\n+\t\t\telse\n+\t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n+\t\t\t\t\tequal, mode);\n+\t\t\tmode += equal + 1;\n+\t\t\tlen -= equal + 1;\n+\t\t}\n+\n+\t\tmsg_id = parse_msg_id(mode, len);\n+\t\toptions->msg_severity[msg_id] = severity;\n+\t\tmode += len;\n+\t}\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -596,6 +650,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \n int fsck_error_function(struct object *obj, int severity, const char *message)\n {\n+\tif (severity == FSCK_WARN) {\n+\t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n+\t\treturn 0;\n+\t}\n \terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex f6f268a..4349860 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,8 @@\n \n struct fsck_options;\n \n+void fsck_set_severity(struct fsck_options *options, const char *mode);\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -25,10 +27,11 @@ struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n \tunsigned strict:1;\n+\tint *msg_severity;\n };\n \n-#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n-#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }\n \n /* descend in all linked child objects\n  * the return value is:\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254889","messageId":"3fd56573b263f2a4551c9a363d1a44728573a1c6.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 05/18] fsck: Allow demoting errors to warnings via receive.fsck.warn = <key>","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:50:32Z","receivedAt":"2015-01-19T15:50:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For example, missing emails in commit and tag objects can be demoted to\nmere warnings with\n\n\tgit config receive.fsck.warn = missing-email\n\nThe value is actually a comma-separated list, and there is a\ncorresponding receive.fsck.error setting.\n\nIn case that the same key is listed in multiple receive.fsck.* lines in\nthe config, the latter configuration wins.\n\nAs git receive-pack does not actually perform the checks, it hands off\nthe setting to index-pack or unpack-objects in the form of an optional\nargument to the --strict option.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/index-pack.c     |  4 ++++\n builtin/receive-pack.c   | 26 ++++++++++++++++++++++----\n builtin/unpack-objects.c |  5 +++++\n 3 files changed, 31 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 925f7b5..f464ca0 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1565,6 +1565,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(arg, \"--strict\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n+\t\t\t} else if (starts_with(arg, \"--strict=\")) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tdo_fsck_object = 1;\n+\t\t\t\tfsck_set_severity(&fsck_options, arg + 9);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex e0ce78e..da2e019 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -36,6 +36,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n+static struct strbuf fsck_severity = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int advertise_atomic_push = 1;\n@@ -115,6 +116,13 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (starts_with(var, \"receive.fsck.\")) {\n+\t\tif (fsck_severity.len)\n+\t\t\tstrbuf_addch(&fsck_severity, ',');\n+\t\tstrbuf_addf(&fsck_severity, \"%s=%s\", var + 13, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsckobjects\") == 0) {\n \t\treceive_fsck_objects = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1470,8 +1478,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"unpack-objects\", hdr_arg, NULL);\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n-\t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\tif (fsck_objects) {\n+\t\t\tif (fsck_severity.len)\n+\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n+\t\t\t\t\tfsck_severity.buf);\n+\t\t\telse\n+\t\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t}\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1488,8 +1501,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n-\t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\tif (fsck_objects) {\n+\t\t\tif (fsck_severity.len)\n+\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n+\t\t\t\t\tfsck_severity.buf);\n+\t\t\telse\n+\t\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t}\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 6d17040..82f2d62 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrict = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (starts_with(arg, \"--strict=\")) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tfsck_set_severity(&fsck_options, arg + 9);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\n \t\t\t\tstruct pack_header *hdr;\n \t\t\t\tchar *c;\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254886","messageId":"e7a87928aad28d124c25214e62766c724398b8fb.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 06/18] fsck: Report the ID of the error/warning","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:50:39Z","receivedAt":"2015-01-19T15:50:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some legacy code has objects with non-fatal fsck issues; To enable the\nuser to ignore those issues, let's print out the ID (e.g. when\nencountering \"missing-email\", the user might want to call `git config\nreceive.fsck.warn missing-email`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c          | 19 +++++++++++++++++++\n t/t1450-fsck.sh |  4 ++--\n 2 files changed, 21 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 7d4c22c..78944f0 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -161,6 +161,23 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t}\n }\n \n+static void append_msg_id(struct strbuf *sb, const char *msg_id)\n+{\n+\tfor (;;) {\n+\t\tchar c = *(msg_id)++;\n+\n+\t\tif (!c)\n+\t\t\tbreak;\n+\t\tif (c == '_')\n+\t\t\tc = '-';\n+\t\telse\n+\t\t\tc = tolower(c);\n+\t\tstrbuf_addch(sb, c);\n+\t}\n+\n+\tstrbuf_addstr(sb, \": \");\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -169,6 +186,8 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tappend_msg_id(&sb, msg_id_info[id].id_string);\n+\n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n \tresult = options->error_func(object, msg_severity, sb.buf);\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex cfb32b6..ea0f216 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: invalid-tag-name: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missing-tagger-entry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254891","messageId":"fd39fcf9ea878711423e03a162a9f671dcf3310c.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 07/18] fsck: Make fsck_ident() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:50:46Z","receivedAt":"2015-01-19T15:50:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_ident() identifies a problem with the ident, it should still\nadvance the pointer to the next line so that fsck can continue in the\ncase of a mere warning.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 49 +++++++++++++++++++++++++++----------------------\n 1 file changed, 27 insertions(+), 22 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 78944f0..233385b 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -453,40 +453,45 @@ static int require_end_of_header(const void *data, unsigned long size,\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n+\tconst char *p = *ident;\n \tchar *end;\n \n-\tif (**ident == '<')\n+\t*ident = strchrnul(*ident, '\\n');\n+\tif (**ident == '\\n')\n+\t\t(*ident)++;\n+\n+\tif (*p == '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident == '>')\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p == '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n-\tif (**ident != '<')\n+\tif (*p != '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n-\tif ((*ident)[-1] != ' ')\n+\tif (p[-1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t(*ident)++;\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident != '>')\n+\tp++;\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p != '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n-\t(*ident)++;\n-\tif (**ident != ' ')\n+\tp++;\n+\tif (*p != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n-\t(*ident)++;\n-\tif (**ident == '0' && (*ident)[1] != ' ')\n+\tp++;\n+\tif (*p == '0' && p[1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n-\tif (date_overflows(strtoul(*ident, &end, 10)))\n+\tif (date_overflows(strtoul(p, &end, 10)))\n \t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n-\tif (end == *ident || *end != ' ')\n+\tif ((end == p || *end != ' '))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n-\t*ident = end + 1;\n-\tif ((**ident != '+' && **ident != '-') ||\n-\t    !isdigit((*ident)[1]) ||\n-\t    !isdigit((*ident)[2]) ||\n-\t    !isdigit((*ident)[3]) ||\n-\t    !isdigit((*ident)[4]) ||\n-\t    ((*ident)[5] != '\\n'))\n+\tp = end + 1;\n+\tif ((*p != '+' && *p != '-') ||\n+\t    !isdigit(p[1]) ||\n+\t    !isdigit(p[2]) ||\n+\t    !isdigit(p[3]) ||\n+\t    !isdigit(p[4]) ||\n+\t    (p[5] != '\\n'))\n \t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n-\t(*ident) += 6;\n+\tp += 6;\n \treturn 0;\n }\n \n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254899","messageId":"f2b980d854afe6728e4d388771a13ce2894b71b6.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 08/18] fsck: Make fsck_commit() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:50:53Z","receivedAt":"2015-01-19T15:50:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_commit() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nNote that some problems are too problematic to simply ignore. For\nexample, when the header lines are mixed up, we punt after encountering\nan incorrect line. Therefore, demoting certain warnings to errors can\nhide other problems. Example: demoting the missing-author error to\na warning would hide a problematic committer line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 28 ++++++++++++++++++++--------\n 1 file changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 233385b..a3b1429 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -508,12 +508,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n-\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n+\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n-\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -522,11 +528,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \tif (graft) {\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n-\t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\telse if (graft->nr_parent != parent_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t} else {\n-\t\tif (parent_count != parent_line_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\tif (parent_count != parent_line_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254887","messageId":"065ce6d5053d3aca7860ea88c1a1742bd9c656bd.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 09/18] fsck: Handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:01Z","receivedAt":"2015-01-19T15:51:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This problem has been detected in the wild, and is the primary reason\nto introduce an option to demote certain fsck errors to warnings. Let's\noffer to ignore this particular problem specifically.\n\nTechnically, we could handle such repositories by setting\nreceive.fsck.warn = missing-committer, but that could hide missing tree\nobjects in the same commit because we cannot continue verifying any\ncommit object after encountering a missing committer line, while we can\ncontinue in the case of multiple author lines.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex a3b1429..ed0a669 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -38,6 +38,7 @@\n \tFUNC(MISSING_TREE, ERROR) \\\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NOT_SORTED, ERROR) \\\n \tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n@@ -545,6 +546,14 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n+\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n+\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\terr = fsck_ident(&buffer, &commit->object, options);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254881","messageId":"ab7482bf5661aa8ab23dd65bcd6f8bc037db50ed.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 10/18] fsck: Make fsck_tag() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:08Z","receivedAt":"2015-01-19T15:51:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_tag() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nJust like fsck_commit(), there are certain problems that could hide other\nissues with the same tag object. For example, if the 'type' line is not\nencountered in the correct position, the 'tag' line – if there is any –\nwould not be handled at all.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex ed0a669..b8cbbfb 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -614,7 +614,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n \t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n-\t\tgoto done;\n+\t\tif (ret)\n+\t\t\tgoto done;\n \t}\n \tbuffer += 41;\n \n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254882","messageId":"ba5b133ec7dfad799107f041489f693022f0fcc8.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 11/18] fsck: Add a simple test for receive.fsck.*","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:14Z","receivedAt":"2015-01-19T15:51:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5504-fetch-receive-strict.sh | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 69ee13c..d491172 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -115,4 +115,24 @@ test_expect_success 'push with transfer.fsckobjects' '\n \ttest_cmp exp act\n '\n \n+cat >bogus-commit << EOF\n+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+author Bugs Bunny 1234567890 +0000\n+committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n+\n+This commit object intentionally broken\n+EOF\n+\n+test_expect_success 'push with receive.fsck.warn = missing-email' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.warn missing-email &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\tgrep \"missing-email\" act\n+'\n+\n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254885","messageId":"ed901ae6a0bcd8e2d0d45b55a105e0f2b1aa916f.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 12/18] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:22Z","receivedAt":"2015-01-19T15:51:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some kinds of errors are intrinsically unrecoverable (e.g. errors while\nuncompressing objects). It does not make sense to allow demoting them to\nmere warnings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 13 +++++++++++--\n t/t5504-fetch-receive-strict.sh |  9 +++++++++\n 2 files changed, 20 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex b8cbbfb..f2c8044 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,12 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FSCK_FATAL -1\n+\n #define FOREACH_MSG_ID(FUNC) \\\n+\t/* fatal errors */ \\\n+\tFUNC(NUL_IN_HEADER, FATAL) \\\n+\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n \t/* errors */ \\\n \tFUNC(BAD_DATE, ERROR) \\\n \tFUNC(BAD_EMAIL, ERROR) \\\n@@ -40,10 +45,8 @@\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NOT_SORTED, ERROR) \\\n-\tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n \tFUNC(UNKNOWN_TYPE, ERROR) \\\n-\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE, WARN) \\\n@@ -157,6 +160,9 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t}\n \n \t\tmsg_id = parse_msg_id(mode, len);\n+\t\tif (severity != FSCK_ERROR &&\n+\t\t\t\tmsg_id_info[msg_id].severity == FSCK_FATAL)\n+\t\t\tdie(\"Cannot demote %.*s\", len, mode);\n \t\toptions->msg_severity[msg_id] = severity;\n \t\tmode += len;\n \t}\n@@ -187,6 +193,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tif (msg_severity == FSCK_FATAL)\n+\t\tmsg_severity = FSCK_ERROR;\n+\n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n \tva_start(ap, fmt);\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex d491172..2757c3a 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -135,4 +135,13 @@ test_expect_success 'push with receive.fsck.warn = missing-email' '\n \tgrep \"missing-email\" act\n '\n \n+test_expect_success 'receive.fsck.warn = unterminated-header triggers error' '\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\tgit --git-dir=dst/.git config receive.fsck.warn unterminated-header &&\n+\ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n+\tgrep \"Cannot demote unterminated-header\" act\n+'\n+\n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254884","messageId":"342278d6d900bced3fbb46fc1ecee995afff1ede.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 13/18] fsck: Optionally ignore specific fsck issues completely","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:29Z","receivedAt":"2015-01-19T15:51:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An fsck issue in a legacy repository might be so common that one would\nlike not to bother the user with mentioning it at all. With this change,\nthat is possible by setting the respective error to \"ignore\".\n\nThis change \"abuses\" the warn=missing-email test to verify that \"ignore\"\nis also accepted and works correctly. And while at it, it makes sure\nthat multiple options work, too (they are passed to unpack-objects or\nindex-pack as a comma-separated list via the --strict=... command-line\noption).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 5 +++++\n fsck.h                          | 1 +\n t/t5504-fetch-receive-strict.sh | 7 ++++++-\n 3 files changed, 12 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex f2c8044..649c8fe 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -152,6 +152,8 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t\t\tseverity = FSCK_ERROR;\n \t\t\telse if (!substrcmp(mode, equal, \"warn\"))\n \t\t\t\tseverity = FSCK_WARN;\n+\t\t\telse if (!substrcmp(mode, equal, \"ignore\"))\n+\t\t\t\tseverity = FSCK_IGNORE;\n \t\t\telse\n \t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n \t\t\t\t\tequal, mode);\n@@ -193,6 +195,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tif (msg_severity == FSCK_IGNORE)\n+\t\treturn 0;\n+\n \tif (msg_severity == FSCK_FATAL)\n \t\tmsg_severity = FSCK_ERROR;\n \ndiff --git a/fsck.h b/fsck.h\nindex 4349860..7be6c50 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -3,6 +3,7 @@\n \n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n+#define FSCK_IGNORE 3\n \n struct fsck_options;\n \ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 2757c3a..e81cedb 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -132,7 +132,12 @@ test_expect_success 'push with receive.fsck.warn = missing-email' '\n \ttest_must_fail git push --porcelain dst bogus &&\n \tgit --git-dir=dst/.git config receive.fsck.warn missing-email &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missing-email\" act\n+\tgrep \"missing-email\" act &&\n+\tgit --git-dir=dst/.git branch -D bogus &&\n+\tgit  --git-dir=dst/.git config receive.fsck.ignore missing-email &&\n+\tgit  --git-dir=dst/.git config receive.fsck.warn bad-date &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\ttest_must_fail grep \"missing-email\" act\n '\n \n test_expect_success 'receive.fsck.warn = unterminated-header triggers error' '\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254898","messageId":"2a520e8d2b9f332f61edd7f8e45eab01b46bd508.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 14/18] fsck: Allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:37Z","receivedAt":"2015-01-19T15:51:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'invalid tag name' and 'missing tagger entry' warnings can now be\nupgraded to errors by specifying `invalid-tag-name` and\n`missing-tagger-entry` to the receive.fsck.error config setting.\n\nIncidentally, the missing tagger warning is now really shown as a warning\n(as opposed to being reported with the \"error:\" prefix, as it used to be\nthe case before this commit).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                | 24 +++++++++++++++++-------\n t/t5302-pack-index.sh |  2 +-\n 2 files changed, 18 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 649c8fe..480cd87 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -10,6 +10,7 @@\n #include \"utf8.h\"\n \n #define FSCK_FATAL -1\n+#define FSCK_INFO -2\n \n #define FOREACH_MSG_ID(FUNC) \\\n \t/* fatal errors */ \\\n@@ -55,10 +56,11 @@\n \tFUNC(HAS_DOT, WARN) \\\n \tFUNC(HAS_DOTDOT, WARN) \\\n \tFUNC(HAS_DOTGIT, WARN) \\\n-\tFUNC(INVALID_TAG_NAME, WARN) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n \tFUNC(NULL_SHA1, WARN) \\\n-\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n+\t/* infos (reported as warnings, but ignored by default) */ \\\n+\tFUNC(INVALID_TAG_NAME, INFO) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n #define MSG_ID(id, severity) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -200,6 +202,8 @@ static int report(struct fsck_options *options, struct object *object,\n \n \tif (msg_severity == FSCK_FATAL)\n \t\tmsg_severity = FSCK_ERROR;\n+\telse if (msg_severity == FSCK_INFO)\n+\t\tmsg_severity = FSCK_WARN;\n \n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n@@ -658,15 +662,21 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n-\tif (check_refname_format(sb.buf, 0))\n-\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\tif (check_refname_format(sb.buf, 0)) {\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n \t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \tbuffer = eol + 1;\n \n-\tif (!skip_prefix(buffer, \"tagger \", &buffer))\n+\tif (!skip_prefix(buffer, \"tagger \", &buffer)) {\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 61bc8da..3dc5ec4 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -259,7 +259,7 @@ EOF\n     thirtyeight=${tag#??} &&\n     rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&\n     git index-pack --strict tag-test-${pack1}.pack 2>err &&\n-    grep \"^error:.* expected .tagger. line\" err\n+    grep \"^warning:.* expected .tagger. line\" err\n '\n \n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254883","messageId":"41d4880fc48ad39d4798c2f9a0cb38a6a41c41b3.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 15/18] fsck: Document the new receive.fsck.* options.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:44Z","receivedAt":"2015-01-19T15:51:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 25 +++++++++++++++++++++++++\n 1 file changed, 25 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ae6791d..7371a5f 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2130,6 +2130,31 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n+receive.fsck.*::\n+\tWhen `receive.fsckObjects` is set to true, errors can be switched\n+\tto warnings and vice versa by configuring the `receive.fsck.*`\n+\tsettings. These settings contain comma-separated lists of fsck\n+\tmessage IDs. For convenience, fsck prefixes the error/warning with\n+\tthe message ID, e.g. \"missing-email: invalid author/committer line\n+\t- missing email\" means that setting `receive.fsck.ignore =\n+\tmissing-email` will hide that issue.\n++\n+--\n+\terror::\n+\t\ta comma-separated list of fsck message IDs that should be\n+\t\ttrigger fsck to error out.\n+\twarn::\n+\t\ta comma-separated list of fsck message IDs that should be\n+\t\tdisplayed, but fsck should continue to error out.\n+\tignore::\n+\t\ta comma-separated list of fsck message IDs that should be\n+\t\tignored completely.\n++\n+This feature is intended to support working with legacy repositories\n+which would not pass pushing when `receive.fsckObjects = true`, allowing\n+the host to accept repositories certain known issues but still catch\n+other issues.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254890","messageId":"f038d7576de0052df1622bd833f31512f69a52a8.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 16/18] fsck: Support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:51Z","receivedAt":"2015-01-19T15:51:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already have support in `git receive-pack` to deal with some legacy\nrepositories which have non-fatal issues.\n\nLet's make `git fsck` itself useful with such repositories, too, by\nallowing users to ignore known issues, or at least demote those issues\nto mere warnings.\n\nExample: `git -c fsck.ignore=missing-email fsck` would hide problems with\nmissing emails in author, committer and tagger lines.\n\nIn the same spirit that `git receive-pack`'s usage of the fsck machinery\ndiffers from `git fsck`'s – some of the non-fatal warnings in `git fsck`\nare fatal with `git receive-pack` when receive.fsckObjects = true, for\nexample – we strictly separate the fsck.* from the receive.fsck.*\nsettings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 13 +++++++++++++\n builtin/fsck.c           | 15 +++++++++++++++\n t/t1450-fsck.sh          | 11 +++++++++++\n 3 files changed, 39 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 7371a5f..0daba8a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1208,6 +1208,19 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n+fsck.*::\n+\tThe `fsck.error`, `fsck.warn` and `fsck.ignore` settings specify\n+\tcomma-separated lists of fsck message IDs which should trigger\n+\tfsck to error out, to print the message and continue, or to ignore\n+\tsaid messages, respectively.\n++\n+For convenience, fsck prefixes the error/warning with the name of the option,\n+e.g.  \"missing-email: invalid author/committer line - missing email\" means\n+that setting `fsck.ignore = missing-email` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which cannot be repaired without disruptive changes.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 99d4538..d5403c4 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,6 +46,19 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n+static int fsck_config(const char *var, const char *value, void *cb)\n+{\n+\tif (starts_with(var, \"fsck.\")) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"%s=%s\", var + 5, value);\n+\t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n static void objreport(struct object *obj, const char *severity,\n                       const char *err)\n {\n@@ -638,6 +651,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tgit_config(fsck_config, NULL);\n+\n \tfsck_head_link();\n \tfsck_object_dir(get_object_directory());\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex ea0f216..a79ff9f 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -287,6 +287,17 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '\n \tgrep -q \"error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff\" out\n '\n \n+test_expect_success 'force fsck to ignore double author' '\n+\tgit cat-file commit HEAD >basis &&\n+\tsed \"s/^author .*/&,&/\" <basis | tr , \\\\n >multiple-authors &&\n+\tnew=$(git hash-object -t commit -w --stdin <multiple-authors) &&\n+\ttest_when_finished \"remove_object $new\" &&\n+\tgit update-ref refs/heads/bogus \"$new\" &&\n+\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n+\ttest_must_fail git fsck &&\n+\tgit -c fsck.ignore=multiple-authors fsck\n+'\n+\n _bz='\\0'\n _bz5=\"$_bz$_bz$_bz$_bz$_bz\"\n _bz20=\"$_bz5$_bz5$_bz5$_bz5\"\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254897","messageId":"5b9bf9ddcac10e3981c9ac7ae5a0af30e25455de.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 17/18] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:51:58Z","receivedAt":"2015-01-19T15:51:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This option avoids unpacking each and all objects, and just verifies the\nconnectivity. In particular with large repositories, this speeds up the\noperation, at the expense of missing corrupt blobs and ignoring\nunreachable objects, if any.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  7 ++++++-\n builtin/fsck.c             |  7 ++++++-\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 3 files changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 25c431d..b98fb43 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n-\t [--[no-]full] [--strict] [--verbose] [--lost-found]\n+\t [--[no-]full] [--quick] [--strict] [--verbose] [--lost-found]\n \t [--[no-]dangling] [--[no-]progress] [<object>*]\n \n DESCRIPTION\n@@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \tobject pools.  This is now default; you can turn it off\n \twith --no-full.\n \n+--quick::\n+\tCheck only the connectivity of tags, commits and tree objects. By\n+\tavoiding to unpack blobs, this speeds up the operation, at the\n+\texpense of missing corrupt objects.\n+\n --strict::\n \tEnable more strict checking, namely to catch a file mode\n \trecorded with g+w bit set, which was created by older\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex d5403c4..c767909 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -23,6 +23,7 @@ static int show_tags;\n static int show_unreachable;\n static int include_reflogs = 1;\n static int check_full = 1;\n+static int quick;\n static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n@@ -184,6 +185,8 @@ static void check_reachable_object(struct object *obj)\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (has_sha1_pack(obj->sha1))\n \t\t\treturn; /* it is in pack - forget about it */\n+\t\tif (quick && has_sha1_file(obj->sha1))\n+\t\t\treturn;\n \t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n@@ -618,6 +621,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"cache\", &keep_cache_objects, N_(\"make index objects head nodes\")),\n \tOPT_BOOL(0, \"reflogs\", &include_reflogs, N_(\"make reflogs head nodes (default)\")),\n \tOPT_BOOL(0, \"full\", &check_full, N_(\"also consider packs and alternate objects\")),\n+\tOPT_BOOL(0, \"quick\", &quick, N_(\"check only connectivity\")),\n \tOPT_BOOL(0, \"strict\", &check_strict, N_(\"enable more strict checking\")),\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n@@ -654,7 +658,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tfsck_object_dir(get_object_directory());\n+\tif (!quick)\n+\t\tfsck_object_dir(get_object_directory());\n \n \tprepare_alt_odb();\n \tfor (alt = alt_odb_list; alt; alt = alt->next) {\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex a79ff9f..1c624a3 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n \ttest_must_fail git -C missing fsck\n '\n \n+test_expect_success 'fsck --quick' '\n+\trm -rf quick &&\n+\tgit init quick &&\n+\t(\n+\t\tcd quick &&\n+\t\ttouch empty &&\n+\t\tgit add empty &&\n+\t\ttest_commit empty &&\n+\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n+\t\trm -f $empty &&\n+\t\techo invalid >$empty &&\n+\t\ttest_must_fail git fsck --strict &&\n+\t\tgit fsck --strict --quick &&\n+\t\ttree=$(git rev-parse HEAD:) &&\n+\t\tsuffix=${tree#??} &&\n+\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n+\t\trm -f $tree &&\n+\t\techo invalid >$tree &&\n+\t\ttest_must_fail git fsck --strict --quick\n+\t)\n+'\n+\n test_done\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254892","messageId":"dd54b4dc097f18bb3427ec6408e71ad65efd08a1.1421682369.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 18/18] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-19T15:52:06Z","receivedAt":"2015-01-19T15:52:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The optional new config option `receive.fsck.skiplist` specifies the path\nto a file listing the names, i.e. SHA-1s, one per line, of objects that\nare to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nThe intended use case is for server administrators to inspect objects\nthat are reported by `git push` as being too problematic to enter the\nrepository, and to add the objects' SHA-1 to a (preferably sorted) file\nwhen the objects are legitimate, i.e. when it is determined that those\nproblematic objects should be allowed to enter the server.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/receive-pack.c          |  9 +++++++\n fsck.c                          | 53 +++++++++++++++++++++++++++++++++++++++++\n fsck.h                          |  1 +\n t/t5504-fetch-receive-strict.sh | 12 ++++++++++\n 4 files changed, 75 insertions(+)\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex da2e019..40514c2 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -116,6 +116,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (starts_with(var, \"receive.fsck.skiplist\")) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tif (fsck_severity.len)\n+\t\t\tstrbuf_addch(&fsck_severity, ',');\n+\t\tstrbuf_addf(&fsck_severity, \"skiplist=%s\", path);\n+\t\treturn 0;\n+\t}\n+\n \tif (starts_with(var, \"receive.fsck.\")) {\n \t\tif (fsck_severity.len)\n \t\t\tstrbuf_addch(&fsck_severity, ',');\ndiff --git a/fsck.c b/fsck.c\nindex 480cd87..dbf9fa1 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -8,6 +8,7 @@\n #include \"fsck.h\"\n #include \"refs.h\"\n #include \"utf8.h\"\n+#include \"sha1-array.h\"\n \n #define FSCK_FATAL -1\n #define FSCK_INFO -2\n@@ -117,6 +118,43 @@ static int fsck_msg_severity(enum fsck_msg_id msg_id,\n \treturn severity;\n }\n \n+static void init_skiplist(struct fsck_options *options, const char *path)\n+{\n+\tstatic struct sha1_array skiplist = SHA1_ARRAY_INIT;\n+\tint sorted, fd;\n+\tchar buffer[41];\n+\tunsigned char sha1[20];\n+\n+\tif (options->skiplist)\n+\t\tsorted = options->skiplist->sorted;\n+\telse {\n+\t\tsorted = 1;\n+\t\toptions->skiplist = &skiplist;\n+\t}\n+\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Could not open skip list: %s\", path);\n+\tfor (;;) {\n+\t\tint result = read_in_full(fd, buffer, sizeof(buffer));\n+\t\tif (result < 0)\n+\t\t\tdie_errno(\"Could not read '%s'\", path);\n+\t\tif (!result)\n+\t\t\tbreak;\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n+\t\t\tdie(\"Invalid SHA-1: %s\", buffer);\n+\t\tsha1_array_append(&skiplist, sha1);\n+\t\tif (sorted && skiplist.nr > 1 &&\n+\t\t\t\thashcmp(skiplist.sha1[skiplist.nr - 2],\n+\t\t\t\t\tsha1) > 0)\n+\t\t\tsorted = 0;\n+\t}\n+\tclose(fd);\n+\n+\tif (sorted)\n+\t\tskiplist.sorted = 1;\n+}\n+\n static inline int substrcmp(const char *string, int len, const char *match)\n {\n \tint match_len = strlen(match);\n@@ -156,6 +194,17 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t\t\tseverity = FSCK_WARN;\n \t\t\telse if (!substrcmp(mode, equal, \"ignore\"))\n \t\t\t\tseverity = FSCK_IGNORE;\n+\t\t\telse if (!substrcmp(mode, equal, \"skiplist\")) {\n+\t\t\t\tchar *path = xstrndup(mode + equal + 1,\n+\t\t\t\t\tlen - equal - 1);\n+\n+\t\t\t\tif (equal == len)\n+\t\t\t\t\tdie(\"skiplist requires a path\");\n+\t\t\t\tinit_skiplist(options, path);\n+\t\t\t\tfree(path);\n+\t\t\t\tmode += len;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\telse\n \t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n \t\t\t\t\tequal, mode);\n@@ -700,6 +749,10 @@ static int fsck_tag(struct tag *tag, const char *data,\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n+\tif (options->skiplist &&\n+\t\t\tsha1_array_lookup(options->skiplist, obj->sha1) >= 0)\n+\t\treturn 0;\n+\n \tif (!obj)\n \t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \ndiff --git a/fsck.h b/fsck.h\nindex 7be6c50..cae280e 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -29,6 +29,7 @@ struct fsck_options {\n \tfsck_error error_func;\n \tunsigned strict:1;\n \tint *msg_severity;\n+\tstruct sha1_array *skiplist;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex e81cedb..21fa9c8 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,6 +123,18 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n+test_expect_success 'push with receive.fsck.skiplist' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.skiplist SKIP &&\n+\techo $commit > dst/.git/SKIP &&\n+\tgit push --porcelain dst bogus\n+'\n+\n test_expect_success 'push with receive.fsck.warn = missing-email' '\n \tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n-- \n2.0.0.rc3.9669.g840d1f9\n"},{"id":"254909","messageId":"CAPig+cSVUAV_6c0Zv36aA++AWCG05yxq-LpPsQ89VoepHGUBRw@mail.gmail.com","threadId":"38144","inReplyTo":"41d4880fc48ad39d4798c2f9a0cb38a6a41c41b3.1421682369.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 15/18] fsck: Document the new receive.fsck.* options.","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2015-01-19T22:44:45Z","receivedAt":"2015-01-19T22:44:45Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Jan 19, 2015 at 10:51 AM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index ae6791d..7371a5f 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -2130,6 +2130,31 @@ receive.fsckObjects::\n>         Defaults to false. If not set, the value of `transfer.fsckObjects`\n>         is used instead.\n>\n> +receive.fsck.*::\n> +       When `receive.fsckObjects` is set to true, errors can be switched\n> +       to warnings and vice versa by configuring the `receive.fsck.*`\n> +       settings. These settings contain comma-separated lists of fsck\n> +       message IDs. For convenience, fsck prefixes the error/warning with\n> +       the message ID, e.g. \"missing-email: invalid author/committer line\n> +       - missing email\" means that setting `receive.fsck.ignore =\n> +       missing-email` will hide that issue.\n> ++\n> +--\n> +       error::\n> +               a comma-separated list of fsck message IDs that should be\n> +               trigger fsck to error out.\n> +       warn::\n> +               a comma-separated list of fsck message IDs that should be\n> +               displayed, but fsck should continue to error out.\n> +       ignore::\n> +               a comma-separated list of fsck message IDs that should be\n> +               ignored completely.\n> ++\n> +This feature is intended to support working with legacy repositories\n> +which would not pass pushing when `receive.fsckObjects = true`, allowing\n> +the host to accept repositories certain known issues but still catch\n\ns/certain/with &/\n\n> +other issues.\n> +\n>  receive.unpackLimit::\n>         If the number of objects received in a push is below this\n>         limit then the objects will be unpacked into loose object\n> --\n> 2.0.0.rc3.9669.g840d1f9\n>\n>\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"254913","messageId":"af8b0ce029243f91e048d474c780b466@www.dscho.org","threadId":"38144","inReplyTo":"CAPig+cSVUAV_6c0Zv36aA++AWCG05yxq-LpPsQ89VoepHGUBRw@mail.gmail.com","subject":"Re: [PATCH v2 15/18] fsck: Document the new receive.fsck.* options.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-20T07:24:51Z","receivedAt":"2015-01-20T07:24:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Eric,\n\nOn 2015-01-19 23:44, Eric Sunshine wrote:\n> On Mon, Jan 19, 2015 at 10:51 AM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n>> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>> ---\n>> diff --git a/Documentation/config.txt b/Documentation/config.txt\n>> index ae6791d..7371a5f 100644\n>> --- a/Documentation/config.txt\n>> +++ b/Documentation/config.txt\n>> @@ -2130,6 +2130,31 @@ receive.fsckObjects::\n>>         Defaults to false. If not set, the value of `transfer.fsckObjects`\n>>         is used instead.\n>>\n>> +receive.fsck.*::\n>> +       When `receive.fsckObjects` is set to true, errors can be switched\n>> +       to warnings and vice versa by configuring the `receive.fsck.*`\n>> +       settings. These settings contain comma-separated lists of fsck\n>> +       message IDs. For convenience, fsck prefixes the error/warning with\n>> +       the message ID, e.g. \"missing-email: invalid author/committer line\n>> +       - missing email\" means that setting `receive.fsck.ignore =\n>> +       missing-email` will hide that issue.\n>> ++\n>> +--\n>> +       error::\n>> +               a comma-separated list of fsck message IDs that should be\n>> +               trigger fsck to error out.\n>> +       warn::\n>> +               a comma-separated list of fsck message IDs that should be\n>> +               displayed, but fsck should continue to error out.\n>> +       ignore::\n>> +               a comma-separated list of fsck message IDs that should be\n>> +               ignored completely.\n>> ++\n>> +This feature is intended to support working with legacy repositories\n>> +which would not pass pushing when `receive.fsckObjects = true`, allowing\n>> +the host to accept repositories certain known issues but still catch\n> \n> s/certain/with &/\n\nGood catch. Fixed here (to be included in the next re-roll):\n\nhttps://github.com/dscho/git/commit/2517476646835e61c33581935fc68062a8ff3f56#diff-ba92ef40c548c691816362bbdc35a613R2155\n\nThanks!\nDscho\n"},{"id":"254977","messageId":"xmqqppa8y0v4.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"a749c4a2542c32dfe256c24000e5556ebb3ce26f.1421682369.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 04/18] fsck: Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-21T08:49:35Z","receivedAt":"2015-01-21T08:49:35Z","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 inline int substrcmp(const char *string, int len, const char *match)\n> +{\n> +\tint match_len = strlen(match);\n> +\tif (match_len != len)\n> +\t\treturn -1;\n> +\treturn memcmp(string, match, len);\n> +}\n\nIs this what we call \"starts_with()\" these days?\n\n> +void fsck_set_severity(struct fsck_options *options, const char *mode)\n> +{\n> +\tint severity = FSCK_ERROR;\n> +\n> +\tif (!options->msg_severity) {\n> +\t\tint i;\n> +\t\tint *msg_severity = malloc(sizeof(int) * FSCK_MSG_MAX);\n\nxmalloc()?\n"},{"id":"254979","messageId":"xmqqlhkwy0my.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"3fd56573b263f2a4551c9a363d1a44728573a1c6.1421682369.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 05/18] fsck: Allow demoting errors to warnings via receive.fsck.warn = <key>","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-21T08:54:29Z","receivedAt":"2015-01-21T08:54:29Z","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>  \n> +\tif (starts_with(var, \"receive.fsck.\")) {\n> +\t\tif (fsck_severity.len)\n> +\t\t\tstrbuf_addch(&fsck_severity, ',');\n> +\t\tstrbuf_addf(&fsck_severity, \"%s=%s\", var + 13, value);\n\nWouldn't it be safer to use skip_prefix() that lets you avoid the\nhardcoded \"var + 13\" here?\n\n> @@ -1470,8 +1478,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n>  \t\targv_array_pushl(&child.args, \"unpack-objects\", hdr_arg, NULL);\n>  \t\tif (quiet)\n>  \t\t\targv_array_push(&child.args, \"-q\");\n> -\t\tif (fsck_objects)\n> -\t\t\targv_array_push(&child.args, \"--strict\");\n> +\t\tif (fsck_objects) {\n> +\t\t\tif (fsck_severity.len)\n> +\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n> +\t\t\t\t\tfsck_severity.buf);\n> +\t\t\telse\n> +\t\t\t\targv_array_push(&child.args, \"--strict\");\n> +\t\t}\n>  \t\tchild.no_stdout = 1;\n>  \t\tchild.err = err_fd;\n>  \t\tchild.git_cmd = 1;\n> @@ -1488,8 +1501,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n>  \n>  \t\targv_array_pushl(&child.args, \"index-pack\",\n>  \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n> -\t\tif (fsck_objects)\n> -\t\t\targv_array_push(&child.args, \"--strict\");\n> +\t\tif (fsck_objects) {\n> +\t\t\tif (fsck_severity.len)\n> +\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n> +\t\t\t\t\tfsck_severity.buf);\n> +\t\t\telse\n> +\t\t\t\targv_array_push(&child.args, \"--strict\");\n> +\t\t}\n\nHmm.  The above two hunks look suspiciously similar.  Would it be\nworth to give them a single helper function?\n\n> diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\n> index 6d17040..82f2d62 100644\n> --- a/builtin/unpack-objects.c\n> +++ b/builtin/unpack-objects.c\n> @@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n>  \t\t\t\tstrict = 1;\n>  \t\t\t\tcontinue;\n>  \t\t\t}\n> +\t\t\tif (starts_with(arg, \"--strict=\")) {\n> +\t\t\t\tstrict = 1;\n> +\t\t\t\tfsck_set_severity(&fsck_options, arg + 9);\n> +\t\t\t\tcontinue;\n> +\t\t\t}\n>  \t\t\tif (starts_with(arg, \"--pack_header=\")) {\n>  \t\t\t\tstruct pack_header *hdr;\n>  \t\t\t\tchar *c;\n"},{"id":"254980","messageId":"xmqqh9vky0ja.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"fd39fcf9ea878711423e03a162a9f671dcf3310c.1421682369.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 07/18] fsck: Make fsck_ident() warn-friendly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-21T08:56:41Z","receivedAt":"2015-01-21T08:56:41Z","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> When fsck_ident() identifies a problem with the ident, it should still\n> advance the pointer to the next line so that fsck can continue in the\n> case of a mere warning.\n\nQuite sensible.\n\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c | 49 +++++++++++++++++++++++++++----------------------\n>  1 file changed, 27 insertions(+), 22 deletions(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index 78944f0..233385b 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -453,40 +453,45 @@ static int require_end_of_header(const void *data, unsigned long size,\n>  \n>  static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n>  {\n> +\tconst char *p = *ident;\n>  \tchar *end;\n>  \n> -\tif (**ident == '<')\n> +\t*ident = strchrnul(*ident, '\\n');\n> +\tif (**ident == '\\n')\n> +\t\t(*ident)++;\n> +\n> +\tif (*p == '<')\n>  \t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n"},{"id":"254981","messageId":"xmqqd268y0f2.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"ba5b133ec7dfad799107f041489f693022f0fcc8.1421682369.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 11/18] fsck: Add a simple test for receive.fsck.*","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-21T08:59:13Z","receivedAt":"2015-01-21T08:59:13Z","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> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  t/t5504-fetch-receive-strict.sh | 20 ++++++++++++++++++++\n>  1 file changed, 20 insertions(+)\n>\n> diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\n> index 69ee13c..d491172 100755\n> --- a/t/t5504-fetch-receive-strict.sh\n> +++ b/t/t5504-fetch-receive-strict.sh\n> @@ -115,4 +115,24 @@ test_expect_success 'push with transfer.fsckobjects' '\n>  \ttest_cmp exp act\n>  '\n>  \n> +cat >bogus-commit << EOF\n\n\"cat >bogus-commit <<\\EOF\", to reduce the mental burden of readers.\n\n> +tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n> +author Bugs Bunny 1234567890 +0000\n> +committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n> +\n> +This commit object intentionally broken\n> +EOF\n> +\n> +test_expect_success 'push with receive.fsck.warn = missing-email' '\n> +\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n> +\tgit push . $commit:refs/heads/bogus &&\n> +\trm -rf dst &&\n> +\tgit init dst &&\n> +\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n> +\ttest_must_fail git push --porcelain dst bogus &&\n> +\tgit --git-dir=dst/.git config receive.fsck.warn missing-email &&\n> +\tgit push --porcelain dst bogus >act 2>&1 &&\n> +\tgrep \"missing-email\" act\n> +'\n> +\n>  test_done\n"},{"id":"254982","messageId":"xmqq8ugwy094.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"dd54b4dc097f18bb3427ec6408e71ad65efd08a1.1421682369.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 18/18] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-21T09:02:47Z","receivedAt":"2015-01-21T09:02:47Z","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> The optional new config option `receive.fsck.skiplist` specifies the path\n> to a file listing the names, i.e. SHA-1s, one per line, of objects that\n> are to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n\nMakes sense, but I wonder if it makes sense to have a similar \"ok to\nbe broken\" list for \"git fsck\" (or perhaps they could even use the\nsame list) for exactly the same reason why this option makes sense.\n"},{"id":"254987","messageId":"xmqq4mrkxzlb.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"cover.1421682369.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 00/18] Introduce an internal API to interact with the fsck machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-21T09:17:04Z","receivedAt":"2015-01-21T09:17:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The documentation did not format well.  Tentatively I added the\nattached fix-up on top of the series before merging to 'pu'.\n\n * The wildcard in \"fsck.*\" and \"receive.fsck.*\" may have made sense\n   back in v1 when the variables are unbounded set, but v2 fixes it\n   and we now have a known fixed set of variables, so lets list them\n   explicitly (this is not a \"fix to unbreak formatting\").\n\n * The \"--\" that is not closed was giving me this:\n\n    asciidoc: ERROR: git-config.txt: line 413: section title not permitted in delimited block\n    asciidoc: ERROR: config.txt: line 2414: [blockdef-open] missing closing delimiter\n    make: *** [git-config.xml] Error 1\n\n   (this is \"workaround to unbreak formatting\"; I didn't check the\n   actual output closely).\n\n * the line that begins with \"- missing email\" after indent was\n   taken as an bulletted item or something, so I rewrapped the\n   paragraph somewhat to avoid having the dash at the beginning.\n\n\n Documentation/config.txt | 35 +++++++++++++++++++----------------\n 1 file changed, 19 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6718578..aae66bb 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1208,7 +1208,9 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n-fsck.*::\n+fsck.error::\n+fsck.warn::\n+fsck.ignore::\n \tThe `fsck.error`, `fsck.warn` and `fsck.ignore` settings specify\n \tcomma-separated lists of fsck message IDs which should trigger\n \tfsck to error out, to print the message and continue, or to ignore\n@@ -2138,25 +2140,26 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n-receive.fsck.*::\n+receive.fsck.error::\n+receive.fsck.warn::\n+receive.fsck.ignore::\n \tWhen `receive.fsckObjects` is set to true, errors can be switched\n \tto warnings and vice versa by configuring the `receive.fsck.*`\n \tsettings. These settings contain comma-separated lists of fsck\n \tmessage IDs. For convenience, fsck prefixes the error/warning with\n-\tthe message ID, e.g. \"missing-email: invalid author/committer line\n-\t- missing email\" means that setting `receive.fsck.ignore =\n-\tmissing-email` will hide that issue.\n-+\n---\n-\terror::\n-\t\ta comma-separated list of fsck message IDs that should be\n-\t\ttrigger fsck to error out.\n-\twarn::\n-\t\ta comma-separated list of fsck message IDs that should be\n-\t\tdisplayed, but fsck should continue to error out.\n-\tignore::\n-\t\ta comma-separated list of fsck message IDs that should be\n-\t\tignored completely.\n+\tthe message ID, e.g. \"missing-email: invalid\n+\tauthor/committer line - missing email\" means that setting\n+\t`receive.fsck.ignore = missing-email` will hide that issue.\n++\n+error;;\n+\ta comma-separated list of fsck message IDs that should be\n+\ttrigger fsck to error out.\n+warn;;\n+\ta comma-separated list of fsck message IDs that should be\n+\tdisplayed, but fsck should continue to error out.\n+ignore;;\n+\ta comma-separated list of fsck message IDs that should be\n+\tignored completely.\n +\n This feature is intended to support working with legacy repositories\n which would not pass pushing when `receive.fsckObjects = true`, allowing\n"},{"id":"254996","messageId":"40ed6b0223f660712004d3a040163f8d@www.dscho.org","threadId":"38144","inReplyTo":"xmqqppa8y0v4.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 04/18] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T17:42:37Z","receivedAt":"2015-01-21T17:42:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-01-21 09:49, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> +static inline int substrcmp(const char *string, int len, const char *match)\n>> +{\n>> +\tint match_len = strlen(match);\n>> +\tif (match_len != len)\n>> +\t\treturn -1;\n>> +\treturn memcmp(string, match, len);\n>> +}\n> \n> Is this what we call \"starts_with()\" these days?\n\nUnfortunately not quite: It really requires the substring specified by `string` and `len` to be identical to the full `match`. For example, `substrcmp(\"Hello world!\", 5, \"Hell\")` would report a failure (because the substring \"Hello\" is *not* matching \"Hell\"), while `starts_with(\"Hello world!\", \"Hell\")` would obviously succeed.\n\n>> +void fsck_set_severity(struct fsck_options *options, const char *mode)\n>> +{\n>> +\tint severity = FSCK_ERROR;\n>> +\n>> +\tif (!options->msg_severity) {\n>> +\t\tint i;\n>> +\t\tint *msg_severity = malloc(sizeof(int) * FSCK_MSG_MAX);\n> \n> xmalloc()?\n\nAbsolutely! Fixed.\n\nThanks,\nDscho\n"},{"id":"254998","messageId":"07b285ef702031fdebcf9def59528b9c@www.dscho.org","threadId":"38144","inReplyTo":"xmqqlhkwy0my.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 05/18] fsck: Allow demoting errors to warnings via receive.fsck.warn = <key>","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T18:01:47Z","receivedAt":"2015-01-21T18:01:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-01-21 09:54, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>>\n>> +\tif (starts_with(var, \"receive.fsck.\")) {\n>> +\t\tif (fsck_severity.len)\n>> +\t\t\tstrbuf_addch(&fsck_severity, ',');\n>> +\t\tstrbuf_addf(&fsck_severity, \"%s=%s\", var + 13, value);\n> \n> Wouldn't it be safer to use skip_prefix() that lets you avoid the\n> hardcoded \"var + 13\" here?\n\nYep, and much more elegant, too. I also fixed three more instances of the same pattern.\n \n>> @@ -1470,8 +1478,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n>>  \t\targv_array_pushl(&child.args, \"unpack-objects\", hdr_arg, NULL);\n>>  \t\tif (quiet)\n>>  \t\t\targv_array_push(&child.args, \"-q\");\n>> -\t\tif (fsck_objects)\n>> -\t\t\targv_array_push(&child.args, \"--strict\");\n>> +\t\tif (fsck_objects) {\n>> +\t\t\tif (fsck_severity.len)\n>> +\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n>> +\t\t\t\t\tfsck_severity.buf);\n>> +\t\t\telse\n>> +\t\t\t\targv_array_push(&child.args, \"--strict\");\n>> +\t\t}\n>>  \t\tchild.no_stdout = 1;\n>>  \t\tchild.err = err_fd;\n>>  \t\tchild.git_cmd = 1;\n>> @@ -1488,8 +1501,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n>>\n>>  \t\targv_array_pushl(&child.args, \"index-pack\",\n>>  \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n>> -\t\tif (fsck_objects)\n>> -\t\t\targv_array_push(&child.args, \"--strict\");\n>> +\t\tif (fsck_objects) {\n>> +\t\t\tif (fsck_severity.len)\n>> +\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n>> +\t\t\t\t\tfsck_severity.buf);\n>> +\t\t\telse\n>> +\t\t\t\targv_array_push(&child.args, \"--strict\");\n>> +\t\t}\n> \n> Hmm.  The above two hunks look suspiciously similar.  Would it be\n> worth to give them a single helper function?\n\nHmm. Not sure. I see what you mean, but for now I found\n\n+                       argv_array_pushf(&child.args, \"--strict%s%s\",\n+                               fsck_severity.len ? \"=\" : \"\",\n+                               fsck_severity.buf);\n\nto be more elegant than to add a fully-fledged new function. But if you feel strongly, I will gladly implement a separate function; I would appreciate suggestions as to the function name...\n\nCiao,\nDscho\n"},{"id":"254999","messageId":"8ddc1de7858a4bd79f5a945b8eb7b84c@www.dscho.org","threadId":"38144","inReplyTo":"xmqqd268y0f2.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 11/18] fsck: Add a simple test for receive.fsck.*","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T18:14:58Z","receivedAt":"2015-01-21T18:14:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-01-21 09:59, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>> ---\n>>  t/t5504-fetch-receive-strict.sh | 20 ++++++++++++++++++++\n>>  1 file changed, 20 insertions(+)\n>>\n>> diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\n>> index 69ee13c..d491172 100755\n>> --- a/t/t5504-fetch-receive-strict.sh\n>> +++ b/t/t5504-fetch-receive-strict.sh\n>> @@ -115,4 +115,24 @@ test_expect_success 'push with transfer.fsckobjects' '\n>>  \ttest_cmp exp act\n>>  '\n>>\n>> +cat >bogus-commit << EOF\n> \n> \"cat >bogus-commit <<\\EOF\", to reduce the mental burden of readers.\n\nCertainly!\nDscho\n"},{"id":"255000","messageId":"1c7e63ae18eb0a164081cc3601c3058e@www.dscho.org","threadId":"38144","inReplyTo":"xmqq8ugwy094.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 18/18] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T18:17:06Z","receivedAt":"2015-01-21T18:17:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-01-21 10:02, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> The optional new config option `receive.fsck.skiplist` specifies the path\n>> to a file listing the names, i.e. SHA-1s, one per line, of objects that\n>> are to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n> \n> Makes sense, but I wonder if it makes sense to have a similar \"ok to\n> be broken\" list for \"git fsck\" (or perhaps they could even use the\n> same list) for exactly the same reason why this option makes sense.\n\nSure! The most pressing use case for the skiplist feature is a Git server, hence this patch. I will implement the corresponding `git fsck` patch before re-submitting.\n\nCiao,\nDscho\n"},{"id":"255001","messageId":"e779d4655b61b33827026e2aa440536b@www.dscho.org","threadId":"38144","inReplyTo":"xmqq4mrkxzlb.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 00/18] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T18:24:57Z","receivedAt":"2015-01-21T18:24:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-01-21 10:17, Junio C Hamano wrote:\n> The documentation did not format well.  Tentatively I added the\n> attached fix-up on top of the series before merging to 'pu'.\n> \n>  [...]\n\nSorry for that! I have to admit that I did not even build the documentation :-( I incorporated your fixes into the respective patches.\n\nCiao,\nDscho\n"},{"id":"255007","messageId":"cover.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"xmqqr3w7gxr4.fsf@gitster.dls.corp.google.com","subject":"[PATCH v3 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:23:48Z","receivedAt":"2015-01-21T19:23:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At the moment, the git-fsck's integrity checks are targeted toward the\nend user, i.e. the error messages are really just messages, intended for\nhuman consumption.\n\nUnder certain circumstances, some of those errors should be allowed to\nbe turned into mere warnings, though, because the cost of fixing the\nissues might well be larger than the cost of carrying those flawed\nobjects. For example, when an already-public repository contains a\ncommit object with two authors for years, it does not make sense to\nforce the maintainer to rewrite the history, affecting all contributors\nnegatively by forcing them to update.\n\nThis branch introduces an internal fsck API to be able to turn some of\nthe errors into warnings, and to make it easier to call the fsck\nmachinery from elsewhere in general.\n\nI am proud to report that this work has been sponsored by GitHub.\n\nInterdiff vs v2 below the diffstat.\n\nJohannes Schindelin (19):\n  fsck: Introduce fsck options\n  fsck: Introduce identifiers for fsck messages\n  fsck: Provide a function to parse fsck message IDs\n  fsck: Offer a function to demote fsck errors to warnings\n  fsck: Allow demoting errors to warnings via receive.fsck.warn = <key>\n  fsck: Report the ID of the error/warning\n  fsck: Make fsck_ident() warn-friendly\n  fsck: Make fsck_commit() warn-friendly\n  fsck: Handle multiple authors in commits specially\n  fsck: Make fsck_tag() warn-friendly\n  fsck: Add a simple test for receive.fsck.*\n  fsck: Disallow demoting grave fsck errors to warnings\n  fsck: Optionally ignore specific fsck issues completely\n  fsck: Allow upgrading fsck warnings to errors\n  fsck: Document the new receive.fsck.* options.\n  fsck: Support demoting errors to warnings\n  fsck: Introduce `git fsck --quick`\n  fsck: git receive-pack: support excluding objects from fsck'ing\n  fsck: support ignoring objects in `git fsck` via fsck.skiplist\n\n Documentation/config.txt        |  57 +++++\n Documentation/git-fsck.txt      |   7 +-\n builtin/fsck.c                  |  76 ++++--\n builtin/index-pack.c            |  13 +-\n builtin/receive-pack.c          |  24 +-\n builtin/unpack-objects.c        |  16 +-\n fsck.c                          | 525 +++++++++++++++++++++++++++++++---------\n fsck.h                          |  27 ++-\n t/t1450-fsck.sh                 |  37 ++-\n t/t5302-pack-index.sh           |   2 +-\n t/t5504-fetch-receive-strict.sh |  46 ++++\n 11 files changed, 668 insertions(+), 162 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 0daba8a..644411a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1208,7 +1208,9 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n-fsck.*::\n+fsck.error::\n+fsck.warn::\n+fsck.ignore::\n \tThe `fsck.error`, `fsck.warn` and `fsck.ignore` settings specify\n \tcomma-separated lists of fsck message IDs which should trigger\n \tfsck to error out, to print the message and continue, or to ignore\n@@ -1221,6 +1223,13 @@ that setting `fsck.ignore = missing-email` will hide that issue.\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n \n+fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\n@@ -2143,31 +2152,41 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n-receive.fsck.*::\n+receive.fsck.error::\n+receive.fsck.warn::\n+receive.fsck.ignore::\n \tWhen `receive.fsckObjects` is set to true, errors can be switched\n \tto warnings and vice versa by configuring the `receive.fsck.*`\n \tsettings. These settings contain comma-separated lists of fsck\n \tmessage IDs. For convenience, fsck prefixes the error/warning with\n-\tthe message ID, e.g. \"missing-email: invalid author/committer line\n-\t- missing email\" means that setting `receive.fsck.ignore =\n-\tmissing-email` will hide that issue.\n+\tthe message ID, e.g. \"missing-email: invalid\n+\tauthor/committer line - missing email\" means that setting\n+\t`receive.fsck.ignore = missing-email` will hide that issue.\n +\n --\n-\terror::\n-\t\ta comma-separated list of fsck message IDs that should be\n-\t\ttrigger fsck to error out.\n-\twarn::\n-\t\ta comma-separated list of fsck message IDs that should be\n-\t\tdisplayed, but fsck should continue to error out.\n-\tignore::\n-\t\ta comma-separated list of fsck message IDs that should be\n-\t\tignored completely.\n+error;;\n+\ta comma-separated list of fsck message IDs that should be\n+\ttrigger fsck to error out.\n+warn;;\n+\ta comma-separated list of fsck message IDs that should be\n+\tdisplayed, but fsck should continue to error out.\n+ignore;;\n+\ta comma-separated list of fsck message IDs that should be\n+\tignored completely.\n+--\n +\n This feature is intended to support working with legacy repositories\n which would not pass pushing when `receive.fsckObjects = true`, allowing\n-the host to accept repositories certain known issues but still catch\n+the host to accept repositories with certain known issues but still catch\n other issues.\n \n+receive.fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex c767909..760b4bd 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -49,9 +49,19 @@ static int show_dangling = 1;\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n-\tif (starts_with(var, \"fsck.\")) {\n+\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n \t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tstrbuf_addf(&sb, \"%s=%s\", var + 5, value);\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"%s=%s\", var, value);\n \t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n \t\tstrbuf_release(&sb);\n \t\treturn 0;\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex f464ca0..b82b4dd 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1565,10 +1565,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(arg, \"--strict\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n-\t\t\t} else if (starts_with(arg, \"--strict=\")) {\n+\t\t\t} else if (skip_prefix(arg, \"--strict=\", &arg)) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n-\t\t\t\tfsck_set_severity(&fsck_options, arg + 9);\n+\t\t\t\tfsck_set_severity(&fsck_options, arg);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 40514c2..8e6d1a1 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -116,7 +116,7 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n-\tif (starts_with(var, \"receive.fsck.skiplist\")) {\n+\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n \t\tconst char *path = is_absolute_path(value) ?\n \t\t\tvalue : git_path(\"%s\", value);\n \t\tif (fsck_severity.len)\n@@ -125,10 +125,9 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n-\tif (starts_with(var, \"receive.fsck.\")) {\n-\t\tif (fsck_severity.len)\n-\t\t\tstrbuf_addch(&fsck_severity, ',');\n-\t\tstrbuf_addf(&fsck_severity, \"%s=%s\", var + 13, value);\n+\tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n+\t\tstrbuf_addf(&fsck_severity, \"%s%s=%s\",\n+\t\t\tfsck_severity.len ? \",\" : \"\", var, value);\n \t\treturn 0;\n \t}\n \n@@ -1487,13 +1486,10 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"unpack-objects\", hdr_arg, NULL);\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n-\t\tif (fsck_objects) {\n-\t\t\tif (fsck_severity.len)\n-\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n-\t\t\t\t\tfsck_severity.buf);\n-\t\t\telse\n-\t\t\t\targv_array_push(&child.args, \"--strict\");\n-\t\t}\n+\t\tif (fsck_objects)\n+\t\t\targv_array_pushf(&child.args, \"--strict%s%s\",\n+\t\t\t\tfsck_severity.len ? \"=\" : \"\",\n+\t\t\t\tfsck_severity.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1510,13 +1506,10 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n-\t\tif (fsck_objects) {\n-\t\t\tif (fsck_severity.len)\n-\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n-\t\t\t\t\tfsck_severity.buf);\n-\t\t\telse\n-\t\t\t\targv_array_push(&child.args, \"--strict\");\n-\t\t}\n+\t\tif (fsck_objects)\n+\t\t\targv_array_pushf(&child.args, \"--strict%s%s\",\n+\t\t\t\tfsck_severity.len ? \"=\" : \"\",\n+\t\t\t\tfsck_severity.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 82f2d62..fe9117c 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -530,9 +530,9 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrict = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tif (starts_with(arg, \"--strict=\")) {\n+\t\t\tif (skip_prefix(arg, \"--strict=\", &arg)) {\n \t\t\t\tstrict = 1;\n-\t\t\t\tfsck_set_severity(&fsck_options, arg + 9);\n+\t\t\t\tfsck_set_severity(&fsck_options, arg);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\ndiff --git a/fsck.c b/fsck.c\nindex dbf9fa1..15cb8bd 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -169,7 +169,7 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \n \tif (!options->msg_severity) {\n \t\tint i;\n-\t\tint *msg_severity = malloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tint *msg_severity = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n \t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n \t\t\tmsg_severity[i] = fsck_msg_severity(i, options);\n \t\toptions->msg_severity = msg_severity;\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 21fa9c8..d367bb2 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -115,7 +115,7 @@ test_expect_success 'push with transfer.fsckobjects' '\n \ttest_cmp exp act\n '\n \n-cat >bogus-commit << EOF\n+cat >bogus-commit <<\\EOF\n tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n author Bugs Bunny 1234567890 +0000\n committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n-- \n2.2.0.33.gc18b867\n"},{"id":"255008","messageId":"2ba8ef18e22fb7d9a7e7b513422dc1f3a2716061.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 01/19] fsck: Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:24:06Z","receivedAt":"2015-01-21T19:24:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Just like the diff machinery, we are about to introduce more settings,\ntherefore it makes sense to carry them around as a (pointer to a) struct\ncontaining all of them.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c           |  20 +++++--\n builtin/index-pack.c     |   9 +--\n builtin/unpack-objects.c |  11 ++--\n fsck.c                   | 150 +++++++++++++++++++++++------------------------\n fsck.h                   |  17 +++++-\n 5 files changed, 114 insertions(+), 93 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex a27515a..2241e29 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -25,6 +25,8 @@ static int include_reflogs = 1;\n static int check_full = 1;\n static int check_strict;\n static int keep_cache_objects;\n+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n static unsigned char head_sha1[20];\n static const char *head_points_at;\n static int errors_found;\n@@ -76,7 +78,7 @@ static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n \n static struct object_array pending;\n \n-static int mark_object(struct object *obj, int type, void *data)\n+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct object *parent = data;\n \n@@ -119,7 +121,7 @@ static int mark_object(struct object *obj, int type, void *data)\n \n static void mark_object_reachable(struct object *obj)\n {\n-\tmark_object(obj, OBJ_ANY, NULL);\n+\tmark_object(obj, OBJ_ANY, NULL, NULL);\n }\n \n static int traverse_one_object(struct object *obj)\n@@ -132,7 +134,7 @@ static int traverse_one_object(struct object *obj)\n \t\tif (parse_tree(tree) < 0)\n \t\t\treturn 1; /* error already displayed */\n \t}\n-\tresult = fsck_walk(obj, mark_object, obj);\n+\tresult = fsck_walk(obj, obj, &fsck_walk_options);\n \tif (tree)\n \t\tfree_tree_buffer(tree);\n \treturn result;\n@@ -158,7 +160,7 @@ static int traverse_reachable(void)\n \treturn !!result;\n }\n \n-static int mark_used(struct object *obj, int type, void *data)\n+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn 1;\n@@ -296,9 +298,9 @@ static int fsck_obj(struct object *obj)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n \t\t\ttypename(obj->type), sha1_to_hex(obj->sha1));\n \n-\tif (fsck_walk(obj, mark_used, NULL))\n+\tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n-\tif (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))\n+\tif (fsck_object(obj, NULL, 0, &fsck_obj_options))\n \t\treturn -1;\n \n \tif (obj->type == OBJ_TREE) {\n@@ -630,6 +632,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);\n \n+\tfsck_walk_options.walk = mark_object;\n+\tfsck_obj_options.walk = mark_used;\n+\tfsck_obj_options.error_func = fsck_error_func;\n+\tif (check_strict)\n+\t\tfsck_obj_options.strict = 1;\n+\n \tif (show_progress == -1)\n \t\tshow_progress = isatty(2);\n \tif (verbose)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 4632117..925f7b5 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -74,6 +74,7 @@ static int nr_threads;\n static int from_stdin;\n static int strict;\n static int do_fsck_object;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n static int verbose;\n static int show_stat;\n static int check_self_contained_and_connected;\n@@ -191,7 +192,7 @@ static void cleanup_thread(void)\n #endif\n \n \n-static int mark_link(struct object *obj, int type, void *data)\n+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -782,10 +783,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (!obj)\n \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(obj, buf, size, 1,\n-\t\t\t\t    fsck_error_function))\n+\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"Error in object\"));\n-\t\t\tif (fsck_walk(obj, mark_link, NULL))\n+\t\t\tfsck_options.walk = mark_link;\n+\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n \n \t\t\tif (obj->type == OBJ_TREE) {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex ac66672..6d17040 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -20,6 +20,7 @@ static unsigned char buffer[4096];\n static unsigned int offset, len;\n static off_t consumed_bytes;\n static git_SHA_CTX ctx;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n /*\n  * When running under --strict mode, objects whose reachability are\n@@ -178,7 +179,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)\n  * that have reachability requirements and calls this function.\n  * Verify its reachability and validity recursively and write it out.\n  */\n-static int check_object(struct object *obj, int type, void *data)\n+static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct obj_buffer *obj_buf;\n \n@@ -203,10 +204,10 @@ static int check_object(struct object *obj, int type, void *data)\n \tobj_buf = lookup_object_buffer(obj);\n \tif (!obj_buf)\n \t\tdie(\"Whoops! Cannot find object '%s'\", sha1_to_hex(obj->sha1));\n-\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1,\n-\t\t\tfsck_error_function))\n+\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"Error in object\");\n-\tif (fsck_walk(obj, check_object, NULL))\n+\tfsck_options.walk = check_object;\n+\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\tdie(\"Error on reachable objects of %s\", sha1_to_hex(obj->sha1));\n \twrite_cached_object(obj, obj_buf);\n \treturn 0;\n@@ -217,7 +218,7 @@ static void write_rest(void)\n \tunsigned i;\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tif (obj_list[i].obj)\n-\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL);\n+\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);\n \t}\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex 10bcb65..d83b811 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,7 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n-static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\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@@ -25,9 +25,9 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n \t\tif (S_ISDIR(entry.mode))\n-\t\t\tresult = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);\n+\t\t\tresult = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);\n \t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n-\t\t\tresult = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);\n+\t\t\tresult = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n \t\t\t\t\tsha1_to_hex(tree->object.sha1), entry.path, entry.mode);\n@@ -40,7 +40,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \treturn res;\n }\n \n-static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)\n+static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n \tstruct commit_list *parents;\n \tint res;\n@@ -49,14 +49,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \tif (parse_commit(commit))\n \t\treturn -1;\n \n-\tresult = walk((struct object *)commit->tree, OBJ_TREE, data);\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 \twhile (parents) {\n-\t\tresult = walk((struct object *)parents->item, OBJ_COMMIT, data);\n+\t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n@@ -66,14 +66,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \treturn res;\n }\n \n-static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)\n+static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n \tif (parse_tag(tag))\n \t\treturn -1;\n-\treturn walk(tag->tagged, OBJ_ANY, data);\n+\treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -81,11 +81,11 @@ int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n \tcase OBJ_BLOB:\n \t\treturn 0;\n \tcase OBJ_TREE:\n-\t\treturn fsck_walk_tree((struct tree *)obj, walk, data);\n+\t\treturn fsck_walk_tree((struct tree *)obj, data, options);\n \tcase OBJ_COMMIT:\n-\t\treturn fsck_walk_commit((struct commit *)obj, walk, data);\n+\t\treturn fsck_walk_commit((struct commit *)obj, data, options);\n \tcase OBJ_TAG:\n-\t\treturn fsck_walk_tag((struct tag *)obj, walk, data);\n+\t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n \tdefault:\n \t\terror(\"Unknown object type for %s\", sha1_to_hex(obj->sha1));\n \t\treturn -1;\n@@ -138,7 +138,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con\n \treturn c1 < c2 ? 0 : TREE_UNORDERED;\n }\n \n-static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n+static int fsck_tree(struct tree *item, struct fsck_options *options)\n {\n \tint retval;\n \tint has_null_sha1 = 0;\n@@ -194,7 +194,7 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \t\t * bits..\n \t\t */\n \t\tcase S_IFREG | 0664:\n-\t\t\tif (!strict)\n+\t\t\tif (!options->strict)\n \t\t\t\tbreak;\n \t\tdefault:\n \t\t\thas_bad_modes = 1;\n@@ -219,30 +219,30 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n \treturn retval;\n }\n \n static int require_end_of_header(const void *data, unsigned long size,\n-\tstruct object *obj, fsck_error error_func)\n+\tstruct object *obj, struct fsck_options *options)\n {\n \tconst char *buffer = (const char *)data;\n \tunsigned long i;\n@@ -250,7 +250,7 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn error_func(obj, FSCK_ERROR,\n+\t\t\treturn options->error_func(obj, FSCK_ERROR,\n \t\t\t\t\"unterminated header: NUL at offset %d\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n@@ -258,36 +258,36 @@ static int require_end_of_header(const void *data, unsigned long size,\n \t\t}\n \t}\n \n-\treturn error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n }\n \n-static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)\n+static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,30 +295,30 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n \n static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n \tunsigned parent_count, parent_line_count = 0;\n \tint err;\n \n-\tif (require_end_of_header(buffer, size, &commit->object, error_func))\n+\tif (require_end_of_header(buffer, size, &commit->object, options))\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,39 +328,39 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n \n static int fsck_commit(struct commit *commit, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tconst char *buffer = data ?  data : get_commit_buffer(commit, &size);\n-\tint ret = fsck_commit_buffer(commit, buffer, size, error_func);\n+\tint ret = fsck_commit_buffer(commit, buffer, size, options);\n \tif (!data)\n \t\tunuse_commit_buffer(commit, buffer);\n \treturn ret;\n }\n \n static int fsck_tag_buffer(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char sha1[20];\n \tint ret = 0;\n@@ -376,65 +376,65 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n \t\t}\n \t}\n \n-\tif (require_end_of_header(buffer, size, &tag->object, error_func))\n+\tif (require_end_of_header(buffer, size, &tag->object, options))\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n \telse\n-\t\tret = fsck_ident(&buffer, &tag->object, error_func);\n+\t\tret = fsck_ident(&buffer, &tag->object, options);\n \n done:\n \tstrbuf_release(&sb);\n@@ -443,34 +443,34 @@ done:\n }\n \n static int fsck_tag(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n \n-\treturn fsck_tag_buffer(tag, data, size, error_func);\n+\treturn fsck_tag_buffer(tag, data, size, options);\n }\n \n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func)\n+\tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n \tif (obj->type == OBJ_TREE)\n-\t\treturn fsck_tree((struct tree *) obj, strict, error_func);\n+\t\treturn fsck_tree((struct tree *) obj, options);\n \tif (obj->type == OBJ_COMMIT)\n \t\treturn fsck_commit((struct commit *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \tif (obj->type == OBJ_TAG)\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \n-\treturn error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex d1e6387..07d0ab2 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -4,6 +4,8 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n \n+struct fsck_options;\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -12,7 +14,7 @@\n  *     <0\terror signaled and abort\n  *     >0\terror signaled and do not abort\n  */\n-typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);\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 *err, ...);\n@@ -20,6 +22,15 @@ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);\n __attribute__((format (printf, 3, 4)))\n int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n \n+struct fsck_options {\n+\tfsck_walk_func walk;\n+\tfsck_error error_func;\n+\tunsigned strict:1;\n+};\n+\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+\n /* descend in all linked child objects\n  * the return value is:\n  *    -1\terror in processing the object\n@@ -27,9 +38,9 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n  *    >0\treturn value of the first signaled error >0 (in the case of no other errors)\n  *    0\t\teverything OK\n  */\n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n /* If NULL is passed for data, we assume the object is local and read it. */\n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func);\n+\tstruct fsck_options *options);\n \n #endif\n-- \n2.2.0.33.gc18b867\n"},{"id":"255009","messageId":"bfe97b0b957b9d2722a6d13522f0a7f4477d0d14.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 02/19] fsck: Introduce identifiers for fsck messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:24:16Z","receivedAt":"2015-01-21T19:24:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Instead of specifying whether a message by the fsck machinery constitutes\nan error or a warning, let's specify an identifier relating to the\nconcrete problem that was encountered. This is necessary for upcoming\nsupport to be able to demote certain errors to warnings.\n\nIn the process, simplify the requirements on the calling code: instead of\nhaving to handle full-blown varargs in every callback, we now send a\nstring buffer ready to be used by the callback.\n\nWe could use a simple enum for the message IDs here, but we want to\nguarantee that the enum values are associated with the appropriate\nseverity levels. Besides, we want to introduce a parser in the next commit\nthat maps the string representation to the enum value, hence we use the\nslightly ugly preprocessor construct that is extensible for use with said\nparser.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c |  24 ++-----\n fsck.c         | 201 +++++++++++++++++++++++++++++++++++++++++----------------\n fsck.h         |   5 +-\n 3 files changed, 153 insertions(+), 77 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2241e29..99d4538 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -47,32 +47,22 @@ static int show_dangling = 1;\n #endif\n \n static void objreport(struct object *obj, const char *severity,\n-                      const char *err, va_list params)\n+                      const char *err)\n {\n-\tfprintf(stderr, \"%s in %s %s: \",\n-\t        severity, typename(obj->type), sha1_to_hex(obj->sha1));\n-\tvfprintf(stderr, err, params);\n-\tfputs(\"\\n\", stderr);\n+\tfprintf(stderr, \"%s in %s %s: %s\\n\",\n+\t        severity, typename(obj->type), sha1_to_hex(obj->sha1), err);\n }\n \n-__attribute__((format (printf, 2, 3)))\n-static int objerror(struct object *obj, const char *err, ...)\n+static int objerror(struct object *obj, const char *err)\n {\n-\tva_list params;\n-\tva_start(params, err);\n \terrors_found |= ERROR_OBJECT;\n-\tobjreport(obj, \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, \"error\", err);\n \treturn -1;\n }\n \n-__attribute__((format (printf, 3, 4)))\n-static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n+static int fsck_error_func(struct object *obj, int type, const char *message)\n {\n-\tva_list params;\n-\tva_start(params, err);\n-\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n \treturn (type == FSCK_WARN) ? 0 : 1;\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex d83b811..30f7a48 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,98 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FOREACH_MSG_ID(FUNC) \\\n+\t/* errors */ \\\n+\tFUNC(BAD_DATE, ERROR) \\\n+\tFUNC(BAD_EMAIL, ERROR) \\\n+\tFUNC(BAD_NAME, ERROR) \\\n+\tFUNC(BAD_PARENT_SHA1, ERROR) \\\n+\tFUNC(BAD_TIMEZONE, ERROR) \\\n+\tFUNC(BAD_TREE_SHA1, ERROR) \\\n+\tFUNC(DATE_OVERFLOW, ERROR) \\\n+\tFUNC(DUPLICATE_ENTRIES, ERROR) \\\n+\tFUNC(INVALID_OBJECT_SHA1, ERROR) \\\n+\tFUNC(INVALID_TAG_OBJECT, ERROR) \\\n+\tFUNC(INVALID_TREE, ERROR) \\\n+\tFUNC(INVALID_TYPE, ERROR) \\\n+\tFUNC(MISSING_AUTHOR, ERROR) \\\n+\tFUNC(MISSING_COMMITTER, ERROR) \\\n+\tFUNC(MISSING_EMAIL, ERROR) \\\n+\tFUNC(MISSING_GRAFT, ERROR) \\\n+\tFUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_OBJECT, ERROR) \\\n+\tFUNC(MISSING_PARENT, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_TAG, ERROR) \\\n+\tFUNC(MISSING_TAG_ENTRY, ERROR) \\\n+\tFUNC(MISSING_TAG_OBJECT, ERROR) \\\n+\tFUNC(MISSING_TREE, ERROR) \\\n+\tFUNC(MISSING_TYPE, ERROR) \\\n+\tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(NOT_SORTED, ERROR) \\\n+\tFUNC(NUL_IN_HEADER, ERROR) \\\n+\tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n+\tFUNC(UNKNOWN_TYPE, ERROR) \\\n+\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n+\tFUNC(ZERO_PADDED_DATE, ERROR) \\\n+\t/* warnings */ \\\n+\tFUNC(BAD_FILEMODE, WARN) \\\n+\tFUNC(EMPTY_NAME, WARN) \\\n+\tFUNC(FULL_PATHNAME, WARN) \\\n+\tFUNC(HAS_DOT, WARN) \\\n+\tFUNC(HAS_DOTDOT, WARN) \\\n+\tFUNC(HAS_DOTGIT, WARN) \\\n+\tFUNC(INVALID_TAG_NAME, WARN) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n+\tFUNC(NULL_SHA1, WARN) \\\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\n+#define MSG_ID(id, severity) FSCK_MSG_##id,\n+enum fsck_msg_id {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\tFSCK_MSG_MAX\n+};\n+#undef MSG_ID\n+\n+#define MSG_ID(id, severity) { FSCK_##severity },\n+static struct {\n+\tint severity;\n+} msg_id_info[FSCK_MSG_MAX + 1] = {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\t{ -1 }\n+};\n+#undef MSG_ID\n+\n+static int fsck_msg_severity(enum fsck_msg_id msg_id,\n+\tstruct fsck_options *options)\n+{\n+\tint severity;\n+\n+\tseverity = msg_id_info[msg_id].severity;\n+\tif (options->strict && severity == FSCK_WARN)\n+\t\tseverity = FSCK_ERROR;\n+\n+\treturn severity;\n+}\n+\n+__attribute__((format (printf, 4, 5)))\n+static int report(struct fsck_options *options, struct object *object,\n+\tenum fsck_msg_id id, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint msg_severity = fsck_msg_severity(id, options), result;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tresult = options->error_func(object, msg_severity, sb.buf);\n+\tstrbuf_release(&sb);\n+\tva_end(ap);\n+\n+\treturn result;\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n@@ -219,25 +311,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NULL_SHA1, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOT, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NOT_SORTED, \"not properly sorted\");\n \treturn retval;\n }\n \n@@ -250,15 +342,17 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn options->error_func(obj, FSCK_ERROR,\n-\t\t\t\t\"unterminated header: NUL at offset %d\", i);\n+\t\t\treturn report(options, obj,\n+\t\t\t\tFSCK_MSG_NUL_IN_HEADER,\n+\t\t\t\t\"unterminated header: NUL at offset %ld\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n \t\t\t\treturn 0;\n \t\t}\n \t}\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn report(options, obj,\n+\t\tFSCK_MSG_UNTERMINATED_HEADER, \"unterminated header\");\n }\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n@@ -266,28 +360,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,7 +389,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n@@ -312,13 +406,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,23 +422,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn report(options, &commit->object, FSCK_MSG_INVALID_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n@@ -376,11 +470,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_MISSING_TAG_OBJECT,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_TAG_OBJECT_NOT_TAG,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n@@ -391,48 +487,49 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \n@@ -448,7 +545,7 @@ static int fsck_tag(struct tag *tag, const char *data,\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn report(options, &tag->object, FSCK_MSG_INVALID_TAG_OBJECT, \"could not load tagged object\");\n \n \treturn fsck_tag_buffer(tag, data, size, options);\n }\n@@ -457,7 +554,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n@@ -470,22 +567,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n \t\t\tsize, options);\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn report(options, obj, FSCK_MSG_UNKNOWN_TYPE, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...)\n+int fsck_error_function(struct object *obj, int severity, const char *message)\n {\n-\tva_list ap;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tstrbuf_addf(&sb, \"object %s:\", sha1_to_hex(obj->sha1));\n-\n-\tva_start(ap, fmt);\n-\tstrbuf_vaddf(&sb, fmt, ap);\n-\tva_end(ap);\n-\n-\terror(\"%s\", sb.buf);\n-\tstrbuf_release(&sb);\n+\terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex 07d0ab2..f6f268a 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -17,10 +17,9 @@ struct fsck_options;\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 *err, ...);\n+typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n \n-__attribute__((format (printf, 3, 4)))\n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n+int fsck_error_function(struct object *obj, int type, const char *message);\n \n struct fsck_options {\n \tfsck_walk_func walk;\n-- \n2.2.0.33.gc18b867\n"},{"id":"255010","messageId":"bcc40c9da51cc3967923a057fbcc3def4e48b1e1.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 03/19] fsck: Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:24:27Z","receivedAt":"2015-01-21T19:24:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This function will be used in the next commits to allow the user to\nask fsck to handle specific problems differently, e.g. demoting certain\nerrors to warnings. It has to handle partial strings because we would\nlike to be able to parse, say, 'missing-email,missing-tagger-entry'\ncommand lines.\n\nTo make the parsing robust, we generate strings from the enum keys, and\nusing these keys, we will map lower-case, dash-separated strings values\nto the corresponding enum values.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 27 +++++++++++++++++++++++++--\n 1 file changed, 25 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 30f7a48..2d91e28 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -63,15 +63,38 @@ enum fsck_msg_id {\n };\n #undef MSG_ID\n \n-#define MSG_ID(id, severity) { FSCK_##severity },\n+#define STR(x) #x\n+#define MSG_ID(id, severity) { STR(id), FSCK_##severity },\n static struct {\n+\tconst char *id_string;\n \tint severity;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n-\t{ -1 }\n+\t{ NULL, -1 }\n };\n #undef MSG_ID\n \n+static int parse_msg_id(const char *text, int len)\n+{\n+\tint i, j;\n+\n+\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n+\t\tconst char *key = msg_id_info[i].id_string;\n+\t\t/* id_string is upper-case, with underscores */\n+\t\tfor (j = 0; j < len; j++) {\n+\t\t\tchar c = *(key++);\n+\t\t\tif (c == '_')\n+\t\t\t\tc = '-';\n+\t\t\tif (text[j] != tolower(c))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (j == len && !*key)\n+\t\t\treturn i;\n+\t}\n+\n+\tdie(\"Unhandled message id: %.*s\", len, text);\n+}\n+\n static int fsck_msg_severity(enum fsck_msg_id msg_id,\n \tstruct fsck_options *options)\n {\n-- \n2.2.0.33.gc18b867\n"},{"id":"255011","messageId":"901cfd52214133a62e216b5d09f4671f63536827.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:24:37Z","receivedAt":"2015-01-21T19:24:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are legacy repositories out there whose older commits and tags\nhave issues that prevent pushing them when 'receive.fsckObjects' is set.\nOne real-life example is a commit object that has been hand-crafted to\nlist two authors.\n\nOften, it is not possible to fix those issues without disrupting the\nwork with said repositories, yet it is still desirable to perform checks\nby setting `receive.fsckObjects = true`. This commit is the first step\nto allow demoting specific fsck issues to mere warnings.\n\nThe function added by this commit parses a list of settings in the form:\n\n\tmissing-email=warn,bad-name=warn,...\n\nUnfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\ngit fsck so far, but other call paths (e.g. git index-pack --strict)\nerror out *always* no matter what type was specified. Therefore, we\nneed to take extra care to default to all FSCK_ERROR in those cases.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n fsck.h |  7 +++++--\n 2 files changed, 66 insertions(+), 5 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 2d91e28..02715ee 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -100,13 +100,67 @@ static int fsck_msg_severity(enum fsck_msg_id msg_id,\n {\n \tint severity;\n \n-\tseverity = msg_id_info[msg_id].severity;\n-\tif (options->strict && severity == FSCK_WARN)\n-\t\tseverity = FSCK_ERROR;\n+\tif (options->msg_severity && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n+\t\tseverity = options->msg_severity[msg_id];\n+\telse {\n+\t\tseverity = msg_id_info[msg_id].severity;\n+\t\tif (options->strict && severity == FSCK_WARN)\n+\t\t\tseverity = FSCK_ERROR;\n+\t}\n \n \treturn severity;\n }\n \n+static inline int substrcmp(const char *string, int len, const char *match)\n+{\n+\tint match_len = strlen(match);\n+\tif (match_len != len)\n+\t\treturn -1;\n+\treturn memcmp(string, match, len);\n+}\n+\n+void fsck_set_severity(struct fsck_options *options, const char *mode)\n+{\n+\tint severity = FSCK_ERROR;\n+\n+\tif (!options->msg_severity) {\n+\t\tint i;\n+\t\tint *msg_severity = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\t\tmsg_severity[i] = fsck_msg_severity(i, options);\n+\t\toptions->msg_severity = msg_severity;\n+\t}\n+\n+\twhile (*mode) {\n+\t\tint len = strcspn(mode, \" ,|\"), equal, msg_id;\n+\n+\t\tif (!len) {\n+\t\t\tmode++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (equal = 0; equal < len; equal++)\n+\t\t\tif (mode[equal] == '=')\n+\t\t\t\tbreak;\n+\n+\t\tif (equal < len) {\n+\t\t\tif (!substrcmp(mode, equal, \"error\"))\n+\t\t\t\tseverity = FSCK_ERROR;\n+\t\t\telse if (!substrcmp(mode, equal, \"warn\"))\n+\t\t\t\tseverity = FSCK_WARN;\n+\t\t\telse\n+\t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n+\t\t\t\t\tequal, mode);\n+\t\t\tmode += equal + 1;\n+\t\t\tlen -= equal + 1;\n+\t\t}\n+\n+\t\tmsg_id = parse_msg_id(mode, len);\n+\t\toptions->msg_severity[msg_id] = severity;\n+\t\tmode += len;\n+\t}\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -596,6 +650,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \n int fsck_error_function(struct object *obj, int severity, const char *message)\n {\n+\tif (severity == FSCK_WARN) {\n+\t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n+\t\treturn 0;\n+\t}\n \terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex f6f268a..4349860 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,8 @@\n \n struct fsck_options;\n \n+void fsck_set_severity(struct fsck_options *options, const char *mode);\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -25,10 +27,11 @@ struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n \tunsigned strict:1;\n+\tint *msg_severity;\n };\n \n-#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n-#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }\n \n /* descend in all linked child objects\n  * the return value is:\n-- \n2.2.0.33.gc18b867\n"},{"id":"255012","messageId":"858d4121150caa63276c392252bd4a476f89adf3.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 05/19] fsck: Allow demoting errors to warnings via receive.fsck.warn = <key>","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:24:49Z","receivedAt":"2015-01-21T19:24:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For example, missing emails in commit and tag objects can be demoted to\nmere warnings with\n\n\tgit config receive.fsck.warn = missing-email\n\nThe value is actually a comma-separated list, and there is a\ncorresponding receive.fsck.error setting.\n\nIn case that the same key is listed in multiple receive.fsck.* lines in\nthe config, the latter configuration wins.\n\nAs git receive-pack does not actually perform the checks, it hands off\nthe setting to index-pack or unpack-objects in the form of an optional\nargument to the --strict option.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/index-pack.c     |  4 ++++\n builtin/receive-pack.c   | 15 +++++++++++++--\n builtin/unpack-objects.c |  5 +++++\n 3 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 925f7b5..b82b4dd 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1565,6 +1565,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(arg, \"--strict\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n+\t\t\t} else if (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tdo_fsck_object = 1;\n+\t\t\t\tfsck_set_severity(&fsck_options, arg);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex e0ce78e..18d5012 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -36,6 +36,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n+static struct strbuf fsck_severity = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int advertise_atomic_push = 1;\n@@ -115,6 +116,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n+\t\tstrbuf_addf(&fsck_severity, \"%s%s=%s\",\n+\t\t\tfsck_severity.len ? \",\" : \"\", var, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsckobjects\") == 0) {\n \t\treceive_fsck_objects = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1471,7 +1478,9 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s%s\",\n+\t\t\t\tfsck_severity.len ? \"=\" : \"\",\n+\t\t\t\tfsck_severity.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1489,7 +1498,9 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s%s\",\n+\t\t\t\tfsck_severity.len ? \"=\" : \"\",\n+\t\t\t\tfsck_severity.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 6d17040..fe9117c 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrict = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tfsck_set_severity(&fsck_options, arg);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\n \t\t\t\tstruct pack_header *hdr;\n \t\t\t\tchar *c;\n-- \n2.2.0.33.gc18b867\n"},{"id":"255013","messageId":"bd6333a6a6e024878a7de6a2b8f0a9c4548a0ac4.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 06/19] fsck: Report the ID of the error/warning","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:25:00Z","receivedAt":"2015-01-21T19:25:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some legacy code has objects with non-fatal fsck issues; To enable the\nuser to ignore those issues, let's print out the ID (e.g. when\nencountering \"missing-email\", the user might want to call `git config\nreceive.fsck.warn missing-email`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c          | 19 +++++++++++++++++++\n t/t1450-fsck.sh |  4 ++--\n 2 files changed, 21 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 02715ee..09f69fe 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -161,6 +161,23 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t}\n }\n \n+static void append_msg_id(struct strbuf *sb, const char *msg_id)\n+{\n+\tfor (;;) {\n+\t\tchar c = *(msg_id)++;\n+\n+\t\tif (!c)\n+\t\t\tbreak;\n+\t\tif (c == '_')\n+\t\t\tc = '-';\n+\t\telse\n+\t\t\tc = tolower(c);\n+\t\tstrbuf_addch(sb, c);\n+\t}\n+\n+\tstrbuf_addstr(sb, \": \");\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -169,6 +186,8 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tappend_msg_id(&sb, msg_id_info[id].id_string);\n+\n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n \tresult = options->error_func(object, msg_severity, sb.buf);\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex cfb32b6..ea0f216 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: invalid-tag-name: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missing-tagger-entry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n-- \n2.2.0.33.gc18b867\n"},{"id":"255014","messageId":"68cb5436187978ad41df09bfae325893ff0ff84d.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 07/19] fsck: Make fsck_ident() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:25:09Z","receivedAt":"2015-01-21T19:25:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_ident() identifies a problem with the ident, it should still\nadvance the pointer to the next line so that fsck can continue in the\ncase of a mere warning.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 49 +++++++++++++++++++++++++++----------------------\n 1 file changed, 27 insertions(+), 22 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 09f69fe..16500e3 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -453,40 +453,45 @@ static int require_end_of_header(const void *data, unsigned long size,\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n+\tconst char *p = *ident;\n \tchar *end;\n \n-\tif (**ident == '<')\n+\t*ident = strchrnul(*ident, '\\n');\n+\tif (**ident == '\\n')\n+\t\t(*ident)++;\n+\n+\tif (*p == '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident == '>')\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p == '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n-\tif (**ident != '<')\n+\tif (*p != '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n-\tif ((*ident)[-1] != ' ')\n+\tif (p[-1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t(*ident)++;\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident != '>')\n+\tp++;\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p != '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n-\t(*ident)++;\n-\tif (**ident != ' ')\n+\tp++;\n+\tif (*p != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n-\t(*ident)++;\n-\tif (**ident == '0' && (*ident)[1] != ' ')\n+\tp++;\n+\tif (*p == '0' && p[1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n-\tif (date_overflows(strtoul(*ident, &end, 10)))\n+\tif (date_overflows(strtoul(p, &end, 10)))\n \t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n-\tif (end == *ident || *end != ' ')\n+\tif ((end == p || *end != ' '))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n-\t*ident = end + 1;\n-\tif ((**ident != '+' && **ident != '-') ||\n-\t    !isdigit((*ident)[1]) ||\n-\t    !isdigit((*ident)[2]) ||\n-\t    !isdigit((*ident)[3]) ||\n-\t    !isdigit((*ident)[4]) ||\n-\t    ((*ident)[5] != '\\n'))\n+\tp = end + 1;\n+\tif ((*p != '+' && *p != '-') ||\n+\t    !isdigit(p[1]) ||\n+\t    !isdigit(p[2]) ||\n+\t    !isdigit(p[3]) ||\n+\t    !isdigit(p[4]) ||\n+\t    (p[5] != '\\n'))\n \t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n-\t(*ident) += 6;\n+\tp += 6;\n \treturn 0;\n }\n \n-- \n2.2.0.33.gc18b867\n"},{"id":"255015","messageId":"70afb6f6748cacd04a2ab95dce9e2eed61c1158e.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:25:19Z","receivedAt":"2015-01-21T19:25:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_commit() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nNote that some problems are too problematic to simply ignore. For\nexample, when the header lines are mixed up, we punt after encountering\nan incorrect line. Therefore, demoting certain warnings to errors can\nhide other problems. Example: demoting the missing-author error to\na warning would hide a problematic committer line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 28 ++++++++++++++++++++--------\n 1 file changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 16500e3..8979357 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -508,12 +508,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n-\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n+\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n-\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -522,11 +528,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \tif (graft) {\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n-\t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\telse if (graft->nr_parent != parent_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t} else {\n-\t\tif (parent_count != parent_line_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\tif (parent_count != parent_line_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-- \n2.2.0.33.gc18b867\n"},{"id":"255016","messageId":"8e23b0f08f9aef7b57716587f2573d8b381b4594.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 09/19] fsck: Handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:25:30Z","receivedAt":"2015-01-21T19:25:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This problem has been detected in the wild, and is the primary reason\nto introduce an option to demote certain fsck errors to warnings. Let's\noffer to ignore this particular problem specifically.\n\nTechnically, we could handle such repositories by setting\nreceive.fsck.warn = missing-committer, but that could hide missing tree\nobjects in the same commit because we cannot continue verifying any\ncommit object after encountering a missing committer line, while we can\ncontinue in the case of multiple author lines.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex 8979357..3118db1 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -38,6 +38,7 @@\n \tFUNC(MISSING_TREE, ERROR) \\\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NOT_SORTED, ERROR) \\\n \tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n@@ -545,6 +546,14 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n+\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n+\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\terr = fsck_ident(&buffer, &commit->object, options);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n-- \n2.2.0.33.gc18b867\n"},{"id":"255017","messageId":"726432c3b043ab282eecb9c70ac3f15cb7fc91e3.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 10/19] fsck: Make fsck_tag() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:25:40Z","receivedAt":"2015-01-21T19:25:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_tag() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nJust like fsck_commit(), there are certain problems that could hide other\nissues with the same tag object. For example, if the 'type' line is not\nencountered in the correct position, the 'tag' line – if there is any –\nwould not be handled at all.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 3118db1..4adf9ce 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -614,7 +614,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n \t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n-\t\tgoto done;\n+\t\tif (ret)\n+\t\t\tgoto done;\n \t}\n \tbuffer += 41;\n \n-- \n2.2.0.33.gc18b867\n"},{"id":"255018","messageId":"f9acf7dd77008502e316795e37503d4c57c94f94.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 11/19] fsck: Add a simple test for receive.fsck.*","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:25:57Z","receivedAt":"2015-01-21T19:25:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5504-fetch-receive-strict.sh | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 69ee13c..40c7557 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -115,4 +115,24 @@ test_expect_success 'push with transfer.fsckobjects' '\n \ttest_cmp exp act\n '\n \n+cat >bogus-commit <<\\EOF\n+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+author Bugs Bunny 1234567890 +0000\n+committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n+\n+This commit object intentionally broken\n+EOF\n+\n+test_expect_success 'push with receive.fsck.warn = missing-email' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.warn missing-email &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\tgrep \"missing-email\" act\n+'\n+\n test_done\n-- \n2.2.0.33.gc18b867\n"},{"id":"255019","messageId":"ac323d099490f533a926d55adb64187c6f012b6c.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 12/19] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:26:12Z","receivedAt":"2015-01-21T19:26:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some kinds of errors are intrinsically unrecoverable (e.g. errors while\nuncompressing objects). It does not make sense to allow demoting them to\nmere warnings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 13 +++++++++++--\n t/t5504-fetch-receive-strict.sh |  9 +++++++++\n 2 files changed, 20 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 4adf9ce..27a381b 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,12 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FSCK_FATAL -1\n+\n #define FOREACH_MSG_ID(FUNC) \\\n+\t/* fatal errors */ \\\n+\tFUNC(NUL_IN_HEADER, FATAL) \\\n+\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n \t/* errors */ \\\n \tFUNC(BAD_DATE, ERROR) \\\n \tFUNC(BAD_EMAIL, ERROR) \\\n@@ -40,10 +45,8 @@\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NOT_SORTED, ERROR) \\\n-\tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n \tFUNC(UNKNOWN_TYPE, ERROR) \\\n-\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE, WARN) \\\n@@ -157,6 +160,9 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t}\n \n \t\tmsg_id = parse_msg_id(mode, len);\n+\t\tif (severity != FSCK_ERROR &&\n+\t\t\t\tmsg_id_info[msg_id].severity == FSCK_FATAL)\n+\t\t\tdie(\"Cannot demote %.*s\", len, mode);\n \t\toptions->msg_severity[msg_id] = severity;\n \t\tmode += len;\n \t}\n@@ -187,6 +193,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tif (msg_severity == FSCK_FATAL)\n+\t\tmsg_severity = FSCK_ERROR;\n+\n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n \tva_start(ap, fmt);\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 40c7557..75d718f 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -135,4 +135,13 @@ test_expect_success 'push with receive.fsck.warn = missing-email' '\n \tgrep \"missing-email\" act\n '\n \n+test_expect_success 'receive.fsck.warn = unterminated-header triggers error' '\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\tgit --git-dir=dst/.git config receive.fsck.warn unterminated-header &&\n+\ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n+\tgrep \"Cannot demote unterminated-header\" act\n+'\n+\n test_done\n-- \n2.2.0.33.gc18b867\n"},{"id":"255020","messageId":"571995874185461d6cf4fca0f725ff459f0720b0.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 13/19] fsck: Optionally ignore specific fsck issues completely","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:26:43Z","receivedAt":"2015-01-21T19:26:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An fsck issue in a legacy repository might be so common that one would\nlike not to bother the user with mentioning it at all. With this change,\nthat is possible by setting the respective error to \"ignore\".\n\nThis change \"abuses\" the warn=missing-email test to verify that \"ignore\"\nis also accepted and works correctly. And while at it, it makes sure\nthat multiple options work, too (they are passed to unpack-objects or\nindex-pack as a comma-separated list via the --strict=... command-line\noption).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 5 +++++\n fsck.h                          | 1 +\n t/t5504-fetch-receive-strict.sh | 7 ++++++-\n 3 files changed, 12 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 27a381b..028a7ca 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -152,6 +152,8 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t\t\tseverity = FSCK_ERROR;\n \t\t\telse if (!substrcmp(mode, equal, \"warn\"))\n \t\t\t\tseverity = FSCK_WARN;\n+\t\t\telse if (!substrcmp(mode, equal, \"ignore\"))\n+\t\t\t\tseverity = FSCK_IGNORE;\n \t\t\telse\n \t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n \t\t\t\t\tequal, mode);\n@@ -193,6 +195,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tif (msg_severity == FSCK_IGNORE)\n+\t\treturn 0;\n+\n \tif (msg_severity == FSCK_FATAL)\n \t\tmsg_severity = FSCK_ERROR;\n \ndiff --git a/fsck.h b/fsck.h\nindex 4349860..7be6c50 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -3,6 +3,7 @@\n \n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n+#define FSCK_IGNORE 3\n \n struct fsck_options;\n \ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 75d718f..5e54a13 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -132,7 +132,12 @@ test_expect_success 'push with receive.fsck.warn = missing-email' '\n \ttest_must_fail git push --porcelain dst bogus &&\n \tgit --git-dir=dst/.git config receive.fsck.warn missing-email &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missing-email\" act\n+\tgrep \"missing-email\" act &&\n+\tgit --git-dir=dst/.git branch -D bogus &&\n+\tgit  --git-dir=dst/.git config receive.fsck.ignore missing-email &&\n+\tgit  --git-dir=dst/.git config receive.fsck.warn bad-date &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\ttest_must_fail grep \"missing-email\" act\n '\n \n test_expect_success 'receive.fsck.warn = unterminated-header triggers error' '\n-- \n2.2.0.33.gc18b867\n"},{"id":"255021","messageId":"00dc8446a9c89b5093ccbe17667743caf09c4e79.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 14/19] fsck: Allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:26:54Z","receivedAt":"2015-01-21T19:26:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'invalid tag name' and 'missing tagger entry' warnings can now be\nupgraded to errors by specifying `invalid-tag-name` and\n`missing-tagger-entry` to the receive.fsck.error config setting.\n\nIncidentally, the missing tagger warning is now really shown as a warning\n(as opposed to being reported with the \"error:\" prefix, as it used to be\nthe case before this commit).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                | 24 +++++++++++++++++-------\n t/t5302-pack-index.sh |  2 +-\n 2 files changed, 18 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 028a7ca..1334941 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -10,6 +10,7 @@\n #include \"utf8.h\"\n \n #define FSCK_FATAL -1\n+#define FSCK_INFO -2\n \n #define FOREACH_MSG_ID(FUNC) \\\n \t/* fatal errors */ \\\n@@ -55,10 +56,11 @@\n \tFUNC(HAS_DOT, WARN) \\\n \tFUNC(HAS_DOTDOT, WARN) \\\n \tFUNC(HAS_DOTGIT, WARN) \\\n-\tFUNC(INVALID_TAG_NAME, WARN) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n \tFUNC(NULL_SHA1, WARN) \\\n-\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n+\t/* infos (reported as warnings, but ignored by default) */ \\\n+\tFUNC(INVALID_TAG_NAME, INFO) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n #define MSG_ID(id, severity) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -200,6 +202,8 @@ static int report(struct fsck_options *options, struct object *object,\n \n \tif (msg_severity == FSCK_FATAL)\n \t\tmsg_severity = FSCK_ERROR;\n+\telse if (msg_severity == FSCK_INFO)\n+\t\tmsg_severity = FSCK_WARN;\n \n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n@@ -658,15 +662,21 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n-\tif (check_refname_format(sb.buf, 0))\n-\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\tif (check_refname_format(sb.buf, 0)) {\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n \t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \tbuffer = eol + 1;\n \n-\tif (!skip_prefix(buffer, \"tagger \", &buffer))\n+\tif (!skip_prefix(buffer, \"tagger \", &buffer)) {\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 61bc8da..3dc5ec4 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -259,7 +259,7 @@ EOF\n     thirtyeight=${tag#??} &&\n     rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&\n     git index-pack --strict tag-test-${pack1}.pack 2>err &&\n-    grep \"^error:.* expected .tagger. line\" err\n+    grep \"^warning:.* expected .tagger. line\" err\n '\n \n test_done\n-- \n2.2.0.33.gc18b867\n"},{"id":"255022","messageId":"15f7d22f08fe590c3a1e5b2bf524dc2877624ca2.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 15/19] fsck: Document the new receive.fsck.* options.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:27:05Z","receivedAt":"2015-01-21T19:27:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 28 ++++++++++++++++++++++++++++\n 1 file changed, 28 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ae6791d..cc4cd91 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2130,6 +2130,34 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n+receive.fsck.error::\n+receive.fsck.warn::\n+receive.fsck.ignore::\n+\tWhen `receive.fsckObjects` is set to true, errors can be switched\n+\tto warnings and vice versa by configuring the `receive.fsck.*`\n+\tsettings. These settings contain comma-separated lists of fsck\n+\tmessage IDs. For convenience, fsck prefixes the error/warning with\n+\tthe message ID, e.g. \"missing-email: invalid\n+\tauthor/committer line - missing email\" means that setting\n+\t`receive.fsck.ignore = missing-email` will hide that issue.\n++\n+--\n+error;;\n+\ta comma-separated list of fsck message IDs that should be\n+\ttrigger fsck to error out.\n+warn;;\n+\ta comma-separated list of fsck message IDs that should be\n+\tdisplayed, but fsck should continue to error out.\n+ignore;;\n+\ta comma-separated list of fsck message IDs that should be\n+\tignored completely.\n+--\n++\n+This feature is intended to support working with legacy repositories\n+which would not pass pushing when `receive.fsckObjects = true`, allowing\n+the host to accept repositories with certain known issues but still catch\n+other issues.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\n-- \n2.2.0.33.gc18b867\n"},{"id":"255023","messageId":"c597de0fd5d5341c749139fd3057c0f51b477250.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 16/19] fsck: Support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:27:15Z","receivedAt":"2015-01-21T19:27:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already have support in `git receive-pack` to deal with some legacy\nrepositories which have non-fatal issues.\n\nLet's make `git fsck` itself useful with such repositories, too, by\nallowing users to ignore known issues, or at least demote those issues\nto mere warnings.\n\nExample: `git -c fsck.ignore=missing-email fsck` would hide problems with\nmissing emails in author, committer and tagger lines.\n\nIn the same spirit that `git receive-pack`'s usage of the fsck machinery\ndiffers from `git fsck`'s – some of the non-fatal warnings in `git fsck`\nare fatal with `git receive-pack` when receive.fsckObjects = true, for\nexample – we strictly separate the fsck.* from the receive.fsck.*\nsettings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 15 +++++++++++++++\n builtin/fsck.c           | 15 +++++++++++++++\n t/t1450-fsck.sh          | 11 +++++++++++\n 3 files changed, 41 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex cc4cd91..115811c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1208,6 +1208,21 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n+fsck.error::\n+fsck.warn::\n+fsck.ignore::\n+\tThe `fsck.error`, `fsck.warn` and `fsck.ignore` settings specify\n+\tcomma-separated lists of fsck message IDs which should trigger\n+\tfsck to error out, to print the message and continue, or to ignore\n+\tsaid messages, respectively.\n++\n+For convenience, fsck prefixes the error/warning with the name of the option,\n+e.g.  \"missing-email: invalid author/committer line - missing email\" means\n+that setting `fsck.ignore = missing-email` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which cannot be repaired without disruptive changes.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 99d4538..6f5e671 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,6 +46,19 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n+static int fsck_config(const char *var, const char *value, void *cb)\n+{\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"%s=%s\", var, value);\n+\t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n static void objreport(struct object *obj, const char *severity,\n                       const char *err)\n {\n@@ -638,6 +651,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tgit_config(fsck_config, NULL);\n+\n \tfsck_head_link();\n \tfsck_object_dir(get_object_directory());\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex ea0f216..a79ff9f 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -287,6 +287,17 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '\n \tgrep -q \"error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff\" out\n '\n \n+test_expect_success 'force fsck to ignore double author' '\n+\tgit cat-file commit HEAD >basis &&\n+\tsed \"s/^author .*/&,&/\" <basis | tr , \\\\n >multiple-authors &&\n+\tnew=$(git hash-object -t commit -w --stdin <multiple-authors) &&\n+\ttest_when_finished \"remove_object $new\" &&\n+\tgit update-ref refs/heads/bogus \"$new\" &&\n+\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n+\ttest_must_fail git fsck &&\n+\tgit -c fsck.ignore=multiple-authors fsck\n+'\n+\n _bz='\\0'\n _bz5=\"$_bz$_bz$_bz$_bz$_bz\"\n _bz20=\"$_bz5$_bz5$_bz5$_bz5\"\n-- \n2.2.0.33.gc18b867\n"},{"id":"255024","messageId":"d2b3c1b00446075e01612d391585292df4f32932.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:27:26Z","receivedAt":"2015-01-21T19:27:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This option avoids unpacking each and all objects, and just verifies the\nconnectivity. In particular with large repositories, this speeds up the\noperation, at the expense of missing corrupt blobs and ignoring\nunreachable objects, if any.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  7 ++++++-\n builtin/fsck.c             |  7 ++++++-\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 3 files changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 25c431d..b98fb43 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n-\t [--[no-]full] [--strict] [--verbose] [--lost-found]\n+\t [--[no-]full] [--quick] [--strict] [--verbose] [--lost-found]\n \t [--[no-]dangling] [--[no-]progress] [<object>*]\n \n DESCRIPTION\n@@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \tobject pools.  This is now default; you can turn it off\n \twith --no-full.\n \n+--quick::\n+\tCheck only the connectivity of tags, commits and tree objects. By\n+\tavoiding to unpack blobs, this speeds up the operation, at the\n+\texpense of missing corrupt objects.\n+\n --strict::\n \tEnable more strict checking, namely to catch a file mode\n \trecorded with g+w bit set, which was created by older\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 6f5e671..7ae4715 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -23,6 +23,7 @@ static int show_tags;\n static int show_unreachable;\n static int include_reflogs = 1;\n static int check_full = 1;\n+static int quick;\n static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n@@ -184,6 +185,8 @@ static void check_reachable_object(struct object *obj)\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (has_sha1_pack(obj->sha1))\n \t\t\treturn; /* it is in pack - forget about it */\n+\t\tif (quick && has_sha1_file(obj->sha1))\n+\t\t\treturn;\n \t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n@@ -618,6 +621,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"cache\", &keep_cache_objects, N_(\"make index objects head nodes\")),\n \tOPT_BOOL(0, \"reflogs\", &include_reflogs, N_(\"make reflogs head nodes (default)\")),\n \tOPT_BOOL(0, \"full\", &check_full, N_(\"also consider packs and alternate objects\")),\n+\tOPT_BOOL(0, \"quick\", &quick, N_(\"check only connectivity\")),\n \tOPT_BOOL(0, \"strict\", &check_strict, N_(\"enable more strict checking\")),\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n@@ -654,7 +658,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tfsck_object_dir(get_object_directory());\n+\tif (!quick)\n+\t\tfsck_object_dir(get_object_directory());\n \n \tprepare_alt_odb();\n \tfor (alt = alt_odb_list; alt; alt = alt->next) {\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex a79ff9f..1c624a3 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n \ttest_must_fail git -C missing fsck\n '\n \n+test_expect_success 'fsck --quick' '\n+\trm -rf quick &&\n+\tgit init quick &&\n+\t(\n+\t\tcd quick &&\n+\t\ttouch empty &&\n+\t\tgit add empty &&\n+\t\ttest_commit empty &&\n+\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n+\t\trm -f $empty &&\n+\t\techo invalid >$empty &&\n+\t\ttest_must_fail git fsck --strict &&\n+\t\tgit fsck --strict --quick &&\n+\t\ttree=$(git rev-parse HEAD:) &&\n+\t\tsuffix=${tree#??} &&\n+\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n+\t\trm -f $tree &&\n+\t\techo invalid >$tree &&\n+\t\ttest_must_fail git fsck --strict --quick\n+\t)\n+'\n+\n test_done\n-- \n2.2.0.33.gc18b867\n"},{"id":"255025","messageId":"93c9736d3fcf0b61389334af750465c2897d409d.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:27:36Z","receivedAt":"2015-01-21T19:27:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The optional new config option `receive.fsck.skiplist` specifies the path\nto a file listing the names, i.e. SHA-1s, one per line, of objects that\nare to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nThe intended use case is for server administrators to inspect objects\nthat are reported by `git push` as being too problematic to enter the\nrepository, and to add the objects' SHA-1 to a (preferably sorted) file\nwhen the objects are legitimate, i.e. when it is determined that those\nproblematic objects should be allowed to enter the server.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt        |  7 ++++++\n builtin/receive-pack.c          |  9 +++++++\n fsck.c                          | 53 +++++++++++++++++++++++++++++++++++++++++\n fsck.h                          |  1 +\n t/t5504-fetch-receive-strict.sh | 12 ++++++++++\n 5 files changed, 82 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 115811c..636adff 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2173,6 +2173,13 @@ which would not pass pushing when `receive.fsckObjects = true`, allowing\n the host to accept repositories with certain known issues but still catch\n other issues.\n \n+receive.fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 18d5012..8e6d1a1 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -116,6 +116,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tif (fsck_severity.len)\n+\t\t\tstrbuf_addch(&fsck_severity, ',');\n+\t\tstrbuf_addf(&fsck_severity, \"skiplist=%s\", path);\n+\t\treturn 0;\n+\t}\n+\n \tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n \t\tstrbuf_addf(&fsck_severity, \"%s%s=%s\",\n \t\t\tfsck_severity.len ? \",\" : \"\", var, value);\ndiff --git a/fsck.c b/fsck.c\nindex 1334941..15cb8bd 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -8,6 +8,7 @@\n #include \"fsck.h\"\n #include \"refs.h\"\n #include \"utf8.h\"\n+#include \"sha1-array.h\"\n \n #define FSCK_FATAL -1\n #define FSCK_INFO -2\n@@ -117,6 +118,43 @@ static int fsck_msg_severity(enum fsck_msg_id msg_id,\n \treturn severity;\n }\n \n+static void init_skiplist(struct fsck_options *options, const char *path)\n+{\n+\tstatic struct sha1_array skiplist = SHA1_ARRAY_INIT;\n+\tint sorted, fd;\n+\tchar buffer[41];\n+\tunsigned char sha1[20];\n+\n+\tif (options->skiplist)\n+\t\tsorted = options->skiplist->sorted;\n+\telse {\n+\t\tsorted = 1;\n+\t\toptions->skiplist = &skiplist;\n+\t}\n+\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Could not open skip list: %s\", path);\n+\tfor (;;) {\n+\t\tint result = read_in_full(fd, buffer, sizeof(buffer));\n+\t\tif (result < 0)\n+\t\t\tdie_errno(\"Could not read '%s'\", path);\n+\t\tif (!result)\n+\t\t\tbreak;\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n+\t\t\tdie(\"Invalid SHA-1: %s\", buffer);\n+\t\tsha1_array_append(&skiplist, sha1);\n+\t\tif (sorted && skiplist.nr > 1 &&\n+\t\t\t\thashcmp(skiplist.sha1[skiplist.nr - 2],\n+\t\t\t\t\tsha1) > 0)\n+\t\t\tsorted = 0;\n+\t}\n+\tclose(fd);\n+\n+\tif (sorted)\n+\t\tskiplist.sorted = 1;\n+}\n+\n static inline int substrcmp(const char *string, int len, const char *match)\n {\n \tint match_len = strlen(match);\n@@ -156,6 +194,17 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t\t\tseverity = FSCK_WARN;\n \t\t\telse if (!substrcmp(mode, equal, \"ignore\"))\n \t\t\t\tseverity = FSCK_IGNORE;\n+\t\t\telse if (!substrcmp(mode, equal, \"skiplist\")) {\n+\t\t\t\tchar *path = xstrndup(mode + equal + 1,\n+\t\t\t\t\tlen - equal - 1);\n+\n+\t\t\t\tif (equal == len)\n+\t\t\t\t\tdie(\"skiplist requires a path\");\n+\t\t\t\tinit_skiplist(options, path);\n+\t\t\t\tfree(path);\n+\t\t\t\tmode += len;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\telse\n \t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n \t\t\t\t\tequal, mode);\n@@ -700,6 +749,10 @@ static int fsck_tag(struct tag *tag, const char *data,\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n+\tif (options->skiplist &&\n+\t\t\tsha1_array_lookup(options->skiplist, obj->sha1) >= 0)\n+\t\treturn 0;\n+\n \tif (!obj)\n \t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \ndiff --git a/fsck.h b/fsck.h\nindex 7be6c50..cae280e 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -29,6 +29,7 @@ struct fsck_options {\n \tfsck_error error_func;\n \tunsigned strict:1;\n \tint *msg_severity;\n+\tstruct sha1_array *skiplist;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 5e54a13..d367bb2 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,6 +123,18 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n+test_expect_success 'push with receive.fsck.skiplist' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.skiplist SKIP &&\n+\techo $commit > dst/.git/SKIP &&\n+\tgit push --porcelain dst bogus\n+'\n+\n test_expect_success 'push with receive.fsck.warn = missing-email' '\n \tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n-- \n2.2.0.33.gc18b867\n"},{"id":"255026","messageId":"fe744590eab4ee0b9ba25ac3473d93ab581956aa.1421868116.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1421868116.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 19/19] fsck: support ignoring objects in `git fsck` via fsck.skiplist","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-21T19:27:46Z","receivedAt":"2015-01-21T19:27:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Identical to support in `git receive-pack for the config option\n`receive.fsck.skiplist`, we now support ignoring given objects in\n`git fsck` altogether.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt |  7 +++++++\n builtin/fsck.c           | 10 ++++++++++\n 2 files changed, 17 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 636adff..644411a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1223,6 +1223,13 @@ that setting `fsck.ignore = missing-email` will hide that issue.\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n \n+fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 7ae4715..760b4bd 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -49,6 +49,16 @@ static int show_dangling = 1;\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n+\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n \tif (skip_prefix(var, \"fsck.\", &var)) {\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstrbuf_addf(&sb, \"%s=%s\", var, value);\n-- \n2.2.0.33.gc18b867\n"},{"id":"255034","messageId":"xmqqa91bx0v5.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"07b285ef702031fdebcf9def59528b9c@www.dscho.org","subject":"Re: [PATCH v2 05/18] fsck: Allow demoting errors to warnings via receive.fsck.warn = <key>","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-01-21T21:47:10Z","receivedAt":"2015-01-21T21:47: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>>> @@ -1488,8 +1501,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n>>>\n>>>  \t\targv_array_pushl(&child.args, \"index-pack\",\n>>>  \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n>>> -\t\tif (fsck_objects)\n>>> -\t\t\targv_array_push(&child.args, \"--strict\");\n>>> +\t\tif (fsck_objects) {\n>>> +\t\t\tif (fsck_severity.len)\n>>> +\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n>>> +\t\t\t\t\tfsck_severity.buf);\n>>> +\t\t\telse\n>>> +\t\t\t\targv_array_push(&child.args, \"--strict\");\n>>> +\t\t}\n>> \n>> Hmm.  The above two hunks look suspiciously similar.  Would it be\n>> worth to give them a single helper function?\n>\n> Hmm. Not sure. I see what you mean, but for now I found\n>\n> +                       argv_array_pushf(&child.args, \"--strict%s%s\",\n> +                               fsck_severity.len ? \"=\" : \"\",\n> +                               fsck_severity.buf);\n>\n> to be more elegant than to add a fully-fledged new function. But if\n> you feel strongly, I will gladly implement a separate function; I\n> would appreciate suggestions as to the function name...\n\nPeff first introduced that trick elsewhere in our codebase, I think,\nbut I find it a bit too ugly.\n\nAs you accumulate fsck_severity strbuf like this anyway:\n\n\tstrbuf_addf(&fsck_severity, \"%s%s=%s\",\n        \tfsck_severity.len ? \",\" : \"\", var, value);\n\nto flip what to prefix each element on the list with, I wonder if it\nis simpler to change that empty string to \"=\", which will allow you\nto say this:\n\n\targv_array_pushf(&child.args, \"--strict%s\", fsck_severity.buf);\n\nOr even this:\n\n\tstrbuf_addf(&fsck_strict_arg, \"%s%s=%s\",\n        \tfsck_strict_arg.len ? \",\" : \"--strict=\", var, value);\n\nand then the child.args stuff can become\n\n\tif (fsck_strict_arg.len)\n\t\targv_array_push(&child.args, fsck_strict_arg.buf);\n\nIn any case, I tend to agree with you that it is overkill to add a\nhelper function for just to add a single element to the argument\nlist.\n\nThanks.\n\n\t\n"},{"id":"255073","messageId":"8605af2fe097d5ebdf575d164eec2408@www.dscho.org","threadId":"38144","inReplyTo":"xmqqa91bx0v5.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 05/18] fsck: Allow demoting errors to warnings via receive.fsck.warn = <key>","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-22T09:35:28Z","receivedAt":"2015-01-22T09:35:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-01-21 22:47, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>>>> @@ -1488,8 +1501,13 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n>>>>\n>>>>  \t\targv_array_pushl(&child.args, \"index-pack\",\n>>>>  \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n>>>> -\t\tif (fsck_objects)\n>>>> -\t\t\targv_array_push(&child.args, \"--strict\");\n>>>> +\t\tif (fsck_objects) {\n>>>> +\t\t\tif (fsck_severity.len)\n>>>> +\t\t\t\targv_array_pushf(&child.args, \"--strict=%s\",\n>>>> +\t\t\t\t\tfsck_severity.buf);\n>>>> +\t\t\telse\n>>>> +\t\t\t\targv_array_push(&child.args, \"--strict\");\n>>>> +\t\t}\n>>>\n>>> Hmm.  The above two hunks look suspiciously similar.  Would it be\n>>> worth to give them a single helper function?\n>>\n>> Hmm. Not sure. I see what you mean, but for now I found\n>>\n>> +                       argv_array_pushf(&child.args, \"--strict%s%s\",\n>> +                               fsck_severity.len ? \"=\" : \"\",\n>> +                               fsck_severity.buf);\n>>\n>> to be more elegant than to add a fully-fledged new function. But if\n>> you feel strongly, I will gladly implement a separate function; I\n>> would appreciate suggestions as to the function name...\n> \n> Peff first introduced that trick elsewhere in our codebase, I think,\n> but I find it a bit too ugly.\n> \n> As you accumulate fsck_severity strbuf like this anyway:\n> \n> \tstrbuf_addf(&fsck_severity, \"%s%s=%s\",\n>         \tfsck_severity.len ? \",\" : \"\", var, value);\n> \n> to flip what to prefix each element on the list with, I wonder if it\n> is simpler to change that empty string to \"=\", which will allow you\n> to say this:\n> \n> \targv_array_pushf(&child.args, \"--strict%s\", fsck_severity.buf);\n\nBut of course! This is what I did now:\n\n-- snip --\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 8e6d1a1..08e3716 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -126,8 +126,8 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t}\n \n \tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n-\t\tstrbuf_addf(&fsck_severity, \"%s%s=%s\",\n-\t\t\tfsck_severity.len ? \",\" : \"\", var, value);\n+\t\tstrbuf_addf(&fsck_severity, \"%c%s=%s\",\n+\t\t\tfsck_severity.len ? ',' : '=', var, value);\n \t\treturn 0;\n \t}\n \n@@ -1487,8 +1487,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects)\n-\t\t\targv_array_pushf(&child.args, \"--strict%s%s\",\n-\t\t\t\tfsck_severity.len ? \"=\" : \"\",\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n \t\t\t\tfsck_severity.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n@@ -1507,8 +1506,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects)\n-\t\t\targv_array_pushf(&child.args, \"--strict%s%s\",\n-\t\t\t\tfsck_severity.len ? \"=\" : \"\",\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n \t\t\t\tfsck_severity.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n-- snap --\n\n> Or even this:\n> \n> \tstrbuf_addf(&fsck_strict_arg, \"%s%s=%s\",\n>         \tfsck_strict_arg.len ? \",\" : \"--strict=\", var, value);\n\nUnfortunately not, because just `--strict` needs to be passed in case no severity levels were overridden.\n\n> In any case, I tend to agree with you that it is overkill to add a\n> helper function for just to add a single element to the argument\n> list.\n\nI am glad we agree!\n\nCiao,\nDscho\n"},{"id":"255103","messageId":"54C11C0B.6080200@alum.mit.edu","threadId":"38144","inReplyTo":"xmqqa92e47ap.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2015-01-22T15:49:31Z","receivedAt":"2015-01-22T15:49:31Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 12/23/2014 06:14 PM, Junio C Hamano wrote:\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>> On Tue, 23 Dec 2014, Junio C Hamano wrote:\n>>> I suspect that it would be much better if the configuration variables\n>>> were organized the other way around, e.g.\n>>>\n>>> \t$ git config fsck.warn missingTagger,someOtherKindOfError\n>>\n>> I had something similar in an earlier version of my patch series, but it\n>> was shot down rightfully: if you want to allow inheriting defaults from\n>> $HOME/.gitconfig, you have to configure the severity levels individually.\n> \n> Hmmm.  What's wrong with \"fsck.warn -missingTagger\" that overrides\n> the earlier one, or even \"fsck.info missingTagger\" after having\n> \"fsck.warn other,missingTagger,yetanother\", with the usual \"last one\n> wins\" rule?\n> \n> Whoever shot it down \"rightfully\" is wrong here, I would think.\n\nSorry I didn't notice this earlier; Johannes, please CC me on these\nseries, especially the ones that I commented on earlier.\n\nI might have been the one who \"shot down\" the \"<severity>=<name>\" style\nof configuration [1].\n\nI don't feel strongly enough to make a big deal about this, especially\nconsidering that the other alternative has already been implemented. But\nfor the record, let me explain why I prefer the \"<name>=<severity>\"\nstyle of configuration.\n\nFirst, it is a truer representation of the data structure within the\nsoftware, which is basically one severity value for each error type.\nThis is not a decisive argument, but it often means that there is less\nimpedance mismatch between the style of configuration and the concepts\nthat it is configuring. For example,\n\n    $ git config receive.fsck.warn A,B,C\n    $ git config receive.fsck.error C,D,E\n\nseems to be configuring two sets, but it is not. It is mysteriously\nsetting \"C\" to be an error, in seeming contradiction of the first line [2].\n\nSecond, it is not correct to say that this is just an application of the\n\"last setting wins\" rule. The \"last setting wins\" rule has heretofore,\nas far as I know, only covered *single* settings that take a single\nvalue. If we applied that rule to the following:\n\n    $ git config receive.fsck.warn A,B,C\n    $ git config receive.fsck.warn B,F\n\nthen the net result would be \"B,F\". But that is not your proposal at\nall; your proposal is for these two settings to be interpreted the same as\n\n    $ git config receive.fsck.warn A,B,C,F\n\nSimilarly, the traditional last setting rule, applied to the first\nexample above, wouldn't cause the value of \"fsck.warn\" to be reduced to\n\"A,B\", as you propose. This is not the \"last setting rule\" that we are\nfamiliar with--it operates *across and within* values and across\n*multiple* names rather than just across the values for a single name.\n\nThird, the \"<severity>=<name>\" style is hard to inquire via the command\nline, and probably also incompatible with the simplified internal config\nAPI in git (and probably libgit2, JGit, etc). The problem is that\ndetermining a *single* setting requires *three* configuration variables\nbe inquired, and that the settings for those three variables need to be\nprocessed in the correct order, including the correct order of\ninterleavings. For example, how would you inquire about the configured\nseverity level of \"missingTaggerEntry\" using the shell? It would be a\nmess that would necessarily have to involve \"git config --get-regexp\"\nand error-prone parsing of comma-separated values. It would be so much\neasier to type\n\n    $ git config receive.fsck.missingtaggerentry\n\nFourth, the \"<severity>=<name>\" style would cause config files to get\ncluttered up with unused values. Suppose you have earlier run\n\n    $ git config receive.fsck.warn A,B,C\n    $ git config receive.fsck.ignore D,E\n\nand now you want to demote \"B\" to \"ignore\". You can do\n\n    $ git config --add receive.fsck.ignore B\n\n(don't forget \"--add\" or you've silently erased other, unrelated\nsettings!) This gives the behavior that you want. But now your config\nfile looks like\n\n    [receive \"fsck\"]\n            warn = A,B,C\n            ignore = D,E\n            ignore = B\n\nThe \"B\" on the first line is now just being carried along for no reason,\nbut it would be quite awkward to clean it up programmatically.\nEffectively, these settings can only be added to but never removed\nbecause of the way multiple properties are mashed into a single setting.\n\n\nI believe that one of the main arguments for the \"<severity>=<name>\"\nstyle of configuration is that it carries over more easily into\nconvenient command-line options. But I think it will be unusual to want\nto configure these options by hand on the command line, let alone adjust\nmany settings at the same time. The idea isn't to make it easy to work\nwith repositories that have a level of breakage that fluctuates over\ntime. It is to make it possible to work with *specific* repositories\nthat have known breakage in their history. For such a repo you would\nconfigure one or two \"ignore\" options one time and then never adjust\nthem again. (And it will also allow us to make our checks stricter in\nthe future without breaking existing repositories, and even to add\noptional \"policy\" checks, like \"forbid Windows-incompatible filenames\".)\n\nI would even go so far as to say that we don't *need* command-line\noption versions of these settings; if somebody really needs that they\ncan type\n\n    $ git -c receive.fsck.missingtaggerentry=ignore fsck\n\n(which also has the advantage of passing the setting through to any\nchild processes). But *if* command-line options are considered\nnecessary, I don't think that using a \"<name>=<severity>\" style within\nthe config needs to rule out allowing command-line options in the form\n\"--<severity>=<name>,<name>\" as Junio has suggested.\n\nLooking back at this email, I guess that I'm more strongly against the\n\"<name>=<severity>\" configuration style than I thought :-/\n\nMichael\n\n[1] I prefer to think that I just offered a little gentle discussion\nthat informed Johannes's independent decision :-)\n\n[2] But even on these terms, it is anomalous. The usual git way to\nconfigure a set in git would be\n\n    $ git config receive.fsck.warn A\n    $ git config --add receive.fsck.warn B\n    $ git config --add receive.fsck.warn C\n    $ git config receive.fsck.error C\n    $ git config --add receive.fsck.error D\n    $ git config --add receive.fsck.error E\n\n, which in fact has fewer of the disadvantages listed in this email.\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"255109","messageId":"595afdd37f7774361bc18de8003c2d16@www.dscho.org","threadId":"38144","inReplyTo":"54C11C0B.6080200@alum.mit.edu","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-22T17:17:16Z","receivedAt":"2015-01-22T17:17:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Michael,\n\nOn 2015-01-22 16:49, Michael Haggerty wrote:\n> On 12/23/2014 06:14 PM, Junio C Hamano wrote:\n>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>>> On Tue, 23 Dec 2014, Junio C Hamano wrote:\n>>>> I suspect that it would be much better if the configuration variables\n>>>> were organized the other way around, e.g.\n>>>>\n>>>> \t$ git config fsck.warn missingTagger,someOtherKindOfError\n>>>\n>>> I had something similar in an earlier version of my patch series, but it\n>>> was shot down rightfully: if you want to allow inheriting defaults from\n>>> $HOME/.gitconfig, you have to configure the severity levels individually.\n>>\n>> Hmmm.  What's wrong with \"fsck.warn -missingTagger\" that overrides\n>> the earlier one, or even \"fsck.info missingTagger\" after having\n>> \"fsck.warn other,missingTagger,yetanother\", with the usual \"last one\n>> wins\" rule?\n>>\n>> Whoever shot it down \"rightfully\" is wrong here, I would think.\n> \n> Sorry I didn't notice this earlier; Johannes, please CC me on these\n> series, especially the ones that I commented on earlier.\n\nVery sorry, this is my fault. It can only be explained by my switching around some tools for other tools to work with email-based patch submission (which I had not done in a long time). But still, my mistake.\n\n\n> [1] I prefer to think that I just offered a little gentle discussion\n> that informed Johannes's independent decision :-)\n\nYou did convince me back then. I just did not want to put up a fight against Junio because I was more interested in getting this feature merged before the holidays (it does feel awkward for me to leave work unwrapped-up before leaving for an extended amount of time, but I guess I am getting more used to that).\n\nSo now I cannot avoid discussing this issue properly...\n\nIn essence, I agreed with Junio from the point of view of an elegant implementation. But then, Michael is correct that it does not really matter as much how complicated the code is, but that it is much more important that the feature is elegant to use.\n\nNow let's step back a bit and think about the users which is supposed to be supported by this patch series: Git repository hosters -- such as GitHub -- need to ensure a certain cleanliness of the repositories they host (for a range of reasons, including the prevention of malicious attacks, or helping users publish their code in a correct form).\n\nAnd the scenario in which the feature needs to be used is most likely started by some Git user pushing some commits, and `git receive-pack` triggering an error. Then the user files a trouble ticket and GitHubber needs to inspect the error and the respective object. Now, in the vast number of cases I imagine that the objects *are* faulty. However, on occasion the problem should not prevent the push, e.g. when somebody crafted a commit object with two authors, forgetting that the tools usually cannot handle such commits. Then the GitHubber has to decide on a case by case basis whether to demote that error to a warning and allow the object to be pushed *into that specific repository*.\n\nI do see the need for this feature to be simple and robust, from the users' point of view. In other words, I agree with Michael that we need to avoid confusing settings such as\n\n```\n[receive.fsck]\n    warn = missing-tagger-entry\n    error = missing-tagger-entry\n```\n\nThis feature will be used rarely enough that the poor soul stuck with interpreting the above config section won't remember that a very specific version of \"last setting wins\" is in effect.\n\nIf I remember correctly, Peff suggested that there needs to be a way to handle these settings in the /etc/gitconfig $HOME/.gitconfig $XDG../gitconfig .git/config cascade, but now I am puzzled whether it is even desirable to demote fsck errors globally, i.e. whether we really need to pay attention to that config cascade.\n\nAnd finally, in the course of preparing this patch series, we came up with an alternative solution to the problem: the receive.fsck.skiplist (i.e. a file that contains a sorted list of SHA-1s of objects that should be skipped from fsck'ing). I am more and more convinced that this is the most convenient tool for the scenario described above: manual inspection of individual objects will tell whether it is safe to allow them onto the server or not.\n\nHowever, others might disagree and prefer the explicit approach, e.g. when some source generates a consistent stream of objects triggering fsck errors.\n\nSummary: I have no preference how to specify the severity levels of fsck messages, but I will gladly change my code to whatever you (meaning Junio and Michael in particular) want to see implemented.\n\nThanks for helping me with this feature,\nDscho\n"},{"id":"255462","messageId":"81a3242fec22358d9c2c16b632cbbf9d@www.dscho.org","threadId":"38144","inReplyTo":"595afdd37f7774361bc18de8003c2d16@www.dscho.org","subject":"Re: [PATCH 16/18] fsck: support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T20:41:32Z","receivedAt":"2015-01-31T20:41:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Michael & Junio,\n\nOn 2015-01-22 18:17, Johannes Schindelin wrote:\n\n> [...] we need to avoid confusing settings such as\n> \n> ```\n> [receive.fsck]\n>     warn = missing-tagger-entry\n>     error = missing-tagger-entry\n> ```\n\nI *think* I found a solution.\n\nPlease let me recapitulate quickly the problem Michael brought up: if we support `receive.fsck.warn` to override `receive.fsck.error` and vice versa, with comma-separated lists, then it can be quite confusing to the user, and actually quite difficult to figure out on the command-line which setting is in effect (because it really depends on the *order* of the receive.fsck.* lines, *plus* the fact that the values are comma-separated lists).\n\nOn the other hand, Junio pointed out two shortcomings with my original implementation (i.e. to support `receive.fsck.<id> = (error|warn|ignore)`), however: it is tedious to set multiple severity levels, and it violates the config file convention that the config variable names are CamelCased (the message IDs are dashed-lowercase instead).\n\nThe solution I just implemented (and will send out shortly in v4 of the patch series) is the following: the config variable is called receive.fsck.severity and it accepts comma-separated settings. Example:\n\n```\n[receive \"fsck\"]\n        severity = multiple-authors=ignore,missing-tagger=error\n```\n\nNow, it is *still* not the easiest to figure out the setting from the command-line:\n\n```sh\n$ git config --get-all receive.fsck.severity |\n        tr \",\" \"\\n\" |\n        grep ^multiple-authors= |\n        tail -n 1\n```\n\nBut I hope this is good enough, Michael?\n\nCiao,\nDscho\n"},{"id":"255463","messageId":"cover.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"xmqqr3w7gxr4.fsf@gitster.dls.corp.google.com","subject":"[PATCH v4 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:04:25Z","receivedAt":"2015-01-31T21:04:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At the moment, the git-fsck's integrity checks are targeted toward the\nend user, i.e. the error messages are really just messages, intended for\nhuman consumption.\n\nUnder certain circumstances, some of those errors should be allowed to\nbe turned into mere warnings, though, because the cost of fixing the\nissues might well be larger than the cost of carrying those flawed\nobjects. For example, when an already-public repository contains a\ncommit object with two authors for years, it does not make sense to\nforce the maintainer to rewrite the history, affecting all contributors\nnegatively by forcing them to update.\n\nThis branch introduces an internal fsck API to be able to turn some of\nthe errors into warnings, and to make it easier to call the fsck\nmachinery from elsewhere in general.\n\nI am proud to report that this work has been sponsored by GitHub.\n\nChanges vs v3: address Junio's concern regarding repeated patterns,\nheed Peff's advice to avoid that pesky clang warning re: unsigned enums,\nuse the fsck.skiplist setting in builtin/fsck.c (not\nreceive.fsck.skiplist), and switch to fsck.severity to address Michael's\nconcerns that letting fsck.(error|warn|ignore)'s comma-separated lists\npossibly overriding each other partially; interdiff below the diffstat.\n\nJohannes Schindelin (19):\n  fsck: Introduce fsck options\n  fsck: Introduce identifiers for fsck messages\n  fsck: Provide a function to parse fsck message IDs\n  fsck: Offer a function to demote fsck errors to warnings\n  fsck: Allow demoting errors to warnings\n  fsck: Report the ID of the error/warning\n  fsck: Make fsck_ident() warn-friendly\n  fsck: Make fsck_commit() warn-friendly\n  fsck: Handle multiple authors in commits specially\n  fsck: Make fsck_tag() warn-friendly\n  fsck: Add a simple test for receive.fsck.severity\n  fsck: Disallow demoting grave fsck errors to warnings\n  fsck: Optionally ignore specific fsck issues completely\n  fsck: Allow upgrading fsck warnings to errors\n  fsck: Document the new receive.fsck.severity options.\n  fsck: Support demoting errors to warnings\n  fsck: Introduce `git fsck --quick`\n  fsck: git receive-pack: support excluding objects from fsck'ing\n  fsck: support ignoring objects in `git fsck` via fsck.skiplist\n\n Documentation/config.txt        |  41 ++++\n Documentation/git-fsck.txt      |   7 +-\n builtin/fsck.c                  |  73 ++++--\n builtin/index-pack.c            |  13 +-\n builtin/receive-pack.c          |  21 +-\n builtin/unpack-objects.c        |  16 +-\n fsck.c                          | 532 +++++++++++++++++++++++++++++++---------\n fsck.h                          |  27 +-\n t/t1450-fsck.sh                 |  37 ++-\n t/t5302-pack-index.sh           |   2 +-\n t/t5504-fetch-receive-strict.sh |  49 ++++\n 11 files changed, 656 insertions(+), 162 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 644411a..93c43d5 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1208,17 +1208,14 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n-fsck.error::\n-fsck.warn::\n-fsck.ignore::\n-\tThe `fsck.error`, `fsck.warn` and `fsck.ignore` settings specify\n-\tcomma-separated lists of fsck message IDs which should trigger\n-\tfsck to error out, to print the message and continue, or to ignore\n-\tsaid messages, respectively.\n+fsck.severity::\n+\tA comma-separated lists of of the form `<id>=<level>` where `<id>`\n+\tdenotes a fsck message ID such as `missing-email` and `<level>` is\n+\tone of `error`, `warn` and `ignore`.\n +\n-For convenience, fsck prefixes the error/warning with the name of the option,\n+For convenience, fsck prefixes the error/warning with the message ID,\n e.g.  \"missing-email: invalid author/committer line - missing email\" means\n-that setting `fsck.ignore = missing-email` will hide that issue.\n+that setting `fsck.severity = missing-email=ignore` will hide that issue.\n +\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n@@ -2152,28 +2149,15 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n-receive.fsck.error::\n-receive.fsck.warn::\n-receive.fsck.ignore::\n+receive.fsck.severity::\n \tWhen `receive.fsckObjects` is set to true, errors can be switched\n-\tto warnings and vice versa by configuring the `receive.fsck.*`\n-\tsettings. These settings contain comma-separated lists of fsck\n-\tmessage IDs. For convenience, fsck prefixes the error/warning with\n-\tthe message ID, e.g. \"missing-email: invalid\n+\tto warnings and vice versa by configuring the `receive.fsck.severity`\n+\tsetting. These settings contain comma-separated lists of the form\n+\t`<id>=<level>` where the `<id>` is the fsck message ID and the level\n+\tis one of `error`, `warn` or `ignore`. For convenience, fsck prefixes\n+\tthe error/warning with the message ID, e.g. \"missing-email: invalid\n \tauthor/committer line - missing email\" means that setting\n-\t`receive.fsck.ignore = missing-email` will hide that issue.\n-+\n---\n-error;;\n-\ta comma-separated list of fsck message IDs that should be\n-\ttrigger fsck to error out.\n-warn;;\n-\ta comma-separated list of fsck message IDs that should be\n-\tdisplayed, but fsck should continue to error out.\n-ignore;;\n-\ta comma-separated list of fsck message IDs that should be\n-\tignored completely.\n---\n+\t`receive.fsck.severity = missing-email=ignore` will hide that issue.\n +\n This feature is intended to support working with legacy repositories\n which would not pass pushing when `receive.fsckObjects = true`, allowing\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 7eb4ff8..81570d8 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -49,7 +49,12 @@ static int show_dangling = 1;\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n-\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n+\tif (strcmp(var, \"fsck.severity\") == 0) {\n+\t\tfsck_set_severity(&fsck_obj_options, value);\n+\t\treturn 0;\n+\t}\n+\n+\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n \t\tconst char *path = is_absolute_path(value) ?\n \t\t\tvalue : git_path(\"%s\", value);\n \t\tstruct strbuf sb = STRBUF_INIT;\n@@ -59,14 +64,6 @@ static int fsck_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n-\tif (skip_prefix(var, \"fsck.\", &var)) {\n-\t\tstruct strbuf sb = STRBUF_INIT;\n-\t\tstrbuf_addf(&sb, \"%s=%s\", var, value);\n-\t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n-\t\tstrbuf_release(&sb);\n-\t\treturn 0;\n-\t}\n-\n \treturn git_default_config(var, value, cb);\n }\n \ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 8e6d1a1..f454e65 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -116,18 +116,17 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"receive.fsck.severity\") == 0) {\n+\t\tstrbuf_addf(&fsck_severity, \"%c%s\",\n+\t\t\tfsck_severity.len ? ',' : '=', value);\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n \t\tconst char *path = is_absolute_path(value) ?\n \t\t\tvalue : git_path(\"%s\", value);\n-\t\tif (fsck_severity.len)\n-\t\t\tstrbuf_addch(&fsck_severity, ',');\n-\t\tstrbuf_addf(&fsck_severity, \"skiplist=%s\", path);\n-\t\treturn 0;\n-\t}\n-\n-\tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n-\t\tstrbuf_addf(&fsck_severity, \"%s%s=%s\",\n-\t\t\tfsck_severity.len ? \",\" : \"\", var, value);\n+\t\tstrbuf_addf(&fsck_severity, \"%cskiplist=%s\",\n+\t\t\tfsck_severity.len ? ',' : '=', path);\n \t\treturn 0;\n \t}\n \n@@ -1487,8 +1486,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects)\n-\t\t\targv_array_pushf(&child.args, \"--strict%s%s\",\n-\t\t\t\tfsck_severity.len ? \"=\" : \"\",\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n \t\t\t\tfsck_severity.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n@@ -1507,8 +1505,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects)\n-\t\t\targv_array_pushf(&child.args, \"--strict%s%s\",\n-\t\t\t\tfsck_severity.len ? \"=\" : \"\",\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n \t\t\t\tfsck_severity.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\ndiff --git a/fsck.c b/fsck.c\nindex 15cb8bd..046af02 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -107,7 +107,9 @@ static int fsck_msg_severity(enum fsck_msg_id msg_id,\n {\n \tint severity;\n \n-\tif (options->msg_severity && msg_id >= 0 && msg_id < FSCK_MSG_MAX)\n+\tassert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);\n+\n+\tif (options->msg_severity)\n \t\tseverity = options->msg_severity[msg_id];\n \telse {\n \t\tseverity = msg_id_info[msg_id].severity;\n@@ -184,35 +186,40 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t}\n \n \t\tfor (equal = 0; equal < len; equal++)\n-\t\t\tif (mode[equal] == '=')\n+\t\t\tif (mode[equal] == '=' || mode[equal] == ':')\n \t\t\t\tbreak;\n \n-\t\tif (equal < len) {\n-\t\t\tif (!substrcmp(mode, equal, \"error\"))\n+\t\tif (!substrcmp(mode, equal, \"skiplist\")) {\n+\t\t\tchar *path = xstrndup(mode + equal + 1,\n+\t\t\t\tlen - equal - 1);\n+\n+\t\t\tif (equal == len)\n+\t\t\t\tdie(\"skiplist requires a path\");\n+\t\t\tinit_skiplist(options, path);\n+\t\t\tfree(path);\n+\t\t\tmode += len;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tmsg_id = parse_msg_id(mode, equal);\n+\n+\t\tif (equal == len)\n+\t\t\tseverity = FSCK_ERROR;\n+\t\telse {\n+\t\t\tconst char *p = mode + equal + 1;\n+\t\t\tint len2 = len - equal - 1;\n+\n+\t\t\tif (!substrcmp(p, len2, \"error\"))\n \t\t\t\tseverity = FSCK_ERROR;\n-\t\t\telse if (!substrcmp(mode, equal, \"warn\"))\n+\t\t\telse if (!substrcmp(p, len2, \"warn\"))\n \t\t\t\tseverity = FSCK_WARN;\n-\t\t\telse if (!substrcmp(mode, equal, \"ignore\"))\n+\t\t\telse if (!substrcmp(p, len2, \"ignore\"))\n \t\t\t\tseverity = FSCK_IGNORE;\n-\t\t\telse if (!substrcmp(mode, equal, \"skiplist\")) {\n-\t\t\t\tchar *path = xstrndup(mode + equal + 1,\n-\t\t\t\t\tlen - equal - 1);\n-\n-\t\t\t\tif (equal == len)\n-\t\t\t\t\tdie(\"skiplist requires a path\");\n-\t\t\t\tinit_skiplist(options, path);\n-\t\t\t\tfree(path);\n-\t\t\t\tmode += len;\n-\t\t\t\tcontinue;\n-\t\t\t}\n \t\t\telse\n \t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n-\t\t\t\t\tequal, mode);\n-\t\t\tmode += equal + 1;\n-\t\t\tlen -= equal + 1;\n+\t\t\t\t\tlen2, p);\n \t\t}\n \n-\t\tmsg_id = parse_msg_id(mode, len);\n \t\tif (severity != FSCK_ERROR &&\n \t\t\t\tmsg_id_info[msg_id].severity == FSCK_FATAL)\n \t\t\tdie(\"Cannot demote %.*s\", len, mode);\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 1c624a3..b32afaf 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -295,7 +295,7 @@ test_expect_success 'force fsck to ignore double author' '\n \tgit update-ref refs/heads/bogus \"$new\" &&\n \ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n \ttest_must_fail git fsck &&\n-\tgit -c fsck.ignore=multiple-authors fsck\n+\tgit -c fsck.severity=multiple-authors=ignore fsck\n '\n \n _bz='\\0'\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex d367bb2..7881e17 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -135,28 +135,31 @@ test_expect_success 'push with receive.fsck.skiplist' '\n \tgit push --porcelain dst bogus\n '\n \n-test_expect_success 'push with receive.fsck.warn = missing-email' '\n+test_expect_success 'push with receive.fsck.severity = missing-email=warn' '\n \tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n \ttest_must_fail git push --porcelain dst bogus &&\n-\tgit --git-dir=dst/.git config receive.fsck.warn missing-email &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.severity missing-email=warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n \tgrep \"missing-email\" act &&\n \tgit --git-dir=dst/.git branch -D bogus &&\n-\tgit  --git-dir=dst/.git config receive.fsck.ignore missing-email &&\n-\tgit  --git-dir=dst/.git config receive.fsck.warn bad-date &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.severity missing-email=ignore,bad-date=warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n \ttest_must_fail grep \"missing-email\" act\n '\n \n-test_expect_success 'receive.fsck.warn = unterminated-header triggers error' '\n+test_expect_success \\\n+\t'receive.fsck.severity = unterminated-header=warn triggers error' '\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n-\tgit --git-dir=dst/.git config receive.fsck.warn unterminated-header &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.severity unterminated-header=warn &&\n \ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n \tgrep \"Cannot demote unterminated-header\" act\n '\n-- \n2.2.0.33.gc18b867\n"},{"id":"255464","messageId":"9e29c2be8c10309470119cb8dcb671e5f0c81ef7.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 01/19] fsck: Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:04:39Z","receivedAt":"2015-01-31T21:04:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Just like the diff machinery, we are about to introduce more settings,\ntherefore it makes sense to carry them around as a (pointer to a) struct\ncontaining all of them.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c           |  20 +++++--\n builtin/index-pack.c     |   9 +--\n builtin/unpack-objects.c |  11 ++--\n fsck.c                   | 150 +++++++++++++++++++++++------------------------\n fsck.h                   |  17 +++++-\n 5 files changed, 114 insertions(+), 93 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 0c75786..0d03c15 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -25,6 +25,8 @@ static int include_reflogs = 1;\n static int check_full = 1;\n static int check_strict;\n static int keep_cache_objects;\n+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n static unsigned char head_sha1[20];\n static const char *head_points_at;\n static int errors_found;\n@@ -76,7 +78,7 @@ static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n \n static struct object_array pending;\n \n-static int mark_object(struct object *obj, int type, void *data)\n+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct object *parent = data;\n \n@@ -119,7 +121,7 @@ static int mark_object(struct object *obj, int type, void *data)\n \n static void mark_object_reachable(struct object *obj)\n {\n-\tmark_object(obj, OBJ_ANY, NULL);\n+\tmark_object(obj, OBJ_ANY, NULL, NULL);\n }\n \n static int traverse_one_object(struct object *obj)\n@@ -132,7 +134,7 @@ static int traverse_one_object(struct object *obj)\n \t\tif (parse_tree(tree) < 0)\n \t\t\treturn 1; /* error already displayed */\n \t}\n-\tresult = fsck_walk(obj, mark_object, obj);\n+\tresult = fsck_walk(obj, obj, &fsck_walk_options);\n \tif (tree)\n \t\tfree_tree_buffer(tree);\n \treturn result;\n@@ -158,7 +160,7 @@ static int traverse_reachable(void)\n \treturn !!result;\n }\n \n-static int mark_used(struct object *obj, int type, void *data)\n+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn 1;\n@@ -296,9 +298,9 @@ static int fsck_obj(struct object *obj)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n \t\t\ttypename(obj->type), sha1_to_hex(obj->sha1));\n \n-\tif (fsck_walk(obj, mark_used, NULL))\n+\tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n-\tif (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))\n+\tif (fsck_object(obj, NULL, 0, &fsck_obj_options))\n \t\treturn -1;\n \n \tif (obj->type == OBJ_TREE) {\n@@ -630,6 +632,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);\n \n+\tfsck_walk_options.walk = mark_object;\n+\tfsck_obj_options.walk = mark_used;\n+\tfsck_obj_options.error_func = fsck_error_func;\n+\tif (check_strict)\n+\t\tfsck_obj_options.strict = 1;\n+\n \tif (show_progress == -1)\n \t\tshow_progress = isatty(2);\n \tif (verbose)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 4632117..925f7b5 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -74,6 +74,7 @@ static int nr_threads;\n static int from_stdin;\n static int strict;\n static int do_fsck_object;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n static int verbose;\n static int show_stat;\n static int check_self_contained_and_connected;\n@@ -191,7 +192,7 @@ static void cleanup_thread(void)\n #endif\n \n \n-static int mark_link(struct object *obj, int type, void *data)\n+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -782,10 +783,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (!obj)\n \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(obj, buf, size, 1,\n-\t\t\t\t    fsck_error_function))\n+\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"Error in object\"));\n-\t\t\tif (fsck_walk(obj, mark_link, NULL))\n+\t\t\tfsck_options.walk = mark_link;\n+\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n \n \t\t\tif (obj->type == OBJ_TREE) {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex ac66672..6d17040 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -20,6 +20,7 @@ static unsigned char buffer[4096];\n static unsigned int offset, len;\n static off_t consumed_bytes;\n static git_SHA_CTX ctx;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n /*\n  * When running under --strict mode, objects whose reachability are\n@@ -178,7 +179,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)\n  * that have reachability requirements and calls this function.\n  * Verify its reachability and validity recursively and write it out.\n  */\n-static int check_object(struct object *obj, int type, void *data)\n+static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct obj_buffer *obj_buf;\n \n@@ -203,10 +204,10 @@ static int check_object(struct object *obj, int type, void *data)\n \tobj_buf = lookup_object_buffer(obj);\n \tif (!obj_buf)\n \t\tdie(\"Whoops! Cannot find object '%s'\", sha1_to_hex(obj->sha1));\n-\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1,\n-\t\t\tfsck_error_function))\n+\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"Error in object\");\n-\tif (fsck_walk(obj, check_object, NULL))\n+\tfsck_options.walk = check_object;\n+\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\tdie(\"Error on reachable objects of %s\", sha1_to_hex(obj->sha1));\n \twrite_cached_object(obj, obj_buf);\n \treturn 0;\n@@ -217,7 +218,7 @@ static void write_rest(void)\n \tunsigned i;\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tif (obj_list[i].obj)\n-\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL);\n+\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);\n \t}\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex 10bcb65..d83b811 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,7 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n-static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\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@@ -25,9 +25,9 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n \t\tif (S_ISDIR(entry.mode))\n-\t\t\tresult = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);\n+\t\t\tresult = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);\n \t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n-\t\t\tresult = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);\n+\t\t\tresult = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n \t\t\t\t\tsha1_to_hex(tree->object.sha1), entry.path, entry.mode);\n@@ -40,7 +40,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \treturn res;\n }\n \n-static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)\n+static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n \tstruct commit_list *parents;\n \tint res;\n@@ -49,14 +49,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \tif (parse_commit(commit))\n \t\treturn -1;\n \n-\tresult = walk((struct object *)commit->tree, OBJ_TREE, data);\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 \twhile (parents) {\n-\t\tresult = walk((struct object *)parents->item, OBJ_COMMIT, data);\n+\t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n@@ -66,14 +66,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \treturn res;\n }\n \n-static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)\n+static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n \tif (parse_tag(tag))\n \t\treturn -1;\n-\treturn walk(tag->tagged, OBJ_ANY, data);\n+\treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -81,11 +81,11 @@ int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n \tcase OBJ_BLOB:\n \t\treturn 0;\n \tcase OBJ_TREE:\n-\t\treturn fsck_walk_tree((struct tree *)obj, walk, data);\n+\t\treturn fsck_walk_tree((struct tree *)obj, data, options);\n \tcase OBJ_COMMIT:\n-\t\treturn fsck_walk_commit((struct commit *)obj, walk, data);\n+\t\treturn fsck_walk_commit((struct commit *)obj, data, options);\n \tcase OBJ_TAG:\n-\t\treturn fsck_walk_tag((struct tag *)obj, walk, data);\n+\t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n \tdefault:\n \t\terror(\"Unknown object type for %s\", sha1_to_hex(obj->sha1));\n \t\treturn -1;\n@@ -138,7 +138,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con\n \treturn c1 < c2 ? 0 : TREE_UNORDERED;\n }\n \n-static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n+static int fsck_tree(struct tree *item, struct fsck_options *options)\n {\n \tint retval;\n \tint has_null_sha1 = 0;\n@@ -194,7 +194,7 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \t\t * bits..\n \t\t */\n \t\tcase S_IFREG | 0664:\n-\t\t\tif (!strict)\n+\t\t\tif (!options->strict)\n \t\t\t\tbreak;\n \t\tdefault:\n \t\t\thas_bad_modes = 1;\n@@ -219,30 +219,30 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n \treturn retval;\n }\n \n static int require_end_of_header(const void *data, unsigned long size,\n-\tstruct object *obj, fsck_error error_func)\n+\tstruct object *obj, struct fsck_options *options)\n {\n \tconst char *buffer = (const char *)data;\n \tunsigned long i;\n@@ -250,7 +250,7 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn error_func(obj, FSCK_ERROR,\n+\t\t\treturn options->error_func(obj, FSCK_ERROR,\n \t\t\t\t\"unterminated header: NUL at offset %d\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n@@ -258,36 +258,36 @@ static int require_end_of_header(const void *data, unsigned long size,\n \t\t}\n \t}\n \n-\treturn error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n }\n \n-static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)\n+static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,30 +295,30 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n \n static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n \tunsigned parent_count, parent_line_count = 0;\n \tint err;\n \n-\tif (require_end_of_header(buffer, size, &commit->object, error_func))\n+\tif (require_end_of_header(buffer, size, &commit->object, options))\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,39 +328,39 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n \n static int fsck_commit(struct commit *commit, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tconst char *buffer = data ?  data : get_commit_buffer(commit, &size);\n-\tint ret = fsck_commit_buffer(commit, buffer, size, error_func);\n+\tint ret = fsck_commit_buffer(commit, buffer, size, options);\n \tif (!data)\n \t\tunuse_commit_buffer(commit, buffer);\n \treturn ret;\n }\n \n static int fsck_tag_buffer(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char sha1[20];\n \tint ret = 0;\n@@ -376,65 +376,65 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n \t\t}\n \t}\n \n-\tif (require_end_of_header(buffer, size, &tag->object, error_func))\n+\tif (require_end_of_header(buffer, size, &tag->object, options))\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n \telse\n-\t\tret = fsck_ident(&buffer, &tag->object, error_func);\n+\t\tret = fsck_ident(&buffer, &tag->object, options);\n \n done:\n \tstrbuf_release(&sb);\n@@ -443,34 +443,34 @@ done:\n }\n \n static int fsck_tag(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n \n-\treturn fsck_tag_buffer(tag, data, size, error_func);\n+\treturn fsck_tag_buffer(tag, data, size, options);\n }\n \n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func)\n+\tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n \tif (obj->type == OBJ_TREE)\n-\t\treturn fsck_tree((struct tree *) obj, strict, error_func);\n+\t\treturn fsck_tree((struct tree *) obj, options);\n \tif (obj->type == OBJ_COMMIT)\n \t\treturn fsck_commit((struct commit *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \tif (obj->type == OBJ_TAG)\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \n-\treturn error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex d1e6387..07d0ab2 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -4,6 +4,8 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n \n+struct fsck_options;\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -12,7 +14,7 @@\n  *     <0\terror signaled and abort\n  *     >0\terror signaled and do not abort\n  */\n-typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);\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 *err, ...);\n@@ -20,6 +22,15 @@ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);\n __attribute__((format (printf, 3, 4)))\n int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n \n+struct fsck_options {\n+\tfsck_walk_func walk;\n+\tfsck_error error_func;\n+\tunsigned strict:1;\n+};\n+\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+\n /* descend in all linked child objects\n  * the return value is:\n  *    -1\terror in processing the object\n@@ -27,9 +38,9 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n  *    >0\treturn value of the first signaled error >0 (in the case of no other errors)\n  *    0\t\teverything OK\n  */\n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n /* If NULL is passed for data, we assume the object is local and read it. */\n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func);\n+\tstruct fsck_options *options);\n \n #endif\n-- \n2.2.0.33.gc18b867\n"},{"id":"255465","messageId":"50f28536d4f68766fb293cd07d2ca655c380e7db.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 02/19] fsck: Introduce identifiers for fsck messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:04:49Z","receivedAt":"2015-01-31T21:04:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Instead of specifying whether a message by the fsck machinery constitutes\nan error or a warning, let's specify an identifier relating to the\nconcrete problem that was encountered. This is necessary for upcoming\nsupport to be able to demote certain errors to warnings.\n\nIn the process, simplify the requirements on the calling code: instead of\nhaving to handle full-blown varargs in every callback, we now send a\nstring buffer ready to be used by the callback.\n\nWe could use a simple enum for the message IDs here, but we want to\nguarantee that the enum values are associated with the appropriate\nseverity levels. Besides, we want to introduce a parser in the next commit\nthat maps the string representation to the enum value, hence we use the\nslightly ugly preprocessor construct that is extensible for use with said\nparser.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c |  24 ++-----\n fsck.c         | 201 +++++++++++++++++++++++++++++++++++++++++----------------\n fsck.h         |   5 +-\n 3 files changed, 153 insertions(+), 77 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 0d03c15..1f7944c 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -47,32 +47,22 @@ static int show_dangling = 1;\n #endif\n \n static void objreport(struct object *obj, const char *severity,\n-                      const char *err, va_list params)\n+                      const char *err)\n {\n-\tfprintf(stderr, \"%s in %s %s: \",\n-\t        severity, typename(obj->type), sha1_to_hex(obj->sha1));\n-\tvfprintf(stderr, err, params);\n-\tfputs(\"\\n\", stderr);\n+\tfprintf(stderr, \"%s in %s %s: %s\\n\",\n+\t        severity, typename(obj->type), sha1_to_hex(obj->sha1), err);\n }\n \n-__attribute__((format (printf, 2, 3)))\n-static int objerror(struct object *obj, const char *err, ...)\n+static int objerror(struct object *obj, const char *err)\n {\n-\tva_list params;\n-\tva_start(params, err);\n \terrors_found |= ERROR_OBJECT;\n-\tobjreport(obj, \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, \"error\", err);\n \treturn -1;\n }\n \n-__attribute__((format (printf, 3, 4)))\n-static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n+static int fsck_error_func(struct object *obj, int type, const char *message)\n {\n-\tva_list params;\n-\tva_start(params, err);\n-\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n \treturn (type == FSCK_WARN) ? 0 : 1;\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex d83b811..30f7a48 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,98 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FOREACH_MSG_ID(FUNC) \\\n+\t/* errors */ \\\n+\tFUNC(BAD_DATE, ERROR) \\\n+\tFUNC(BAD_EMAIL, ERROR) \\\n+\tFUNC(BAD_NAME, ERROR) \\\n+\tFUNC(BAD_PARENT_SHA1, ERROR) \\\n+\tFUNC(BAD_TIMEZONE, ERROR) \\\n+\tFUNC(BAD_TREE_SHA1, ERROR) \\\n+\tFUNC(DATE_OVERFLOW, ERROR) \\\n+\tFUNC(DUPLICATE_ENTRIES, ERROR) \\\n+\tFUNC(INVALID_OBJECT_SHA1, ERROR) \\\n+\tFUNC(INVALID_TAG_OBJECT, ERROR) \\\n+\tFUNC(INVALID_TREE, ERROR) \\\n+\tFUNC(INVALID_TYPE, ERROR) \\\n+\tFUNC(MISSING_AUTHOR, ERROR) \\\n+\tFUNC(MISSING_COMMITTER, ERROR) \\\n+\tFUNC(MISSING_EMAIL, ERROR) \\\n+\tFUNC(MISSING_GRAFT, ERROR) \\\n+\tFUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_OBJECT, ERROR) \\\n+\tFUNC(MISSING_PARENT, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_TAG, ERROR) \\\n+\tFUNC(MISSING_TAG_ENTRY, ERROR) \\\n+\tFUNC(MISSING_TAG_OBJECT, ERROR) \\\n+\tFUNC(MISSING_TREE, ERROR) \\\n+\tFUNC(MISSING_TYPE, ERROR) \\\n+\tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(NOT_SORTED, ERROR) \\\n+\tFUNC(NUL_IN_HEADER, ERROR) \\\n+\tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n+\tFUNC(UNKNOWN_TYPE, ERROR) \\\n+\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n+\tFUNC(ZERO_PADDED_DATE, ERROR) \\\n+\t/* warnings */ \\\n+\tFUNC(BAD_FILEMODE, WARN) \\\n+\tFUNC(EMPTY_NAME, WARN) \\\n+\tFUNC(FULL_PATHNAME, WARN) \\\n+\tFUNC(HAS_DOT, WARN) \\\n+\tFUNC(HAS_DOTDOT, WARN) \\\n+\tFUNC(HAS_DOTGIT, WARN) \\\n+\tFUNC(INVALID_TAG_NAME, WARN) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n+\tFUNC(NULL_SHA1, WARN) \\\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\n+#define MSG_ID(id, severity) FSCK_MSG_##id,\n+enum fsck_msg_id {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\tFSCK_MSG_MAX\n+};\n+#undef MSG_ID\n+\n+#define MSG_ID(id, severity) { FSCK_##severity },\n+static struct {\n+\tint severity;\n+} msg_id_info[FSCK_MSG_MAX + 1] = {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\t{ -1 }\n+};\n+#undef MSG_ID\n+\n+static int fsck_msg_severity(enum fsck_msg_id msg_id,\n+\tstruct fsck_options *options)\n+{\n+\tint severity;\n+\n+\tseverity = msg_id_info[msg_id].severity;\n+\tif (options->strict && severity == FSCK_WARN)\n+\t\tseverity = FSCK_ERROR;\n+\n+\treturn severity;\n+}\n+\n+__attribute__((format (printf, 4, 5)))\n+static int report(struct fsck_options *options, struct object *object,\n+\tenum fsck_msg_id id, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint msg_severity = fsck_msg_severity(id, options), result;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tresult = options->error_func(object, msg_severity, sb.buf);\n+\tstrbuf_release(&sb);\n+\tva_end(ap);\n+\n+\treturn result;\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n@@ -219,25 +311,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NULL_SHA1, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOT, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NOT_SORTED, \"not properly sorted\");\n \treturn retval;\n }\n \n@@ -250,15 +342,17 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn options->error_func(obj, FSCK_ERROR,\n-\t\t\t\t\"unterminated header: NUL at offset %d\", i);\n+\t\t\treturn report(options, obj,\n+\t\t\t\tFSCK_MSG_NUL_IN_HEADER,\n+\t\t\t\t\"unterminated header: NUL at offset %ld\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n \t\t\t\treturn 0;\n \t\t}\n \t}\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn report(options, obj,\n+\t\tFSCK_MSG_UNTERMINATED_HEADER, \"unterminated header\");\n }\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n@@ -266,28 +360,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,7 +389,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n@@ -312,13 +406,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,23 +422,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn report(options, &commit->object, FSCK_MSG_INVALID_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n@@ -376,11 +470,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_MISSING_TAG_OBJECT,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_TAG_OBJECT_NOT_TAG,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n@@ -391,48 +487,49 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \n@@ -448,7 +545,7 @@ static int fsck_tag(struct tag *tag, const char *data,\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn report(options, &tag->object, FSCK_MSG_INVALID_TAG_OBJECT, \"could not load tagged object\");\n \n \treturn fsck_tag_buffer(tag, data, size, options);\n }\n@@ -457,7 +554,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n@@ -470,22 +567,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n \t\t\tsize, options);\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn report(options, obj, FSCK_MSG_UNKNOWN_TYPE, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...)\n+int fsck_error_function(struct object *obj, int severity, const char *message)\n {\n-\tva_list ap;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tstrbuf_addf(&sb, \"object %s:\", sha1_to_hex(obj->sha1));\n-\n-\tva_start(ap, fmt);\n-\tstrbuf_vaddf(&sb, fmt, ap);\n-\tva_end(ap);\n-\n-\terror(\"%s\", sb.buf);\n-\tstrbuf_release(&sb);\n+\terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex 07d0ab2..f6f268a 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -17,10 +17,9 @@ struct fsck_options;\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 *err, ...);\n+typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n \n-__attribute__((format (printf, 3, 4)))\n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n+int fsck_error_function(struct object *obj, int type, const char *message);\n \n struct fsck_options {\n \tfsck_walk_func walk;\n-- \n2.2.0.33.gc18b867\n"},{"id":"255466","messageId":"c6fb23c70970e083b046a784e2dc22bb3f0776ba.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 03/19] fsck: Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:04:59Z","receivedAt":"2015-01-31T21:04:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This function will be used in the next commits to allow the user to\nask fsck to handle specific problems differently, e.g. demoting certain\nerrors to warnings. It has to handle partial strings because we would\nlike to be able to parse, say, 'missing-email,missing-tagger-entry'\ncommand lines.\n\nTo make the parsing robust, we generate strings from the enum keys, and\nusing these keys, we will map lower-case, dash-separated strings values\nto the corresponding enum values.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 27 +++++++++++++++++++++++++--\n 1 file changed, 25 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 30f7a48..2d91e28 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -63,15 +63,38 @@ enum fsck_msg_id {\n };\n #undef MSG_ID\n \n-#define MSG_ID(id, severity) { FSCK_##severity },\n+#define STR(x) #x\n+#define MSG_ID(id, severity) { STR(id), FSCK_##severity },\n static struct {\n+\tconst char *id_string;\n \tint severity;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n-\t{ -1 }\n+\t{ NULL, -1 }\n };\n #undef MSG_ID\n \n+static int parse_msg_id(const char *text, int len)\n+{\n+\tint i, j;\n+\n+\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n+\t\tconst char *key = msg_id_info[i].id_string;\n+\t\t/* id_string is upper-case, with underscores */\n+\t\tfor (j = 0; j < len; j++) {\n+\t\t\tchar c = *(key++);\n+\t\t\tif (c == '_')\n+\t\t\t\tc = '-';\n+\t\t\tif (text[j] != tolower(c))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (j == len && !*key)\n+\t\t\treturn i;\n+\t}\n+\n+\tdie(\"Unhandled message id: %.*s\", len, text);\n+}\n+\n static int fsck_msg_severity(enum fsck_msg_id msg_id,\n \tstruct fsck_options *options)\n {\n-- \n2.2.0.33.gc18b867\n"},{"id":"255467","messageId":"4d054c3aee1e0743828170841c0169b33b7485df.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 05/19] fsck: Allow demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:05:07Z","receivedAt":"2015-01-31T21:05:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For example, missing emails in commit and tag objects can be demoted to\nmere warnings with\n\n\tgit config receive.fsck.severity missing-email=warn\n\nThe value is actually a comma-separated list.\n\nIn case that the same key is listed in multiple receive.fsck.severity\nlines in the config, the latter configuration wins (this can happen for\nexample when both $HOME/.gitconfig and .git/config contain severity\nsettings).\n\nAs git receive-pack does not actually perform the checks, it hands off\nthe setting to index-pack or unpack-objects in the form of an optional\nargument to the --strict option.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/index-pack.c     |  4 ++++\n builtin/receive-pack.c   | 13 +++++++++++--\n builtin/unpack-objects.c |  5 +++++\n 3 files changed, 20 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 925f7b5..b82b4dd 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1565,6 +1565,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(arg, \"--strict\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n+\t\t\t} else if (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tdo_fsck_object = 1;\n+\t\t\t\tfsck_set_severity(&fsck_options, arg);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex e0ce78e..9b7f1a8 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -36,6 +36,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n+static struct strbuf fsck_severity = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int advertise_atomic_push = 1;\n@@ -115,6 +116,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"receive.fsck.severity\") == 0) {\n+\t\tstrbuf_addf(&fsck_severity, \"%c%s\",\n+\t\t\tfsck_severity.len ? ',' : '=', value);\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsckobjects\") == 0) {\n \t\treceive_fsck_objects = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1471,7 +1478,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n+\t\t\t\tfsck_severity.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1489,7 +1497,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n+\t\t\t\tfsck_severity.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 6d17040..fe9117c 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrict = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tfsck_set_severity(&fsck_options, arg);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\n \t\t\t\tstruct pack_header *hdr;\n \t\t\t\tchar *c;\n-- \n2.2.0.33.gc18b867\n"},{"id":"255469","messageId":"91836561c79867d382913a567cf8ec085fe0ac0d.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:05:18Z","receivedAt":"2015-01-31T21:05:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are legacy repositories out there whose older commits and tags\nhave issues that prevent pushing them when 'receive.fsckObjects' is set.\nOne real-life example is a commit object that has been hand-crafted to\nlist two authors.\n\nOften, it is not possible to fix those issues without disrupting the\nwork with said repositories, yet it is still desirable to perform checks\nby setting `receive.fsckObjects = true`. This commit is the first step\nto allow demoting specific fsck issues to mere warnings.\n\nThe function added by this commit parses a list of settings in the form:\n\n\tmissing-email=warn,bad-name=warn,...\n\nUnfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\ngit fsck so far, but other call paths (e.g. git index-pack --strict)\nerror out *always* no matter what type was specified. Therefore, we\nneed to take extra care to default to all FSCK_ERROR in those cases.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n fsck.h |  7 +++++--\n 2 files changed, 72 insertions(+), 5 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 2d91e28..b5e7d2c 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -100,13 +100,73 @@ static int fsck_msg_severity(enum fsck_msg_id msg_id,\n {\n \tint severity;\n \n-\tseverity = msg_id_info[msg_id].severity;\n-\tif (options->strict && severity == FSCK_WARN)\n-\t\tseverity = FSCK_ERROR;\n+\tassert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);\n+\n+\tif (options->msg_severity)\n+\t\tseverity = options->msg_severity[msg_id];\n+\telse {\n+\t\tseverity = msg_id_info[msg_id].severity;\n+\t\tif (options->strict && severity == FSCK_WARN)\n+\t\t\tseverity = FSCK_ERROR;\n+\t}\n \n \treturn severity;\n }\n \n+static inline int substrcmp(const char *string, int len, const char *match)\n+{\n+\tint match_len = strlen(match);\n+\tif (match_len != len)\n+\t\treturn -1;\n+\treturn memcmp(string, match, len);\n+}\n+\n+void fsck_set_severity(struct fsck_options *options, const char *mode)\n+{\n+\tint severity = FSCK_ERROR;\n+\n+\tif (!options->msg_severity) {\n+\t\tint i;\n+\t\tint *msg_severity = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\t\tmsg_severity[i] = fsck_msg_severity(i, options);\n+\t\toptions->msg_severity = msg_severity;\n+\t}\n+\n+\twhile (*mode) {\n+\t\tint len = strcspn(mode, \" ,|\"), equal, msg_id;\n+\n+\t\tif (!len) {\n+\t\t\tmode++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (equal = 0; equal < len; equal++)\n+\t\t\tif (mode[equal] == '=' || mode[equal] == ':')\n+\t\t\t\tbreak;\n+\n+\t\tmsg_id = parse_msg_id(mode, equal);\n+\n+\t\tif (equal == len)\n+\t\t\tseverity = FSCK_ERROR;\n+\t\telse {\n+\t\t\tconst char *p = mode + equal + 1;\n+\t\t\tint len2 = len - equal - 1;\n+\n+\t\t\tif (!substrcmp(p, len2, \"error\"))\n+\t\t\t\tseverity = FSCK_ERROR;\n+\t\t\telse if (!substrcmp(p, len2, \"warn\"))\n+\t\t\t\tseverity = FSCK_WARN;\n+\t\t\telse\n+\t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n+\t\t\t\t\tlen2, p);\n+\t\t}\n+\n+\t\toptions->msg_severity[msg_id] = severity;\n+\t\tmode += len;\n+\t}\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -596,6 +656,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \n int fsck_error_function(struct object *obj, int severity, const char *message)\n {\n+\tif (severity == FSCK_WARN) {\n+\t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n+\t\treturn 0;\n+\t}\n \terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex f6f268a..4349860 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,8 @@\n \n struct fsck_options;\n \n+void fsck_set_severity(struct fsck_options *options, const char *mode);\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -25,10 +27,11 @@ struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n \tunsigned strict:1;\n+\tint *msg_severity;\n };\n \n-#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n-#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }\n \n /* descend in all linked child objects\n  * the return value is:\n-- \n2.2.0.33.gc18b867\n"},{"id":"255468","messageId":"1104acf532dc94156ec5a83214851ea08f896899.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 11/19] fsck: Add a simple test for receive.fsck.severity","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:05:26Z","receivedAt":"2015-01-31T21:05:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5504-fetch-receive-strict.sh | 21 +++++++++++++++++++++\n 1 file changed, 21 insertions(+)\n\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 69ee13c..9d49cb7 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -115,4 +115,25 @@ test_expect_success 'push with transfer.fsckobjects' '\n \ttest_cmp exp act\n '\n \n+cat >bogus-commit <<\\EOF\n+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+author Bugs Bunny 1234567890 +0000\n+committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n+\n+This commit object intentionally broken\n+EOF\n+\n+test_expect_success 'push with receive.fsck.severity = missing-email=warn' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.severity missing-email=warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\tgrep \"missing-email\" act\n+'\n+\n test_done\n-- \n2.2.0.33.gc18b867\n"},{"id":"255470","messageId":"1a5759c5882313ce5f39da089f9d1728abd85b77.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 12/19] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:05:34Z","receivedAt":"2015-01-31T21:05:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some kinds of errors are intrinsically unrecoverable (e.g. errors while\nuncompressing objects). It does not make sense to allow demoting them to\nmere warnings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 13 +++++++++++--\n t/t5504-fetch-receive-strict.sh | 11 +++++++++++\n 2 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex f72e404..e126320 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,12 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FSCK_FATAL -1\n+\n #define FOREACH_MSG_ID(FUNC) \\\n+\t/* fatal errors */ \\\n+\tFUNC(NUL_IN_HEADER, FATAL) \\\n+\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n \t/* errors */ \\\n \tFUNC(BAD_DATE, ERROR) \\\n \tFUNC(BAD_EMAIL, ERROR) \\\n@@ -40,10 +45,8 @@\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NOT_SORTED, ERROR) \\\n-\tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n \tFUNC(UNKNOWN_TYPE, ERROR) \\\n-\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE, WARN) \\\n@@ -163,6 +166,9 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t\t\t\tlen2, p);\n \t\t}\n \n+\t\tif (severity != FSCK_ERROR &&\n+\t\t\t\tmsg_id_info[msg_id].severity == FSCK_FATAL)\n+\t\t\tdie(\"Cannot demote %.*s\", len, mode);\n \t\toptions->msg_severity[msg_id] = severity;\n \t\tmode += len;\n \t}\n@@ -193,6 +199,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tif (msg_severity == FSCK_FATAL)\n+\t\tmsg_severity = FSCK_ERROR;\n+\n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n \tva_start(ap, fmt);\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 9d49cb7..0b6af82 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -136,4 +136,15 @@ test_expect_success 'push with receive.fsck.severity = missing-email=warn' '\n \tgrep \"missing-email\" act\n '\n \n+test_expect_success \\\n+\t'receive.fsck.severity = unterminated-header=warn triggers error' '\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.severity unterminated-header=warn &&\n+\ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n+\tgrep \"Cannot demote unterminated-header\" act\n+'\n+\n test_done\n-- \n2.2.0.33.gc18b867\n"},{"id":"255472","messageId":"8f24728329cfdac09800e4bb249b44486e459bc2.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 07/19] fsck: Make fsck_ident() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:05:41Z","receivedAt":"2015-01-31T21:05:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_ident() identifies a problem with the ident, it should still\nadvance the pointer to the next line so that fsck can continue in the\ncase of a mere warning.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 49 +++++++++++++++++++++++++++----------------------\n 1 file changed, 27 insertions(+), 22 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex e52b027..cd3ee48 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -459,40 +459,45 @@ static int require_end_of_header(const void *data, unsigned long size,\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n+\tconst char *p = *ident;\n \tchar *end;\n \n-\tif (**ident == '<')\n+\t*ident = strchrnul(*ident, '\\n');\n+\tif (**ident == '\\n')\n+\t\t(*ident)++;\n+\n+\tif (*p == '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident == '>')\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p == '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n-\tif (**ident != '<')\n+\tif (*p != '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n-\tif ((*ident)[-1] != ' ')\n+\tif (p[-1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t(*ident)++;\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident != '>')\n+\tp++;\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p != '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n-\t(*ident)++;\n-\tif (**ident != ' ')\n+\tp++;\n+\tif (*p != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n-\t(*ident)++;\n-\tif (**ident == '0' && (*ident)[1] != ' ')\n+\tp++;\n+\tif (*p == '0' && p[1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n-\tif (date_overflows(strtoul(*ident, &end, 10)))\n+\tif (date_overflows(strtoul(p, &end, 10)))\n \t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n-\tif (end == *ident || *end != ' ')\n+\tif ((end == p || *end != ' '))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n-\t*ident = end + 1;\n-\tif ((**ident != '+' && **ident != '-') ||\n-\t    !isdigit((*ident)[1]) ||\n-\t    !isdigit((*ident)[2]) ||\n-\t    !isdigit((*ident)[3]) ||\n-\t    !isdigit((*ident)[4]) ||\n-\t    ((*ident)[5] != '\\n'))\n+\tp = end + 1;\n+\tif ((*p != '+' && *p != '-') ||\n+\t    !isdigit(p[1]) ||\n+\t    !isdigit(p[2]) ||\n+\t    !isdigit(p[3]) ||\n+\t    !isdigit(p[4]) ||\n+\t    (p[5] != '\\n'))\n \t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n-\t(*ident) += 6;\n+\tp += 6;\n \treturn 0;\n }\n \n-- \n2.2.0.33.gc18b867\n"},{"id":"255471","messageId":"0575169ea5b47fd0a585db515736d10d5f9e8c14.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:05:49Z","receivedAt":"2015-01-31T21:05:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_commit() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nNote that some problems are too problematic to simply ignore. For\nexample, when the header lines are mixed up, we punt after encountering\nan incorrect line. Therefore, demoting certain warnings to errors can\nhide other problems. Example: demoting the missing-author error to\na warning would hide a problematic committer line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 28 ++++++++++++++++++++--------\n 1 file changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex cd3ee48..7ce7857 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -514,12 +514,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n-\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n+\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n-\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -528,11 +534,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \tif (graft) {\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n-\t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\telse if (graft->nr_parent != parent_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t} else {\n-\t\tif (parent_count != parent_line_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\tif (parent_count != parent_line_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-- \n2.2.0.33.gc18b867\n"},{"id":"255473","messageId":"eff683e7d1c7bd3b692f20c170615671e1672f84.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 10/19] fsck: Make fsck_tag() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:05:56Z","receivedAt":"2015-01-31T21:05:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_tag() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nJust like fsck_commit(), there are certain problems that could hide other\nissues with the same tag object. For example, if the 'type' line is not\nencountered in the correct position, the 'tag' line – if there is any –\nwould not be handled at all.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex b92d8c4..f72e404 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -620,7 +620,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n \t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n-\t\tgoto done;\n+\t\tif (ret)\n+\t\t\tgoto done;\n \t}\n \tbuffer += 41;\n \n-- \n2.2.0.33.gc18b867\n"},{"id":"255474","messageId":"4fb8d36a862fb5363b70e9d7bebd3577101be953.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 15/19] fsck: Document the new receive.fsck.severity options.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:06:03Z","receivedAt":"2015-01-31T21:06:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 15 +++++++++++++++\n 1 file changed, 15 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ae6791d..f893492 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2130,6 +2130,21 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n+receive.fsck.severity::\n+\tWhen `receive.fsckObjects` is set to true, errors can be switched\n+\tto warnings and vice versa by configuring the `receive.fsck.severity`\n+\tsetting. These settings contain comma-separated lists of the form\n+\t`<id>=<level>` where the `<id>` is the fsck message ID and the level\n+\tis one of `error`, `warn` or `ignore`. For convenience, fsck prefixes\n+\tthe error/warning with the message ID, e.g. \"missing-email: invalid\n+\tauthor/committer line - missing email\" means that setting\n+\t`receive.fsck.severity = missing-email=ignore` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which would not pass pushing when `receive.fsckObjects = true`, allowing\n+the host to accept repositories with certain known issues but still catch\n+other issues.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\n-- \n2.2.0.33.gc18b867\n"},{"id":"255476","messageId":"cf00fd78964eac735fa87089d8c2ec451e827089.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 09/19] fsck: Handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:06:11Z","receivedAt":"2015-01-31T21:06:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This problem has been detected in the wild, and is the primary reason\nto introduce an option to demote certain fsck errors to warnings. Let's\noffer to ignore this particular problem specifically.\n\nTechnically, we could handle such repositories by setting\nreceive.fsck.severity to missing-committer=warn, but that could hide\nmissing tree objects in the same commit because we cannot continue\nverifying any commit object after encountering a missing committer line,\nwhile we can continue in the case of multiple author lines.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex 7ce7857..b92d8c4 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -38,6 +38,7 @@\n \tFUNC(MISSING_TREE, ERROR) \\\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NOT_SORTED, ERROR) \\\n \tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n@@ -551,6 +552,14 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n+\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n+\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\terr = fsck_ident(&buffer, &commit->object, options);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n-- \n2.2.0.33.gc18b867\n"},{"id":"255475","messageId":"7bfd13f0407122a5ea7a61baf2c4dd3ba87c4444.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 14/19] fsck: Allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:06:18Z","receivedAt":"2015-01-31T21:06:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'invalid tag name' and 'missing tagger entry' warnings can now be\nupgraded to errors by specifying `invalid-tag-name` and\n`missing-tagger-entry` in the receive.fsck.severity config setting.\n\nIncidentally, the missing tagger warning is now really shown as a warning\n(as opposed to being reported with the \"error:\" prefix, as it used to be\nthe case before this commit).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                | 24 +++++++++++++++++-------\n t/t5302-pack-index.sh |  2 +-\n 2 files changed, 18 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 03ec945..c4079ed 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -10,6 +10,7 @@\n #include \"utf8.h\"\n \n #define FSCK_FATAL -1\n+#define FSCK_INFO -2\n \n #define FOREACH_MSG_ID(FUNC) \\\n \t/* fatal errors */ \\\n@@ -55,10 +56,11 @@\n \tFUNC(HAS_DOT, WARN) \\\n \tFUNC(HAS_DOTDOT, WARN) \\\n \tFUNC(HAS_DOTGIT, WARN) \\\n-\tFUNC(INVALID_TAG_NAME, WARN) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n \tFUNC(NULL_SHA1, WARN) \\\n-\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n+\t/* infos (reported as warnings, but ignored by default) */ \\\n+\tFUNC(INVALID_TAG_NAME, INFO) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n #define MSG_ID(id, severity) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -206,6 +208,8 @@ static int report(struct fsck_options *options, struct object *object,\n \n \tif (msg_severity == FSCK_FATAL)\n \t\tmsg_severity = FSCK_ERROR;\n+\telse if (msg_severity == FSCK_INFO)\n+\t\tmsg_severity = FSCK_WARN;\n \n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n@@ -664,15 +668,21 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n-\tif (check_refname_format(sb.buf, 0))\n-\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\tif (check_refname_format(sb.buf, 0)) {\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n \t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \tbuffer = eol + 1;\n \n-\tif (!skip_prefix(buffer, \"tagger \", &buffer))\n+\tif (!skip_prefix(buffer, \"tagger \", &buffer)) {\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 61bc8da..3dc5ec4 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -259,7 +259,7 @@ EOF\n     thirtyeight=${tag#??} &&\n     rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&\n     git index-pack --strict tag-test-${pack1}.pack 2>err &&\n-    grep \"^error:.* expected .tagger. line\" err\n+    grep \"^warning:.* expected .tagger. line\" err\n '\n \n test_done\n-- \n2.2.0.33.gc18b867\n"},{"id":"255477","messageId":"824d55e9930395ee70371295d8bc52b050732607.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 06/19] fsck: Report the ID of the error/warning","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:06:28Z","receivedAt":"2015-01-31T21:06:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some legacy code has objects with non-fatal fsck issues; To enable the\nuser to ignore those issues, let's print out the ID (e.g. when\nencountering \"missing-email\", the user might want to call `git config\n--add receive.fsck.severity missing-email=warn`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c          | 19 +++++++++++++++++++\n t/t1450-fsck.sh |  4 ++--\n 2 files changed, 21 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex b5e7d2c..e52b027 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -167,6 +167,23 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t}\n }\n \n+static void append_msg_id(struct strbuf *sb, const char *msg_id)\n+{\n+\tfor (;;) {\n+\t\tchar c = *(msg_id)++;\n+\n+\t\tif (!c)\n+\t\t\tbreak;\n+\t\tif (c == '_')\n+\t\t\tc = '-';\n+\t\telse\n+\t\t\tc = tolower(c);\n+\t\tstrbuf_addch(sb, c);\n+\t}\n+\n+\tstrbuf_addstr(sb, \": \");\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -175,6 +192,8 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tappend_msg_id(&sb, msg_id_info[id].id_string);\n+\n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n \tresult = options->error_func(object, msg_severity, sb.buf);\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex cfb32b6..ea0f216 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: invalid-tag-name: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missing-tagger-entry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n-- \n2.2.0.33.gc18b867\n"},{"id":"255478","messageId":"3723b0f57ade97234ff8709f70b2922a0fd4d016.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 13/19] fsck: Optionally ignore specific fsck issues completely","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:06:36Z","receivedAt":"2015-01-31T21:06:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An fsck issue in a legacy repository might be so common that one would\nlike not to bother the user with mentioning it at all. With this change,\nthat is possible by setting the respective error to \"ignore\".\n\nThis change \"abuses\" the missing-email=warn test to verify that \"ignore\"\nis also accepted and works correctly. And while at it, it makes sure\nthat multiple options work, too (they are passed to unpack-objects or\nindex-pack as a comma-separated list via the --strict=... command-line\noption).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 5 +++++\n fsck.h                          | 1 +\n t/t5504-fetch-receive-strict.sh | 7 ++++++-\n 3 files changed, 12 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex e126320..03ec945 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -161,6 +161,8 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t\t\tseverity = FSCK_ERROR;\n \t\t\telse if (!substrcmp(p, len2, \"warn\"))\n \t\t\t\tseverity = FSCK_WARN;\n+\t\t\telse if (!substrcmp(p, len2, \"ignore\"))\n+\t\t\t\tseverity = FSCK_IGNORE;\n \t\t\telse\n \t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n \t\t\t\t\tlen2, p);\n@@ -199,6 +201,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_severity = fsck_msg_severity(id, options), result;\n \n+\tif (msg_severity == FSCK_IGNORE)\n+\t\treturn 0;\n+\n \tif (msg_severity == FSCK_FATAL)\n \t\tmsg_severity = FSCK_ERROR;\n \ndiff --git a/fsck.h b/fsck.h\nindex 4349860..7be6c50 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -3,6 +3,7 @@\n \n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n+#define FSCK_IGNORE 3\n \n struct fsck_options;\n \ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 0b6af82..9e4e77b 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -133,7 +133,12 @@ test_expect_success 'push with receive.fsck.severity = missing-email=warn' '\n \tgit --git-dir=dst/.git config \\\n \t\treceive.fsck.severity missing-email=warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missing-email\" act\n+\tgrep \"missing-email\" act &&\n+\tgit --git-dir=dst/.git branch -D bogus &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.severity missing-email=ignore,bad-date=warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\ttest_must_fail grep \"missing-email\" act\n '\n \n test_expect_success \\\n-- \n2.2.0.33.gc18b867\n"},{"id":"255479","messageId":"5e2a68e7e8de3d66f91b95408344c50cfb9b4b6c.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 16/19] fsck: Support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:06:44Z","receivedAt":"2015-01-31T21:06:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already have support in `git receive-pack` to deal with some legacy\nrepositories which have non-fatal issues.\n\nLet's make `git fsck` itself useful with such repositories, too, by\nallowing users to ignore known issues, or at least demote those issues\nto mere warnings.\n\nExample: `git -c fsck.severity=missing-email=ignore fsck` would hide\nproblems with missing emails in author, committer and tagger lines.\n\nIn the same spirit that `git receive-pack`'s usage of the fsck machinery\ndiffers from `git fsck`'s – some of the non-fatal warnings in `git fsck`\nare fatal with `git receive-pack` when receive.fsckObjects = true, for\nexample – we strictly separate the fsck.severity from the\nreceive.fsck.severity settings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 12 ++++++++++++\n builtin/fsck.c           | 12 ++++++++++++\n t/t1450-fsck.sh          | 11 +++++++++++\n 3 files changed, 35 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex f893492..4c0a13d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1208,6 +1208,18 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n+fsck.severity::\n+\tA comma-separated lists of of the form `<id>=<level>` where `<id>`\n+\tdenotes a fsck message ID such as `missing-email` and `<level>` is\n+\tone of `error`, `warn` and `ignore`.\n++\n+For convenience, fsck prefixes the error/warning with the message ID,\n+e.g.  \"missing-email: invalid author/committer line - missing email\" means\n+that setting `fsck.severity = missing-email=ignore` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which cannot be repaired without disruptive changes.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 1f7944c..9e5cc31 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,6 +46,16 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n+static int fsck_config(const char *var, const char *value, void *cb)\n+{\n+\tif (strcmp(var, \"fsck.severity\") == 0) {\n+\t\tfsck_set_severity(&fsck_obj_options, value);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n static void objreport(struct object *obj, const char *severity,\n                       const char *err)\n {\n@@ -638,6 +648,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tgit_config(fsck_config, NULL);\n+\n \tfsck_head_link();\n \tfsck_object_dir(get_object_directory());\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex ea0f216..0f15b74 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -287,6 +287,17 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '\n \tgrep -q \"error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff\" out\n '\n \n+test_expect_success 'force fsck to ignore double author' '\n+\tgit cat-file commit HEAD >basis &&\n+\tsed \"s/^author .*/&,&/\" <basis | tr , \\\\n >multiple-authors &&\n+\tnew=$(git hash-object -t commit -w --stdin <multiple-authors) &&\n+\ttest_when_finished \"remove_object $new\" &&\n+\tgit update-ref refs/heads/bogus \"$new\" &&\n+\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n+\ttest_must_fail git fsck &&\n+\tgit -c fsck.severity=multiple-authors=ignore fsck\n+'\n+\n _bz='\\0'\n _bz5=\"$_bz$_bz$_bz$_bz$_bz\"\n _bz20=\"$_bz5$_bz5$_bz5$_bz5\"\n-- \n2.2.0.33.gc18b867\n"},{"id":"255480","messageId":"72e75da5cb7b99bdad8fedcd0a10d282cf769fca.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:06:51Z","receivedAt":"2015-01-31T21:06:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This option avoids unpacking each and all objects, and just verifies the\nconnectivity. In particular with large repositories, this speeds up the\noperation, at the expense of missing corrupt blobs and ignoring\nunreachable objects, if any.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  7 ++++++-\n builtin/fsck.c             |  7 ++++++-\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 3 files changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 25c431d..b98fb43 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n-\t [--[no-]full] [--strict] [--verbose] [--lost-found]\n+\t [--[no-]full] [--quick] [--strict] [--verbose] [--lost-found]\n \t [--[no-]dangling] [--[no-]progress] [<object>*]\n \n DESCRIPTION\n@@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \tobject pools.  This is now default; you can turn it off\n \twith --no-full.\n \n+--quick::\n+\tCheck only the connectivity of tags, commits and tree objects. By\n+\tavoiding to unpack blobs, this speeds up the operation, at the\n+\texpense of missing corrupt objects.\n+\n --strict::\n \tEnable more strict checking, namely to catch a file mode\n \trecorded with g+w bit set, which was created by older\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 9e5cc31..cf61aad 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -23,6 +23,7 @@ static int show_tags;\n static int show_unreachable;\n static int include_reflogs = 1;\n static int check_full = 1;\n+static int quick;\n static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n@@ -181,6 +182,8 @@ static void check_reachable_object(struct object *obj)\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (has_sha1_pack(obj->sha1))\n \t\t\treturn; /* it is in pack - forget about it */\n+\t\tif (quick && has_sha1_file(obj->sha1))\n+\t\t\treturn;\n \t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n@@ -615,6 +618,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"cache\", &keep_cache_objects, N_(\"make index objects head nodes\")),\n \tOPT_BOOL(0, \"reflogs\", &include_reflogs, N_(\"make reflogs head nodes (default)\")),\n \tOPT_BOOL(0, \"full\", &check_full, N_(\"also consider packs and alternate objects\")),\n+\tOPT_BOOL(0, \"quick\", &quick, N_(\"check only connectivity\")),\n \tOPT_BOOL(0, \"strict\", &check_strict, N_(\"enable more strict checking\")),\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n@@ -651,7 +655,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tfsck_object_dir(get_object_directory());\n+\tif (!quick)\n+\t\tfsck_object_dir(get_object_directory());\n \n \tprepare_alt_odb();\n \tfor (alt = alt_odb_list; alt; alt = alt->next) {\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 0f15b74..b32afaf 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n \ttest_must_fail git -C missing fsck\n '\n \n+test_expect_success 'fsck --quick' '\n+\trm -rf quick &&\n+\tgit init quick &&\n+\t(\n+\t\tcd quick &&\n+\t\ttouch empty &&\n+\t\tgit add empty &&\n+\t\ttest_commit empty &&\n+\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n+\t\trm -f $empty &&\n+\t\techo invalid >$empty &&\n+\t\ttest_must_fail git fsck --strict &&\n+\t\tgit fsck --strict --quick &&\n+\t\ttree=$(git rev-parse HEAD:) &&\n+\t\tsuffix=${tree#??} &&\n+\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n+\t\trm -f $tree &&\n+\t\techo invalid >$tree &&\n+\t\ttest_must_fail git fsck --strict --quick\n+\t)\n+'\n+\n test_done\n-- \n2.2.0.33.gc18b867\n"},{"id":"255481","messageId":"4bd1cadd9b7b958e4d7c3a4ea07aa92a0ae02065.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:06:59Z","receivedAt":"2015-01-31T21:06:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The optional new config option `receive.fsck.skiplist` specifies the path\nto a file listing the names, i.e. SHA-1s, one per line, of objects that\nare to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nThe intended use case is for server administrators to inspect objects\nthat are reported by `git push` as being too problematic to enter the\nrepository, and to add the objects' SHA-1 to a (preferably sorted) file\nwhen the objects are legitimate, i.e. when it is determined that those\nproblematic objects should be allowed to enter the server.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt        |  7 ++++++\n builtin/receive-pack.c          |  8 ++++++\n fsck.c                          | 54 +++++++++++++++++++++++++++++++++++++++++\n fsck.h                          |  1 +\n t/t5504-fetch-receive-strict.sh | 12 +++++++++\n 5 files changed, 82 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 4c0a13d..e685aef 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2157,6 +2157,13 @@ which would not pass pushing when `receive.fsckObjects = true`, allowing\n the host to accept repositories with certain known issues but still catch\n other issues.\n \n+receive.fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 9b7f1a8..f454e65 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -122,6 +122,14 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tstrbuf_addf(&fsck_severity, \"%cskiplist=%s\",\n+\t\t\tfsck_severity.len ? ',' : '=', path);\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsckobjects\") == 0) {\n \t\treceive_fsck_objects = git_config_bool(var, value);\n \t\treturn 0;\ndiff --git a/fsck.c b/fsck.c\nindex c4079ed..046af02 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -8,6 +8,7 @@\n #include \"fsck.h\"\n #include \"refs.h\"\n #include \"utf8.h\"\n+#include \"sha1-array.h\"\n \n #define FSCK_FATAL -1\n #define FSCK_INFO -2\n@@ -119,6 +120,43 @@ static int fsck_msg_severity(enum fsck_msg_id msg_id,\n \treturn severity;\n }\n \n+static void init_skiplist(struct fsck_options *options, const char *path)\n+{\n+\tstatic struct sha1_array skiplist = SHA1_ARRAY_INIT;\n+\tint sorted, fd;\n+\tchar buffer[41];\n+\tunsigned char sha1[20];\n+\n+\tif (options->skiplist)\n+\t\tsorted = options->skiplist->sorted;\n+\telse {\n+\t\tsorted = 1;\n+\t\toptions->skiplist = &skiplist;\n+\t}\n+\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Could not open skip list: %s\", path);\n+\tfor (;;) {\n+\t\tint result = read_in_full(fd, buffer, sizeof(buffer));\n+\t\tif (result < 0)\n+\t\t\tdie_errno(\"Could not read '%s'\", path);\n+\t\tif (!result)\n+\t\t\tbreak;\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n+\t\t\tdie(\"Invalid SHA-1: %s\", buffer);\n+\t\tsha1_array_append(&skiplist, sha1);\n+\t\tif (sorted && skiplist.nr > 1 &&\n+\t\t\t\thashcmp(skiplist.sha1[skiplist.nr - 2],\n+\t\t\t\t\tsha1) > 0)\n+\t\t\tsorted = 0;\n+\t}\n+\tclose(fd);\n+\n+\tif (sorted)\n+\t\tskiplist.sorted = 1;\n+}\n+\n static inline int substrcmp(const char *string, int len, const char *match)\n {\n \tint match_len = strlen(match);\n@@ -151,6 +189,18 @@ void fsck_set_severity(struct fsck_options *options, const char *mode)\n \t\t\tif (mode[equal] == '=' || mode[equal] == ':')\n \t\t\t\tbreak;\n \n+\t\tif (!substrcmp(mode, equal, \"skiplist\")) {\n+\t\t\tchar *path = xstrndup(mode + equal + 1,\n+\t\t\t\tlen - equal - 1);\n+\n+\t\t\tif (equal == len)\n+\t\t\t\tdie(\"skiplist requires a path\");\n+\t\t\tinit_skiplist(options, path);\n+\t\t\tfree(path);\n+\t\t\tmode += len;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tmsg_id = parse_msg_id(mode, equal);\n \n \t\tif (equal == len)\n@@ -706,6 +756,10 @@ static int fsck_tag(struct tag *tag, const char *data,\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n+\tif (options->skiplist &&\n+\t\t\tsha1_array_lookup(options->skiplist, obj->sha1) >= 0)\n+\t\treturn 0;\n+\n \tif (!obj)\n \t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \ndiff --git a/fsck.h b/fsck.h\nindex 7be6c50..cae280e 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -29,6 +29,7 @@ struct fsck_options {\n \tfsck_error error_func;\n \tunsigned strict:1;\n \tint *msg_severity;\n+\tstruct sha1_array *skiplist;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 9e4e77b..7881e17 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,6 +123,18 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n+test_expect_success 'push with receive.fsck.skiplist' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.skiplist SKIP &&\n+\techo $commit > dst/.git/SKIP &&\n+\tgit push --porcelain dst bogus\n+'\n+\n test_expect_success 'push with receive.fsck.severity = missing-email=warn' '\n \tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n-- \n2.2.0.33.gc18b867\n"},{"id":"255482","messageId":"efb672505c7f15b7e5775f1637a2ae7cfe30edeb.1422737997.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v4 19/19] fsck: support ignoring objects in `git fsck` via fsck.skiplist","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-01-31T21:07:06Z","receivedAt":"2015-01-31T21:07:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Identical to support in `git receive-pack for the config option\n`receive.fsck.skiplist`, we now support ignoring given objects in\n`git fsck` via `fsck.skiplist` altogether.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt |  7 +++++++\n builtin/fsck.c           | 10 ++++++++++\n 2 files changed, 17 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex e685aef..93c43d5 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1220,6 +1220,13 @@ that setting `fsck.severity = missing-email=ignore` will hide that issue.\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n \n+fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex cf61aad..81570d8 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -54,6 +54,16 @@ static int fsck_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n \treturn git_default_config(var, value, cb);\n }\n \n-- \n2.2.0.33.gc18b867\n"},{"id":"255510","messageId":"060e1e6a5530dfb311074b0aa854b281@www.dscho.org","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v4 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-02-02T11:41:56Z","receivedAt":"2015-02-02T11:41:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi all (in particular Junio),\n\nOn 2015-01-31 22:04, Johannes Schindelin wrote:\n\n> [...] switch to fsck.severity to address Michael's\n> concerns that letting fsck.(error|warn|ignore)'s comma-separated lists\n> possibly overriding each other partially;\n\nHaving participated in the CodingStyle thread, I came to the conclusion that the fsck.severity solution favors syntax over intuitiveness.\n\nTherefore, I would like to support the case for `fsck.level.missingAuthor` (note that there is an extra \".level.\" in contrast to earlier suggestions).\n\nThe benefits:\n\n- it is very, very easy to understand\n\n- cumulative settings are intuitively cumulative, i.e. setting `fsck.level.missingAuthor` will leave `fsck.level.invalidEmail` completely unaffected\n\n- it is very easy to enquire and set the levels via existing `git config` calls\n\nNow, there is one downside, but *only* if we ignore Postel's law.\n\nPostel's law (\"be lenient in what you accept as input, but strict in your output\") would dictate that our message ID parser accept both \"missing-author\" and \"missingAuthor\" if we follow the inconsistent practice of using lowercase-dashed keys on the command-line but CamelCased ones in the config.\n\nHowever, earlier Junio made very clear that the parser is required to fail to parse \"missing-author\" in the config, and to fail to parse \"missingAuthor\" on the command-line.\n\nTherefore, the design I recommend above will require two, minimally different parsers for essentially the same thing.\n\nIMHO this is a downside that is by far outweighed by the ease of use of the new feature, therefore I am willing to bear the burden of implementation.\n\nDo you agree?\n\nCiao,\nDscho\n"},{"id":"255511","messageId":"54CF70DA.5090506@alum.mit.edu","threadId":"38144","inReplyTo":"060e1e6a5530dfb311074b0aa854b281@www.dscho.org","subject":"Re: [PATCH v4 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2015-02-02T12:43:06Z","receivedAt":"2015-02-02T12:43:06Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/02/2015 12:41 PM, Johannes Schindelin wrote:\n> Hi all (in particular Junio),\n> \n> On 2015-01-31 22:04, Johannes Schindelin wrote:\n> \n>> [...] switch to fsck.severity to address Michael's concerns that\n>> letting fsck.(error|warn|ignore)'s comma-separated lists possibly\n>> overriding each other partially;\n> \n> Having participated in the CodingStyle thread, I came to the\n> conclusion that the fsck.severity solution favors syntax over\n> intuitiveness.\n> \n> Therefore, I would like to support the case for\n> `fsck.level.missingAuthor` (note that there is an extra \".level.\" in\n> contrast to earlier suggestions).\n\nWhy \"level\"?\n\n> The benefits:\n> \n> - it is very, very easy to understand\n> \n> - cumulative settings are intuitively cumulative, i.e. setting\n> `fsck.level.missingAuthor` will leave `fsck.level.invalidEmail`\n> completely unaffected\n> \n> - it is very easy to enquire and set the levels via existing `git\n> config` calls\n> \n> Now, there is one downside, but *only* if we ignore Postel's law.\n> \n> Postel's law (\"be lenient in what you accept as input, but strict in\n> your output\") would dictate that our message ID parser accept both\n> \"missing-author\" and \"missingAuthor\" if we follow the inconsistent\n> practice of using lowercase-dashed keys on the command-line but\n> CamelCased ones in the config.\n> \n> However, earlier Junio made very clear that the parser is required to\n> fail to parse \"missing-author\" in the config, and to fail to parse\n> \"missingAuthor\" on the command-line.\n> \n> Therefore, the design I recommend above will require two, minimally\n> different parsers for essentially the same thing.\n> \n> IMHO this is a downside that is by far outweighed by the ease of use\n> of the new feature, therefore I am willing to bear the burden of\n> implementation.\n\nI again encourage you to consider skipping the implementation of\ncommand-line options entirely. It's not like users are going to want to\nuse different options for different invocations. Let them use\n\n    git -c fsck.level.missingAuthor=ignore fsck\n\nif they really want to play around, then\n\n    git config fsck.level.missingAuthor ignore\n\nto make it permanent. After that they will never have to worry about\nthat option again.\n\nAnd Postel needn't be offended :-)\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"255514","messageId":"d5ebe9aeb5f4dd5cbe8673ae2076ba04@www.dscho.org","threadId":"38144","inReplyTo":"54CF70DA.5090506@alum.mit.edu","subject":"Re: [PATCH v4 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-02-02T16:48:38Z","receivedAt":"2015-02-02T16:48:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Michael,\n\nOn 2015-02-02 13:43, Michael Haggerty wrote:\n> On 02/02/2015 12:41 PM, Johannes Schindelin wrote:\n>> Hi all (in particular Junio),\n>>\n>> On 2015-01-31 22:04, Johannes Schindelin wrote:\n>>\n>>> [...] switch to fsck.severity to address Michael's concerns that\n>>> letting fsck.(error|warn|ignore)'s comma-separated lists possibly\n>>> overriding each other partially;\n>>\n>> Having participated in the CodingStyle thread, I came to the\n>> conclusion that the fsck.severity solution favors syntax over\n>> intuitiveness.\n>>\n>> Therefore, I would like to support the case for\n>> `fsck.level.missingAuthor` (note that there is an extra \".level.\" in\n>> contrast to earlier suggestions).\n> \n> Why \"level\"?\n\n\"Severity level\", or \"error level\". Maybe \".severity.\" would be better?\n\n>> The benefits:\n>>\n>> - it is very, very easy to understand\n>>\n>> - cumulative settings are intuitively cumulative, i.e. setting\n>> `fsck.level.missingAuthor` will leave `fsck.level.invalidEmail`\n>> completely unaffected\n>>\n>> - it is very easy to enquire and set the levels via existing `git\n>> config` calls\n>>\n>> Now, there is one downside, but *only* if we ignore Postel's law.\n>>\n>> Postel's law (\"be lenient in what you accept as input, but strict in\n>> your output\") would dictate that our message ID parser accept both\n>> \"missing-author\" and \"missingAuthor\" if we follow the inconsistent\n>> practice of using lowercase-dashed keys on the command-line but\n>> CamelCased ones in the config.\n>>\n>> However, earlier Junio made very clear that the parser is required to\n>> fail to parse \"missing-author\" in the config, and to fail to parse\n>> \"missingAuthor\" on the command-line.\n>>\n>> Therefore, the design I recommend above will require two, minimally\n>> different parsers for essentially the same thing.\n>>\n>> IMHO this is a downside that is by far outweighed by the ease of use\n>> of the new feature, therefore I am willing to bear the burden of\n>> implementation.\n> \n> I again encourage you to consider skipping the implementation of\n> command-line options entirely. It's not like users are going to want to\n> use different options for different invocations. Let them use\n> \n>     git -c fsck.level.missingAuthor=ignore fsck\n> \n> if they really want to play around, then\n> \n>     git config fsck.level.missingAuthor ignore\n> \n> to make it permanent. After that they will never have to worry about\n> that option again.\n\nUnfortunately, I have to pass the `receive.fsck.*` settings from `git-receive-pack` to `git-unpack-objects` or `git-index-pack` via the command-line, because it is `git-receive-pack` that consumes the config setting, but it is one of `git-unpack-objects` and `git-index-pack` that has to act on it...\n\n> And Postel needn't be offended :-)\n\n;-)\n\nCiao,\nDscho\n"},{"id":"255573","messageId":"54D0E50A.5030601@alum.mit.edu","threadId":"38144","inReplyTo":"d5ebe9aeb5f4dd5cbe8673ae2076ba04@www.dscho.org","subject":"Re: [PATCH v4 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2015-02-03T15:11:06Z","receivedAt":"2015-02-03T15:11:06Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 02/02/2015 05:48 PM, Johannes Schindelin wrote:\n> On 2015-02-02 13:43, Michael Haggerty wrote:\n>> On 02/02/2015 12:41 PM, Johannes Schindelin wrote:\n>>> Hi all (in particular Junio),\n>>>\n>>> On 2015-01-31 22:04, Johannes Schindelin wrote:\n>>>\n>>>> [...] switch to fsck.severity to address Michael's concerns that\n>>>> letting fsck.(error|warn|ignore)'s comma-separated lists possibly\n>>>> overriding each other partially;\n>>>\n>>> Having participated in the CodingStyle thread, I came to the\n>>> conclusion that the fsck.severity solution favors syntax over\n>>> intuitiveness.\n>>>\n>>> Therefore, I would like to support the case for\n>>> `fsck.level.missingAuthor` (note that there is an extra \".level.\" in\n>>> contrast to earlier suggestions).\n>>\n>> Why \"level\"?\n> \n> \"Severity level\", or \"error level\". Maybe \".severity.\" would be better?\n\nSorry, I should have been clearer. I understand why the word \"level\"\nmakes sense, as opposed to, say, \"peanut-butter\". What I don't\nunderstand is why a middle word is needed at all. In the config file it\nwill look like\n\n[fsck \"level\"]\n        missingAuthor = error\n\n, which looks funny. \"level\" is a constant, so it seems superfluous.\n\nIf anything, it might be more useful to allow an optional middle word to\nallow the strictness level to be adjusted based on which command\nencounters the problem. For example, if you want to tolerate existing\ncommits that have missing authors, but not allow any new ones to be\npushed, you could set\n\n[strictness]\n        missingAuthor = ignore\n[strictness \"receive-pack\"]\n        missingAuthor = error\n\n(There's probably a better word than \"strictness\", but you get the idea.)\n\n>>> The benefits:\n>>>\n>>> - it is very, very easy to understand\n>>>\n>>> - cumulative settings are intuitively cumulative, i.e. setting\n>>> `fsck.level.missingAuthor` will leave `fsck.level.invalidEmail`\n>>> completely unaffected\n>>>\n>>> - it is very easy to enquire and set the levels via existing `git\n>>> config` calls\n>>>\n>>> Now, there is one downside, but *only* if we ignore Postel's law.\n>>>\n>>> Postel's law (\"be lenient in what you accept as input, but strict in\n>>> your output\") would dictate that our message ID parser accept both\n>>> \"missing-author\" and \"missingAuthor\" if we follow the inconsistent\n>>> practice of using lowercase-dashed keys on the command-line but\n>>> CamelCased ones in the config.\n>>>\n>>> However, earlier Junio made very clear that the parser is required to\n>>> fail to parse \"missing-author\" in the config, and to fail to parse\n>>> \"missingAuthor\" on the command-line.\n>>>\n>>> Therefore, the design I recommend above will require two, minimally\n>>> different parsers for essentially the same thing.\n>>>\n>>> IMHO this is a downside that is by far outweighed by the ease of use\n>>> of the new feature, therefore I am willing to bear the burden of\n>>> implementation.\n>>\n>> I again encourage you to consider skipping the implementation of\n>> command-line options entirely. It's not like users are going to want to\n>> use different options for different invocations. Let them use\n>>\n>>     git -c fsck.level.missingAuthor=ignore fsck\n>>\n>> if they really want to play around, then\n>>\n>>     git config fsck.level.missingAuthor ignore\n>>\n>> to make it permanent. After that they will never have to worry about\n>> that option again.\n> \n> Unfortunately, I have to pass the `receive.fsck.*` settings from\n> `git-receive-pack` to `git-unpack-objects` or `git-index-pack` via the\n> command-line, because it is `git-receive-pack` that consumes the config\n> setting, but it is one of `git-unpack-objects` and `git-index-pack` that\n> has to act on it...\n\nWouldn't that work automatically via the GIT_CONFIG_PARAMETERS\nmechanism? If I run\n\n    git -c foo.bar=baz $CMD\n\n, then git-$CMD is invoked with GIT_CONFIG_PARAMETERS set to\n\"'foo.bar=baz'\", which causes child processes to treat that value as a\nconfiguration setting. I don't have a lot of experience with this but I\nthink it should do what you need.\n\nMichael\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"255577","messageId":"2313893ae2200229ccdeb8f71b629445@www.dscho.org","threadId":"38144","inReplyTo":"54D0E50A.5030601@alum.mit.edu","subject":"Re: [PATCH v4 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-02-03T16:33:39Z","receivedAt":"2015-02-03T16:33:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Michael,\n\nOn 2015-02-03 16:11, Michael Haggerty wrote:\n> On 02/02/2015 05:48 PM, Johannes Schindelin wrote:\n>> On 2015-02-02 13:43, Michael Haggerty wrote:\n>>> On 02/02/2015 12:41 PM, Johannes Schindelin wrote:\n>>>> Hi all (in particular Junio),\n>>>>\n>>>> On 2015-01-31 22:04, Johannes Schindelin wrote:\n>>>>\n>>>>> [...] switch to fsck.severity to address Michael's concerns that\n>>>>> letting fsck.(error|warn|ignore)'s comma-separated lists possibly\n>>>>> overriding each other partially;\n>>>>\n>>>> Having participated in the CodingStyle thread, I came to the\n>>>> conclusion that the fsck.severity solution favors syntax over\n>>>> intuitiveness.\n>>>>\n>>>> Therefore, I would like to support the case for\n>>>> `fsck.level.missingAuthor` (note that there is an extra \".level.\" in\n>>>> contrast to earlier suggestions).\n>>>\n>>> Why \"level\"?\n>>\n>> \"Severity level\", or \"error level\". Maybe \".severity.\" would be better?\n> \n> Sorry, I should have been clearer. I understand why the word \"level\"\n> makes sense, as opposed to, say, \"peanut-butter\". What I don't\n> understand is why a middle word is needed at all. In the config file it\n> will look like\n> \n> [fsck \"level\"]\n>         missingAuthor = error\n> \n> , which looks funny. \"level\" is a constant, so it seems superfluous.\n> \n> If anything, it might be more useful to allow an optional middle word to\n> allow the strictness level to be adjusted based on which command\n> encounters the problem. For example, if you want to tolerate existing\n> commits that have missing authors, but not allow any new ones to be\n> pushed, you could set\n> \n> [strictness]\n>         missingAuthor = ignore\n> [strictness \"receive-pack\"]\n>         missingAuthor = error\n> \n> (There's probably a better word than \"strictness\", but you get the idea.)\n\nAh. Well, the idea of the middle constant is to separate the severity levels from all other fsck (or receive.fsck) settings. The 'fsck.skiplist' setting that I introduce in this patch series, for example, looks pretty much the same as 'fsck.missingauthor', but they have different roles.\n\nThis becomes important when I want to catch obvious problems such as 'fsck.missingautor': if I have an extra '.level', I can be certain that it is a typo rather than a config setting unrelated to the severity levels.\n\n>>>> The benefits:\n>>>>\n>>>> - it is very, very easy to understand\n>>>>\n>>>> - cumulative settings are intuitively cumulative, i.e. setting\n>>>> `fsck.level.missingAuthor` will leave `fsck.level.invalidEmail`\n>>>> completely unaffected\n>>>>\n>>>> - it is very easy to enquire and set the levels via existing `git\n>>>> config` calls\n>>>>\n>>>> Now, there is one downside, but *only* if we ignore Postel's law.\n>>>>\n>>>> Postel's law (\"be lenient in what you accept as input, but strict in\n>>>> your output\") would dictate that our message ID parser accept both\n>>>> \"missing-author\" and \"missingAuthor\" if we follow the inconsistent\n>>>> practice of using lowercase-dashed keys on the command-line but\n>>>> CamelCased ones in the config.\n>>>>\n>>>> However, earlier Junio made very clear that the parser is required to\n>>>> fail to parse \"missing-author\" in the config, and to fail to parse\n>>>> \"missingAuthor\" on the command-line.\n>>>>\n>>>> Therefore, the design I recommend above will require two, minimally\n>>>> different parsers for essentially the same thing.\n>>>>\n>>>> IMHO this is a downside that is by far outweighed by the ease of use\n>>>> of the new feature, therefore I am willing to bear the burden of\n>>>> implementation.\n>>>\n>>> I again encourage you to consider skipping the implementation of\n>>> command-line options entirely. It's not like users are going to want to\n>>> use different options for different invocations. Let them use\n>>>\n>>>     git -c fsck.level.missingAuthor=ignore fsck\n>>>\n>>> if they really want to play around, then\n>>>\n>>>     git config fsck.level.missingAuthor ignore\n>>>\n>>> to make it permanent. After that they will never have to worry about\n>>> that option again.\n>>\n>> Unfortunately, I have to pass the `receive.fsck.*` settings from\n>> `git-receive-pack` to `git-unpack-objects` or `git-index-pack` via the\n>> command-line, because it is `git-receive-pack` that consumes the config\n>> setting, but it is one of `git-unpack-objects` and `git-index-pack` that\n>> has to act on it...\n> \n> Wouldn't that work automatically via the GIT_CONFIG_PARAMETERS\n> mechanism? If I run\n> \n>     git -c foo.bar=baz $CMD\n> \n> , then git-$CMD is invoked with GIT_CONFIG_PARAMETERS set to\n> \"'foo.bar=baz'\", which causes child processes to treat that value as a\n> configuration setting. I don't have a lot of experience with this but I\n> think it should do what you need.\n\nThis is true, but please remember that the receive.fsck.* settings should be heeded by index-pack/unpack-objects *only* if one of the latter programs is called by receive-pack. It would therefore be a little funny (or wrong, depending on your point of view) if, say, index-pack would respect the receive.fsck.* settings.\n\nThat is why receive-pack adds a `--strict` command-line option when receive.fsckobjects is set to true instead of letting index-pack (or unpack-objects) look at the config variable receive.fsckobjects itself.\n\nIn the same spirit, I extend the `--strict` command-line option with an optional list of severity level overrides (e.g. `--strict=missing-author=ignore,...`) if receive-pack was configured to override those levels.\n\nNow, as this optional argument is intended for internal use only, we could declare that it is okay to pass CamelCased stuff there, even if it disagrees with our command-line option conventions. If it is not okay, I will have to write a rewriter that consumes the CamelCased config settings and rewrites them as lowercase-dashed settings, to be passed to index-pack or unpack-objects, which in turn parses the lowercase-dashed settings.\n\nIt might be seen as unnecessarily complicated, which is why I argued in favor of letting the parser accept both forms (which incidentally would heed Postel's law, too). But I understand if Junio does not want that, because it technically violates the conventions he wants to see established. I just need a definitive statement which way to go so I can implement it.\n\nCiao,\nDscho\n"},{"id":"255600","messageId":"xmqqegq6nxmh.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"2313893ae2200229ccdeb8f71b629445@www.dscho.org","subject":"Re: [PATCH v4 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-02-04T03:50:30Z","receivedAt":"2015-02-04T03:50:30Z","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>> [fsck \"level\"]\n>>         missingAuthor = error\n>> \n>> , which looks funny. \"level\" is a constant, so it seems superfluous.\n\nYes, it is superfluous, but is one way to avoid the ambiguity with\n\"skiplist\".  Structuring it like this would not be so bad, either,\nthough.\n\n\t[fsck]\n        \terror = missingAuthor[, other kinds of errors...]\n\nA small set like {ignore, warn, error} is easily maintainable not to\nconflict with \"skiplist\" and others.\n\nSo that \"avoid ambiguity with skiplist\" does not favor either choice\nin any significant way.\n\n> This becomes important when I want to catch obvious problems such as\n> fsck.missingautor': if I have an extra '.level', I can be certain that\n> it is a typo rather than a config setting unrelated to the severity\n> levels.\n\n\"[fsck] error = missingAutor\" would let you catch the typo in a similar\nway with the same context clue, so this does not decide which is\nbetter, either.\n\nOne clear benefit I can see in it is that you can do\n\n\tgit config fsck.level.missingAuthor\n\nin scripts that wants to learn the current setting for a single\nvariable.  With \"fsck.error=missingAuthor[,other kinds]\", you would\ninstead have to do a bit more silly post-processing\n\n\tgit config -l | sed -ne '\n        \t/^fsck\\./{\n\t\t\t# make it \"var=,token1,token2,token3,\"\n\t\t\ts/=/=,/\n                        s/$/,/\n                        s/[ \t]*//g\n\t\t\ts|^fsck\\.\\([^=]*\\)=.*,missingAuthor,.*|\\1|p\n\t\t}\n\t' | tail -n 1\n\nto grab the last fsck.{error,ignore,...}= thing that has the token\n(I personally do not think the latter is so bad, though).\n\nI wonder if\n\n\t[fsckError]\n        \tmissingAuthor = error\n                missingTagger = warn\n\nwouldn't be a better way, though.  We'd keep the easier scripting\n\n\tgit config fsckError.missingTagger\n\nThere is nothing that says that the top-level grouping must match\nthe Git subcommand name.  Nothing says that one Git subcommand can\nown at most one namespace, either.  Nothing stops us from reserving\nfsckError top-level namespace for variable name collision avoidance\nwith other fsck.* variables, if that gives us a better system.\n\n>>> Unfortunately, I have to pass the `receive.fsck.*` settings from\n>>> `git-receive-pack` to `git-unpack-objects` or `git-index-pack` via the\n>>> command-line, because it is `git-receive-pack` that consumes the config\n>>> setting, but it is one of `git-unpack-objects` and `git-index-pack` that\n>>> has to act on it...\n\nBut receive-pack at some point decides what, if anything, needs to\nbe passed when invoking unpack-objects, or index-pack, no?  Why is\nit hard to pass \"-c var=val\" at the beginning where it would have\npassed \"--strictness=var=val\" at the end?\n\n>> Wouldn't that work automatically via the GIT_CONFIG_PARAMETERS\n>> mechanism? If I run\n>> \n>>     git -c foo.bar=baz $CMD\n>> \n>> , then git-$CMD is invoked with GIT_CONFIG_PARAMETERS set to\n>> \"'foo.bar=baz'\", which causes child processes to treat that value as a\n>> configuration setting. I don't have a lot of experience with this but I\n>> think it should do what you need.\n>\n> This is true, but please remember that the receive.fsck.* settings\n> should be heeded by index-pack/unpack-objects *only* if one of the\n> latter programs is called by receive-pack. It would therefore be a\n> little funny (or wrong, depending on your point of view) if, say,\n> index-pack would respect the receive.fsck.* settings.\n\nThat means it would be fine if receive-pack invokes (when it sees\nreceive.fsck.severity=missingAuthor=error,missingTagger=warn config\nmeant for it and was told with receive.fsckObjects to check the\nincoming objects) a command line like this:\n\n\tgit -c fsckError.missingAuthor=error \\\n            -c fsckError.missingTagger=warn \\\n\t\tindex-pack $args...\n\n(or whatever variable names and name structure we settle on).  And\nthe index-pack command does not have to even know there are\nreceive.fsck.* variables at all, no?\n\nAnother way to do that may be for receive-pack to invoke\n\n\tgit index-pack --use-fsck-severity=receive.fsck $args...\n\nto instruct it to look at receive.fsck.* variables, again when and\nonly when receive-pack wants to do so.  I think either way would be\nfine, as this communication is an internal implementation detail\nbetween receive-pack and index-pack and is not meant to be exposed\nto the end users anyway.\n"},{"id":"255603","messageId":"d268ca769c3813cdb600672c40e2c484@www.dscho.org","threadId":"38144","inReplyTo":"xmqqegq6nxmh.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v4 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-02-04T11:02:45Z","receivedAt":"2015-02-04T11:02:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-02-04 04:50, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>>> [fsck \"level\"]\n>>>         missingAuthor = error\n>>>\n>>> , which looks funny. \"level\" is a constant, so it seems superfluous.\n> \n> Yes, it is superfluous, but is one way to avoid the ambiguity with\n> \"skiplist\".  Structuring it like this would not be so bad, either,\n> though.\n> \n> \t[fsck]\n>         \terror = missingAuthor[, other kinds of errors...]\n> \n> A small set like {ignore, warn, error} is easily maintainable not to\n> conflict with \"skiplist\" and others.\n\nBut you augmented the case against the {ignore, warn, error} structure with this yourself:\n\n> With \"fsck.error=missingAuthor[,other kinds]\", you would\n> instead have to do a bit more silly post-processing\n> \n> \tgit config -l | sed -ne '\n>         \t/^fsck\\./{\n> \t\t\t# make it \"var=,token1,token2,token3,\"\n> \t\t\ts/=/=,/\n>                         s/$/,/\n>                         s/[ \t]*//g\n> \t\t\ts|^fsck\\.\\([^=]*\\)=.*,missingAuthor,.*|\\1|p\n> \t\t}\n> \t' | tail -n 1\n\nIf a script to determine the current state of affairs is already convoluted, how complicated would it be for users of this feature? No, let's go with Michael's suggestion and have a much more elegant (read: easy to grasp) configuration.\n\nI see that the extra \".level.\" is universally frowned on, and therefore am convinced that it would be bad to add it.\n\nAs to the conflict with \"skiplist\", by your own argument (\"small set ... is easily maintainable\") it is not a problem at all.\n\n>> This becomes important when I want to catch obvious problems such as\n>> fsck.missingautor': if I have an extra '.level', I can be certain that\n>> it is a typo rather than a config setting unrelated to the severity\n>> levels.\n> \n> \"[fsck] error = missingAutor\" would let you catch the typo in a similar\n> way with the same context clue, so this does not decide which is\n> better, either.\n\nNo, you are correct, this does not decide it. What decides it that `fsck.missingAuthor = error` is better than `fsck.error = missingAuthor` is that the user does not need to wrap her head around the significance of the order of mutually overriding `error`, `warn` and `ignore` settings in the former.\n\nAs to the typo handling, you are absolutely correct that it is easy to handle \"skiplist\" first and then expect all other fsck.* settings (or receive.fsck.* settings) to match a message ID. To make things even smoother, I think it would make most sense to only *warn* about typos instead of *erroring out*.\n\n> I wonder if\n> \n> \t[fsckError]\n>         \tmissingAuthor = error\n>                 missingTagger = warn\n\nHmm. I am not so sure that `fsckError` is the correct term. After all, we can also upgrade warnings to errors, not only demote errors to warnings. So I guess the `fsck.*` naming scheme is not so bad after all!\n\n>>>> Unfortunately, I have to pass the `receive.fsck.*` settings from\n>>>> `git-receive-pack` to `git-unpack-objects` or `git-index-pack` via the\n>>>> command-line, because it is `git-receive-pack` that consumes the config\n>>>> setting, but it is one of `git-unpack-objects` and `git-index-pack` that\n>>>> has to act on it...\n> \n> But receive-pack at some point decides what, if anything, needs to\n> be passed when invoking unpack-objects, or index-pack, no?  Why is\n> it hard to pass \"-c var=val\" at the beginning where it would have\n> passed \"--strictness=var=val\" at the end?\n\nIt is not hard, it is just inelegant: 1) we would have to introduce a new function to handle the config settings in `unpack-objects`, 2) the command-line would be longer (for what benefit?), and 3) index-pack uses run_command() to launch the children [*1*] and wouldn't you find it super-ugly if that argv started with a \"-c\", \"...\"? I would.\n\nWe already have command-line handling in both index-pack and unpack-objects (and config handling only in the former), we already have --strict handling in particular, and arguably the severity levels are tightly connected to that --strict option that at least in my opinion it makes sense to understand that the severity levels are optional parameters to that command-line option.\n\nSo in short, I maintain that passing the options via the config mechanism would just make things more complicated, and in no way better.\n\n>>> Wouldn't that work automatically via the GIT_CONFIG_PARAMETERS\n>>> mechanism? If I run\n>>>\n>>>     git -c foo.bar=baz $CMD\n>>>\n>>> , then git-$CMD is invoked with GIT_CONFIG_PARAMETERS set to\n>>> \"'foo.bar=baz'\", which causes child processes to treat that value as a\n>>> configuration setting. I don't have a lot of experience with this but I\n>>> think it should do what you need.\n>>\n>> This is true, but please remember that the receive.fsck.* settings\n>> should be heeded by index-pack/unpack-objects *only* if one of the\n>> latter programs is called by receive-pack. It would therefore be a\n>> little funny (or wrong, depending on your point of view) if, say,\n>> index-pack would respect the receive.fsck.* settings.\n> \n> That means it would be fine if receive-pack invokes (when it sees\n> receive.fsck.severity=missingAuthor=error,missingTagger=warn config\n> meant for it and was told with receive.fsckObjects to check the\n> incoming objects) a command line like this:\n> \n> \tgit -c fsckError.missingAuthor=error \\\n>             -c fsckError.missingTagger=warn \\\n> \t\tindex-pack $args...\n> \n> (or whatever variable names and name structure we settle on).  And\n> the index-pack command does not have to even know there are\n> receive.fsck.* variables at all, no?\n\nIt does not know that at all already, so yes: I agree. It does not even need to know that those settings were config variables. Even better: it should not let config settings interfere with the settings receive-pack wants index-pack to use.\n\nSo there you have it, another reason why passing the levels via command-line options is conceptually much more sound than passing them via fake config settings.\n\n> I think either way would be fine, as this communication is an internal implementation detail between receive-pack and index-pack and is not meant to be exposed to the end users anyway.\n\nYeah, I think we really used enough time to discuss something as unimportant as this detail.\n\nJust to make sure: are you okay with passing CamelCased settings (e.g. `--strict=missingAuthor=warn,...`) as part of that internal communication between receive-pack and index-pack/unpack-objects? That is my preference by now.\n\nCiao,\nDscho\n\nFootnote *1*: https://github.com/msysgit/git/blob/c47d6ec67188cec2a782bc245aa7df4e3cbdbc01/builtin/receive-pack.c#L992-L1001\n"},{"id":"264215","messageId":"cover.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1422737997.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:07:10Z","receivedAt":"2015-06-18T20:07:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At the moment, the git-fsck's integrity checks are targeted toward the\nend user, i.e. the error messages are really just messages, intended for\nhuman consumption.\n\nUnder certain circumstances, some of those errors should be allowed to\nbe turned into mere warnings, though, because the cost of fixing the\nissues might well be larger than the cost of carrying those flawed\nobjects. For example, when an already-public repository contains a\ncommit object with two authors for years, it does not make sense to\nforce the maintainer to rewrite the history, affecting all contributors\nnegatively by forcing them to update.\n\nThis branch introduces an internal fsck API to be able to turn some of\nthe errors into warnings, and to make it easier to call the fsck\nmachinery from elsewhere in general.\n\nI am proud to report that this work has been sponsored by GitHub.\n\nChanges since v4 (sorry for the long delay):\n\n- the config settings' convention changed as discussed. Example:\n  `fsck.severity.warn = missing-author` is now\n  `fsck.missingAuthor = warn` (or `fsck.missingauthor = warn` because\n  config settings are traditionally case-insensitive). As a consequence,\n  the command-line parameter passed on to `index-pack` and\n  `unpack-objects` also uses camelCased values.\n\n- previously, we errored out when encountering an unknown message id,\n  now we warn instead.\n\n- I now use `msg_type` consistently where I used `severity` before because\n  it appears clearer to me\n\n- the skiplist handling is now done only in the error case, for enhanced\n  performance. While at it, a potential segmentation fault was fixed when\n  a NULL object was dereferenced to be looked up in the skiplist.\n\nInterdiff below the diffstat. It's huge. Sorry.\n\nJohannes Schindelin (19):\n  fsck: Introduce fsck options\n  fsck: Introduce identifiers for fsck messages\n  fsck: Provide a function to parse fsck message IDs\n  fsck: Offer a function to demote fsck errors to warnings\n  fsck (receive-pack): Allow demoting errors to warnings\n  fsck: Report the ID of the error/warning\n  fsck: Make fsck_ident() warn-friendly\n  fsck: Make fsck_commit() warn-friendly\n  fsck: Handle multiple authors in commits specially\n  fsck: Make fsck_tag() warn-friendly\n  fsck: Add a simple test for receive.fsck.<msg-id>\n  fsck: Disallow demoting grave fsck errors to warnings\n  fsck: Optionally ignore specific fsck issues completely\n  fsck: Allow upgrading fsck warnings to errors\n  fsck: Document the new receive.fsck.<msg-id> options\n  fsck: Support demoting errors to warnings\n  fsck: Introduce `git fsck --quick`\n  fsck: git receive-pack: support excluding objects from fsck'ing\n  fsck: support ignoring objects in `git fsck` via fsck.skiplist\n\n Documentation/config.txt        |  39 +++\n Documentation/git-fsck.txt      |   7 +-\n builtin/fsck.c                  |  75 ++++--\n builtin/index-pack.c            |  13 +-\n builtin/receive-pack.c          |  25 +-\n builtin/unpack-objects.c        |  16 +-\n fsck.c                          | 553 +++++++++++++++++++++++++++++++---------\n fsck.h                          |  31 ++-\n t/t1450-fsck.sh                 |  37 ++-\n t/t5302-pack-index.sh           |   2 +-\n t/t5504-fetch-receive-strict.sh |  51 ++++\n 11 files changed, 686 insertions(+), 163 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex b5b1a22..5aba63a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1250,14 +1250,13 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n-fsck.severity::\n-\tA comma-separated lists of of the form `<id>=<level>` where `<id>`\n-\tdenotes a fsck message ID such as `missing-email` and `<level>` is\n-\tone of `error`, `warn` and `ignore`.\n+fsck.<msg-id>::\n+\tAllows overriding the message type (error, warn or ignore) of a\n+\tspecific message ID such as `missingemail`.\n +\n For convenience, fsck prefixes the error/warning with the message ID,\n-e.g.  \"missing-email: invalid author/committer line - missing email\" means\n-that setting `fsck.severity = missing-email=ignore` will hide that issue.\n+e.g.  \"missingemail: invalid author/committer line - missing email\" means\n+that setting `fsck.missingemail = ignore` will hide that issue.\n +\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n@@ -2224,15 +2223,14 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n-receive.fsck.severity::\n+receive.fsck.<msg-id>::\n \tWhen `receive.fsckObjects` is set to true, errors can be switched\n-\tto warnings and vice versa by configuring the `receive.fsck.severity`\n-\tsetting. These settings contain comma-separated lists of the form\n-\t`<id>=<level>` where the `<id>` is the fsck message ID and the level\n+\tto warnings and vice versa by configuring the `receive.fsck.<msg-id>`\n+\tsetting where the `<msg-id>` is the fsck message ID and the value\n \tis one of `error`, `warn` or `ignore`. For convenience, fsck prefixes\n-\tthe error/warning with the message ID, e.g. \"missing-email: invalid\n+\tthe error/warning with the message ID, e.g. \"missingemail: invalid\n \tauthor/committer line - missing email\" means that setting\n-\t`receive.fsck.severity = missing-email=ignore` will hide that issue.\n+\t`receive.fsck.missingemail = ignore` will hide that issue.\n +\n This feature is intended to support working with legacy repositories\n which would not pass pushing when `receive.fsckObjects = true`, allowing\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 32e476a..ce538ac 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -49,8 +49,8 @@ static int show_dangling = 1;\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n-\tif (strcmp(var, \"fsck.severity\") == 0) {\n-\t\tfsck_set_severity(&fsck_obj_options, value);\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tfsck_set_msg_type(&fsck_obj_options, var, -1, value, -1);\n \t\treturn 0;\n \t}\n \n@@ -59,7 +59,7 @@ static int fsck_config(const char *var, const char *value, void *cb)\n \t\t\tvalue : git_path(\"%s\", value);\n \t\tstruct strbuf sb = STRBUF_INIT;\n \t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n-\t\tfsck_set_severity(&fsck_obj_options, sb.buf);\n+\t\tfsck_set_msg_types(&fsck_obj_options, sb.buf);\n \t\tstrbuf_release(&sb);\n \t\treturn 0;\n \t}\n@@ -67,11 +67,11 @@ static int fsck_config(const char *var, const char *value, void *cb)\n \treturn git_default_config(var, value, cb);\n }\n \n-static void objreport(struct object *obj, const char *severity,\n-                      const char *err)\n+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        severity, typename(obj->type), sha1_to_hex(obj->sha1), err);\n+\t\tmsg_type, typename(obj->type), sha1_to_hex(obj->sha1), err);\n }\n \n static int objerror(struct object *obj, const char *err)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 94c64ab..98e14fe 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1636,7 +1636,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (skip_prefix(arg, \"--strict=\", &arg)) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n-\t\t\t\tfsck_set_severity(&fsck_options, arg);\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 4cbeb14..80574f9 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -19,6 +19,7 @@\n #include \"tag.h\"\n #include \"gpg-interface.h\"\n #include \"sigchain.h\"\n+#include \"fsck.h\"\n \n static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n \n@@ -36,7 +37,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n-static struct strbuf fsck_severity = STRBUF_INIT;\n+static struct strbuf fsck_msg_types = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int advertise_atomic_push = 1;\n@@ -116,17 +117,20 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n-\tif (strcmp(var, \"receive.fsck.severity\") == 0) {\n-\t\tstrbuf_addf(&fsck_severity, \"%c%s\",\n-\t\t\tfsck_severity.len ? ',' : '=', value);\n-\t\treturn 0;\n-\t}\n-\n \tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n \t\tconst char *path = is_absolute_path(value) ?\n \t\t\tvalue : git_path(\"%s\", value);\n-\t\tstrbuf_addf(&fsck_severity, \"%cskiplist=%s\",\n-\t\t\tfsck_severity.len ? ',' : '=', path);\n+\t\tstrbuf_addf(&fsck_msg_types, \"%cskiplist=%s\",\n+\t\t\tfsck_msg_types.len ? ',' : '=', path);\n+\t\treturn 0;\n+\t}\n+\n+\tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n+\t\tif (is_valid_msg_type(var, value))\n+\t\t\tstrbuf_addf(&fsck_msg_types, \"%c%s=%s\",\n+\t\t\t\tfsck_msg_types.len ? ',' : '=', var, value);\n+\t\telse\n+\t\t\twarning(\"Skipping unknown msg id '%s'\", var);\n \t\treturn 0;\n \t}\n \n@@ -1506,7 +1510,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects)\n \t\t\targv_array_pushf(&child.args, \"--strict%s\",\n-\t\t\t\tfsck_severity.buf);\n+\t\t\t\tfsck_msg_types.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1525,7 +1529,7 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects)\n \t\t\targv_array_pushf(&child.args, \"--strict%s\",\n-\t\t\t\tfsck_severity.buf);\n+\t\t\t\tfsck_msg_types.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex fe9117c..7cc086f 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -532,7 +532,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t}\n \t\t\tif (skip_prefix(arg, \"--strict=\", &arg)) {\n \t\t\t\tstrict = 1;\n-\t\t\t\tfsck_set_severity(&fsck_options, arg);\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\ndiff --git a/fsck.c b/fsck.c\nindex 046af02..9b8981e 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -63,7 +63,7 @@\n \tFUNC(INVALID_TAG_NAME, INFO) \\\n \tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n-#define MSG_ID(id, severity) FSCK_MSG_##id,\n+#define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n \tFOREACH_MSG_ID(MSG_ID)\n \tFSCK_MSG_MAX\n@@ -71,10 +71,10 @@ enum fsck_msg_id {\n #undef MSG_ID\n \n #define STR(x) #x\n-#define MSG_ID(id, severity) { STR(id), FSCK_##severity },\n+#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n static struct {\n \tconst char *id_string;\n-\tint severity;\n+\tint msg_type;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n \t{ NULL, -1 }\n@@ -85,39 +85,42 @@ static int parse_msg_id(const char *text, int len)\n {\n \tint i, j;\n \n+\tif (len < 0)\n+\t\tlen = strlen(text);\n+\n \tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n \t\tconst char *key = msg_id_info[i].id_string;\n-\t\t/* id_string is upper-case, with underscores */\n+\t\t/* match id_string case-insensitively, without underscores. */\n \t\tfor (j = 0; j < len; j++) {\n \t\t\tchar c = *(key++);\n \t\t\tif (c == '_')\n-\t\t\t\tc = '-';\n-\t\t\tif (text[j] != tolower(c))\n+\t\t\t\tc = *(key++);\n+\t\t\tif (toupper(text[j]) != c)\n \t\t\t\tbreak;\n \t\t}\n \t\tif (j == len && !*key)\n \t\t\treturn i;\n \t}\n \n-\tdie(\"Unhandled message id: %.*s\", len, text);\n+\treturn -1;\n }\n \n-static int fsck_msg_severity(enum fsck_msg_id msg_id,\n+static int fsck_msg_type(enum fsck_msg_id msg_id,\n \tstruct fsck_options *options)\n {\n-\tint severity;\n+\tint msg_type;\n \n \tassert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);\n \n-\tif (options->msg_severity)\n-\t\tseverity = options->msg_severity[msg_id];\n+\tif (options->msg_type)\n+\t\tmsg_type = options->msg_type[msg_id];\n \telse {\n-\t\tseverity = msg_id_info[msg_id].severity;\n-\t\tif (options->strict && severity == FSCK_WARN)\n-\t\t\tseverity = FSCK_ERROR;\n+\t\tmsg_type = msg_id_info[msg_id].msg_type;\n+\t\tif (options->strict && msg_type == FSCK_WARN)\n+\t\t\tmsg_type = FSCK_ERROR;\n \t}\n \n-\treturn severity;\n+\treturn msg_type;\n }\n \n static void init_skiplist(struct fsck_options *options, const char *path)\n@@ -165,66 +168,87 @@ static inline int substrcmp(const char *string, int len, const char *match)\n \treturn memcmp(string, match, len);\n }\n \n-void fsck_set_severity(struct fsck_options *options, const char *mode)\n+static int parse_msg_type(const char *str, int len)\n+{\n+\tif (len < 0)\n+\t\tlen = strlen(str);\n+\n+\tif (!substrcmp(str, len, \"error\"))\n+\t\treturn FSCK_ERROR;\n+\telse if (!substrcmp(str, len, \"warn\"))\n+\t\treturn FSCK_WARN;\n+\telse if (!substrcmp(str, len, \"ignore\"))\n+\t\treturn FSCK_IGNORE;\n+\telse\n+\t\tdie(\"Unknown fsck message type: '%.*s'\",\n+\t\t\t\tlen, str);\n+}\n+\n+int is_valid_msg_type(const char *msg_id, const char *msg_type)\n {\n-\tint severity = FSCK_ERROR;\n+\tif (parse_msg_id(msg_id, -1) < 0)\n+\t\treturn 0;\n+\tparse_msg_type(msg_type, -1);\n+\treturn 1;\n+}\n+\n+void fsck_set_msg_type(struct fsck_options *options,\n+\t\tconst char *msg_id, int msg_id_len,\n+\t\tconst char *msg_type, int msg_type_len)\n+{\n+\tint id = parse_msg_id(msg_id, msg_id_len), type;\n+\n+\tif (id < 0)\n+\t\tdie(\"Unhandled message id: %.*s\", msg_id_len, msg_id);\n+\ttype = parse_msg_type(msg_type, msg_type_len);\n \n-\tif (!options->msg_severity) {\n+\tif (type != FSCK_ERROR && msg_id_info[id].msg_type == FSCK_FATAL)\n+\t\tdie(\"Cannot demote %.*s to %.*s\", msg_id_len, msg_id,\n+\t\t\t\tmsg_type_len, msg_type);\n+\n+\tif (!options->msg_type) {\n \t\tint i;\n-\t\tint *msg_severity = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tint *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n \t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n-\t\t\tmsg_severity[i] = fsck_msg_severity(i, options);\n-\t\toptions->msg_severity = msg_severity;\n+\t\t\tmsg_type[i] = fsck_msg_type(i, options);\n+\t\toptions->msg_type = msg_type;\n \t}\n \n-\twhile (*mode) {\n-\t\tint len = strcspn(mode, \" ,|\"), equal, msg_id;\n+\toptions->msg_type[id] = type;\n+}\n+\n+void fsck_set_msg_types(struct fsck_options *options, const char *values)\n+{\n+\twhile (*values) {\n+\t\tint len = strcspn(values, \" ,|\"), equal;\n \n \t\tif (!len) {\n-\t\t\tmode++;\n+\t\t\tvalues++;\n \t\t\tcontinue;\n \t\t}\n \n \t\tfor (equal = 0; equal < len; equal++)\n-\t\t\tif (mode[equal] == '=' || mode[equal] == ':')\n+\t\t\tif (values[equal] == '=' || values[equal] == ':')\n \t\t\t\tbreak;\n \n-\t\tif (!substrcmp(mode, equal, \"skiplist\")) {\n-\t\t\tchar *path = xstrndup(mode + equal + 1,\n+\t\tif (!substrcmp(values, equal, \"skiplist\")) {\n+\t\t\tchar *path = xstrndup(values + equal + 1,\n \t\t\t\tlen - equal - 1);\n \n \t\t\tif (equal == len)\n \t\t\t\tdie(\"skiplist requires a path\");\n \t\t\tinit_skiplist(options, path);\n \t\t\tfree(path);\n-\t\t\tmode += len;\n+\t\t\tvalues += len;\n \t\t\tcontinue;\n \t\t}\n \n-\t\tmsg_id = parse_msg_id(mode, equal);\n-\n \t\tif (equal == len)\n-\t\t\tseverity = FSCK_ERROR;\n-\t\telse {\n-\t\t\tconst char *p = mode + equal + 1;\n-\t\t\tint len2 = len - equal - 1;\n-\n-\t\t\tif (!substrcmp(p, len2, \"error\"))\n-\t\t\t\tseverity = FSCK_ERROR;\n-\t\t\telse if (!substrcmp(p, len2, \"warn\"))\n-\t\t\t\tseverity = FSCK_WARN;\n-\t\t\telse if (!substrcmp(p, len2, \"ignore\"))\n-\t\t\t\tseverity = FSCK_IGNORE;\n-\t\t\telse\n-\t\t\t\tdie(\"Unknown fsck message severity: '%.*s'\",\n-\t\t\t\t\tlen2, p);\n-\t\t}\n+\t\t\tdie(\"Missing '=': '%.*s'\", len, values);\n \n-\t\tif (severity != FSCK_ERROR &&\n-\t\t\t\tmsg_id_info[msg_id].severity == FSCK_FATAL)\n-\t\t\tdie(\"Cannot demote %.*s\", len, mode);\n-\t\toptions->msg_severity[msg_id] = severity;\n-\t\tmode += len;\n+\t\tfsck_set_msg_type(options, values, equal,\n+\t\t\t\tvalues + equal + 1, len - equal - 1);\n+\t\tvalues += len;\n \t}\n }\n \n@@ -235,11 +259,8 @@ static void append_msg_id(struct strbuf *sb, const char *msg_id)\n \n \t\tif (!c)\n \t\t\tbreak;\n-\t\tif (c == '_')\n-\t\t\tc = '-';\n-\t\telse\n-\t\t\tc = tolower(c);\n-\t\tstrbuf_addch(sb, c);\n+\t\tif (c != '_')\n+\t\t\tstrbuf_addch(sb, tolower(c));\n \t}\n \n \tstrbuf_addstr(sb, \": \");\n@@ -251,21 +272,25 @@ static int report(struct fsck_options *options, struct object *object,\n {\n \tva_list ap;\n \tstruct strbuf sb = STRBUF_INIT;\n-\tint msg_severity = fsck_msg_severity(id, options), result;\n+\tint msg_type = fsck_msg_type(id, options), result;\n+\n+\tif (msg_type == FSCK_IGNORE)\n+\t\treturn 0;\n \n-\tif (msg_severity == FSCK_IGNORE)\n+\tif (options->skiplist && object &&\n+\t\t\tsha1_array_lookup(options->skiplist, object->sha1) >= 0)\n \t\treturn 0;\n \n-\tif (msg_severity == FSCK_FATAL)\n-\t\tmsg_severity = FSCK_ERROR;\n-\telse if (msg_severity == FSCK_INFO)\n-\t\tmsg_severity = FSCK_WARN;\n+\tif (msg_type == FSCK_FATAL)\n+\t\tmsg_type = FSCK_ERROR;\n+\telse if (msg_type == FSCK_INFO)\n+\t\tmsg_type = FSCK_WARN;\n \n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n-\tresult = options->error_func(object, msg_severity, sb.buf);\n+\tresult = options->error_func(object, msg_type, sb.buf);\n \tstrbuf_release(&sb);\n \tva_end(ap);\n \n@@ -756,10 +781,6 @@ static int fsck_tag(struct tag *tag, const char *data,\n int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n-\tif (options->skiplist &&\n-\t\t\tsha1_array_lookup(options->skiplist, obj->sha1) >= 0)\n-\t\treturn 0;\n-\n \tif (!obj)\n \t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \n@@ -778,9 +799,9 @@ 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 severity, const char *message)\n+int fsck_error_function(struct object *obj, int msg_type, const char *message)\n {\n-\tif (severity == FSCK_WARN) {\n+\tif (msg_type == FSCK_WARN) {\n \t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \t\treturn 0;\n \t}\ndiff --git a/fsck.h b/fsck.h\nindex cae280e..cab9c65 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -7,7 +7,11 @@\n \n struct fsck_options;\n \n-void fsck_set_severity(struct fsck_options *options, const char *mode);\n+void fsck_set_msg_type(struct fsck_options *options,\n+\t\tconst char *msg_id, int msg_id_len,\n+\t\tconst char *msg_type, int msg_type_len);\n+void fsck_set_msg_types(struct fsck_options *options, const char *values);\n+int is_valid_msg_type(const char *msg_id, const char *msg_type);\n \n /*\n  * callback function for fsck_walk\n@@ -28,7 +32,7 @@ struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n \tunsigned strict:1;\n-\tint *msg_severity;\n+\tint *msg_type;\n \tstruct sha1_array *skiplist;\n };\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex b32afaf..471e2ea 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalid-tag-name: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: missing-tagger-entry: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: invalidtagname: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missingtaggerentry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n@@ -295,7 +295,7 @@ test_expect_success 'force fsck to ignore double author' '\n \tgit update-ref refs/heads/bogus \"$new\" &&\n \ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n \ttest_must_fail git fsck &&\n-\tgit -c fsck.severity=multiple-authors=ignore fsck\n+\tgit -c fsck.multipleauthors=ignore fsck\n '\n \n _bz='\\0'\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 7881e17..1ada54c 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -124,44 +124,46 @@ This commit object intentionally broken\n EOF\n \n test_expect_success 'push with receive.fsck.skiplist' '\n-\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+\tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n \ttest_must_fail git push --porcelain dst bogus &&\n \tgit --git-dir=dst/.git config receive.fsck.skiplist SKIP &&\n-\techo $commit > dst/.git/SKIP &&\n+\techo $commit >dst/.git/SKIP &&\n \tgit push --porcelain dst bogus\n '\n \n-test_expect_success 'push with receive.fsck.severity = missing-email=warn' '\n-\tcommit=\"$(git hash-object -t commit -w --stdin < bogus-commit)\" &&\n+test_expect_success 'push with receive.fsck.missingemail=warn' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n \ttest_must_fail git push --porcelain dst bogus &&\n \tgit --git-dir=dst/.git config \\\n-\t\treceive.fsck.severity missing-email=warn &&\n+\t\treceive.fsck.missingemail warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missing-email\" act &&\n+\tgrep \"missingemail\" act &&\n \tgit --git-dir=dst/.git branch -D bogus &&\n \tgit  --git-dir=dst/.git config --add \\\n-\t\treceive.fsck.severity missing-email=ignore,bad-date=warn &&\n+\t\treceive.fsck.missingemail ignore &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.baddate warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\ttest_must_fail grep \"missing-email\" act\n+\ttest_must_fail grep \"missingemail\" act\n '\n \n test_expect_success \\\n-\t'receive.fsck.severity = unterminated-header=warn triggers error' '\n+\t'receive.fsck.unterminatedheader=warn triggers error' '\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n \tgit --git-dir=dst/.git config \\\n-\t\treceive.fsck.severity unterminated-header=warn &&\n+\t\treceive.fsck.unterminatedheader warn &&\n \ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n-\tgrep \"Cannot demote unterminated-header\" act\n+\tgrep \"Cannot demote unterminatedheader\" act\n '\n \n test_done\n\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264216","messageId":"562d120ab5aa4f01b00398cb9561a690241a8bf6.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 01/19] fsck: Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:07:29Z","receivedAt":"2015-06-18T20:07:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Just like the diff machinery, we are about to introduce more settings,\ntherefore it makes sense to carry them around as a (pointer to a) struct\ncontaining all of them.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c           |  20 +++++--\n builtin/index-pack.c     |   9 +--\n builtin/unpack-objects.c |  11 ++--\n fsck.c                   | 150 +++++++++++++++++++++++------------------------\n fsck.h                   |  17 +++++-\n 5 files changed, 114 insertions(+), 93 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2679793..981dca5 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -25,6 +25,8 @@ static int include_reflogs = 1;\n static int check_full = 1;\n static int check_strict;\n static int keep_cache_objects;\n+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n static struct object_id head_oid;\n static const char *head_points_at;\n static int errors_found;\n@@ -76,7 +78,7 @@ static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n \n static struct object_array pending;\n \n-static int mark_object(struct object *obj, int type, void *data)\n+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct object *parent = data;\n \n@@ -119,7 +121,7 @@ static int mark_object(struct object *obj, int type, void *data)\n \n static void mark_object_reachable(struct object *obj)\n {\n-\tmark_object(obj, OBJ_ANY, NULL);\n+\tmark_object(obj, OBJ_ANY, NULL, NULL);\n }\n \n static int traverse_one_object(struct object *obj)\n@@ -132,7 +134,7 @@ static int traverse_one_object(struct object *obj)\n \t\tif (parse_tree(tree) < 0)\n \t\t\treturn 1; /* error already displayed */\n \t}\n-\tresult = fsck_walk(obj, mark_object, obj);\n+\tresult = fsck_walk(obj, obj, &fsck_walk_options);\n \tif (tree)\n \t\tfree_tree_buffer(tree);\n \treturn result;\n@@ -158,7 +160,7 @@ static int traverse_reachable(void)\n \treturn !!result;\n }\n \n-static int mark_used(struct object *obj, int type, void *data)\n+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn 1;\n@@ -296,9 +298,9 @@ static int fsck_obj(struct object *obj)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n \t\t\ttypename(obj->type), sha1_to_hex(obj->sha1));\n \n-\tif (fsck_walk(obj, mark_used, NULL))\n+\tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n-\tif (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))\n+\tif (fsck_object(obj, NULL, 0, &fsck_obj_options))\n \t\treturn -1;\n \n \tif (obj->type == OBJ_TREE) {\n@@ -638,6 +640,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);\n \n+\tfsck_walk_options.walk = mark_object;\n+\tfsck_obj_options.walk = mark_used;\n+\tfsck_obj_options.error_func = fsck_error_func;\n+\tif (check_strict)\n+\t\tfsck_obj_options.strict = 1;\n+\n \tif (show_progress == -1)\n \t\tshow_progress = isatty(2);\n \tif (verbose)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 48fa472..87ae9ba 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -75,6 +75,7 @@ static int nr_threads;\n static int from_stdin;\n static int strict;\n static int do_fsck_object;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n static int verbose;\n static int show_stat;\n static int check_self_contained_and_connected;\n@@ -192,7 +193,7 @@ static void cleanup_thread(void)\n #endif\n \n \n-static int mark_link(struct object *obj, int type, void *data)\n+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -838,10 +839,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (!obj)\n \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(obj, buf, size, 1,\n-\t\t\t\t    fsck_error_function))\n+\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"Error in object\"));\n-\t\t\tif (fsck_walk(obj, mark_link, NULL))\n+\t\t\tfsck_options.walk = mark_link;\n+\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n \n \t\t\tif (obj->type == OBJ_TREE) {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex ac66672..6d17040 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -20,6 +20,7 @@ static unsigned char buffer[4096];\n static unsigned int offset, len;\n static off_t consumed_bytes;\n static git_SHA_CTX ctx;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n /*\n  * When running under --strict mode, objects whose reachability are\n@@ -178,7 +179,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)\n  * that have reachability requirements and calls this function.\n  * Verify its reachability and validity recursively and write it out.\n  */\n-static int check_object(struct object *obj, int type, void *data)\n+static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct obj_buffer *obj_buf;\n \n@@ -203,10 +204,10 @@ static int check_object(struct object *obj, int type, void *data)\n \tobj_buf = lookup_object_buffer(obj);\n \tif (!obj_buf)\n \t\tdie(\"Whoops! Cannot find object '%s'\", sha1_to_hex(obj->sha1));\n-\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1,\n-\t\t\tfsck_error_function))\n+\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"Error in object\");\n-\tif (fsck_walk(obj, check_object, NULL))\n+\tfsck_options.walk = check_object;\n+\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\tdie(\"Error on reachable objects of %s\", sha1_to_hex(obj->sha1));\n \twrite_cached_object(obj, obj_buf);\n \treturn 0;\n@@ -217,7 +218,7 @@ static void write_rest(void)\n \tunsigned i;\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tif (obj_list[i].obj)\n-\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL);\n+\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);\n \t}\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex 10bcb65..d83b811 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,7 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n-static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\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@@ -25,9 +25,9 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n \t\tif (S_ISDIR(entry.mode))\n-\t\t\tresult = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);\n+\t\t\tresult = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);\n \t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n-\t\t\tresult = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);\n+\t\t\tresult = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n \t\t\t\t\tsha1_to_hex(tree->object.sha1), entry.path, entry.mode);\n@@ -40,7 +40,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \treturn res;\n }\n \n-static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)\n+static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n \tstruct commit_list *parents;\n \tint res;\n@@ -49,14 +49,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \tif (parse_commit(commit))\n \t\treturn -1;\n \n-\tresult = walk((struct object *)commit->tree, OBJ_TREE, data);\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 \twhile (parents) {\n-\t\tresult = walk((struct object *)parents->item, OBJ_COMMIT, data);\n+\t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n@@ -66,14 +66,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \treturn res;\n }\n \n-static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)\n+static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n \tif (parse_tag(tag))\n \t\treturn -1;\n-\treturn walk(tag->tagged, OBJ_ANY, data);\n+\treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -81,11 +81,11 @@ int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n \tcase OBJ_BLOB:\n \t\treturn 0;\n \tcase OBJ_TREE:\n-\t\treturn fsck_walk_tree((struct tree *)obj, walk, data);\n+\t\treturn fsck_walk_tree((struct tree *)obj, data, options);\n \tcase OBJ_COMMIT:\n-\t\treturn fsck_walk_commit((struct commit *)obj, walk, data);\n+\t\treturn fsck_walk_commit((struct commit *)obj, data, options);\n \tcase OBJ_TAG:\n-\t\treturn fsck_walk_tag((struct tag *)obj, walk, data);\n+\t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n \tdefault:\n \t\terror(\"Unknown object type for %s\", sha1_to_hex(obj->sha1));\n \t\treturn -1;\n@@ -138,7 +138,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con\n \treturn c1 < c2 ? 0 : TREE_UNORDERED;\n }\n \n-static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n+static int fsck_tree(struct tree *item, struct fsck_options *options)\n {\n \tint retval;\n \tint has_null_sha1 = 0;\n@@ -194,7 +194,7 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \t\t * bits..\n \t\t */\n \t\tcase S_IFREG | 0664:\n-\t\t\tif (!strict)\n+\t\t\tif (!options->strict)\n \t\t\t\tbreak;\n \t\tdefault:\n \t\t\thas_bad_modes = 1;\n@@ -219,30 +219,30 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n \treturn retval;\n }\n \n static int require_end_of_header(const void *data, unsigned long size,\n-\tstruct object *obj, fsck_error error_func)\n+\tstruct object *obj, struct fsck_options *options)\n {\n \tconst char *buffer = (const char *)data;\n \tunsigned long i;\n@@ -250,7 +250,7 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn error_func(obj, FSCK_ERROR,\n+\t\t\treturn options->error_func(obj, FSCK_ERROR,\n \t\t\t\t\"unterminated header: NUL at offset %d\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n@@ -258,36 +258,36 @@ static int require_end_of_header(const void *data, unsigned long size,\n \t\t}\n \t}\n \n-\treturn error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n }\n \n-static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)\n+static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,30 +295,30 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n \n static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n \tunsigned parent_count, parent_line_count = 0;\n \tint err;\n \n-\tif (require_end_of_header(buffer, size, &commit->object, error_func))\n+\tif (require_end_of_header(buffer, size, &commit->object, options))\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,39 +328,39 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n \n static int fsck_commit(struct commit *commit, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tconst char *buffer = data ?  data : get_commit_buffer(commit, &size);\n-\tint ret = fsck_commit_buffer(commit, buffer, size, error_func);\n+\tint ret = fsck_commit_buffer(commit, buffer, size, options);\n \tif (!data)\n \t\tunuse_commit_buffer(commit, buffer);\n \treturn ret;\n }\n \n static int fsck_tag_buffer(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char sha1[20];\n \tint ret = 0;\n@@ -376,65 +376,65 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n \t\t}\n \t}\n \n-\tif (require_end_of_header(buffer, size, &tag->object, error_func))\n+\tif (require_end_of_header(buffer, size, &tag->object, options))\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n \telse\n-\t\tret = fsck_ident(&buffer, &tag->object, error_func);\n+\t\tret = fsck_ident(&buffer, &tag->object, options);\n \n done:\n \tstrbuf_release(&sb);\n@@ -443,34 +443,34 @@ done:\n }\n \n static int fsck_tag(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n \n-\treturn fsck_tag_buffer(tag, data, size, error_func);\n+\treturn fsck_tag_buffer(tag, data, size, options);\n }\n \n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func)\n+\tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n \tif (obj->type == OBJ_TREE)\n-\t\treturn fsck_tree((struct tree *) obj, strict, error_func);\n+\t\treturn fsck_tree((struct tree *) obj, options);\n \tif (obj->type == OBJ_COMMIT)\n \t\treturn fsck_commit((struct commit *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \tif (obj->type == OBJ_TAG)\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \n-\treturn error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex d1e6387..07d0ab2 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -4,6 +4,8 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n \n+struct fsck_options;\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -12,7 +14,7 @@\n  *     <0\terror signaled and abort\n  *     >0\terror signaled and do not abort\n  */\n-typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);\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 *err, ...);\n@@ -20,6 +22,15 @@ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);\n __attribute__((format (printf, 3, 4)))\n int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n \n+struct fsck_options {\n+\tfsck_walk_func walk;\n+\tfsck_error error_func;\n+\tunsigned strict:1;\n+};\n+\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+\n /* descend in all linked child objects\n  * the return value is:\n  *    -1\terror in processing the object\n@@ -27,9 +38,9 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n  *    >0\treturn value of the first signaled error >0 (in the case of no other errors)\n  *    0\t\teverything OK\n  */\n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n /* If NULL is passed for data, we assume the object is local and read it. */\n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func);\n+\tstruct fsck_options *options);\n \n #endif\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264217","messageId":"821f31745096ff6d35a46ca5fdd15a5cece2443d.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 02/19] fsck: Introduce identifiers for fsck messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:07:45Z","receivedAt":"2015-06-18T20:07:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Instead of specifying whether a message by the fsck machinery constitutes\nan error or a warning, let's specify an identifier relating to the\nconcrete problem that was encountered. This is necessary for upcoming\nsupport to be able to demote certain errors to warnings.\n\nIn the process, simplify the requirements on the calling code: instead of\nhaving to handle full-blown varargs in every callback, we now send a\nstring buffer ready to be used by the callback.\n\nWe could use a simple enum for the message IDs here, but we want to\nguarantee that the enum values are associated with the appropriate\nmessage types (i.e. error or warning?). Besides, we want to introduce a\nparser in the next commit that maps the string representation to the\nenum value, hence we use the slightly ugly preprocessor construct that\nis extensible for use with said parser.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c |  26 +++-----\n fsck.c         | 201 +++++++++++++++++++++++++++++++++++++++++----------------\n fsck.h         |   5 +-\n 3 files changed, 154 insertions(+), 78 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 981dca5..fff38fe 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,33 +46,23 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n-static void objreport(struct object *obj, const char *severity,\n-                      const char *err, va_list params)\n+static void objreport(struct object *obj, const char *msg_type,\n+\t\t\tconst char *err)\n {\n-\tfprintf(stderr, \"%s in %s %s: \",\n-\t        severity, typename(obj->type), sha1_to_hex(obj->sha1));\n-\tvfprintf(stderr, err, params);\n-\tfputs(\"\\n\", stderr);\n+\tfprintf(stderr, \"%s in %s %s: %s\\n\",\n+\t\tmsg_type, typename(obj->type), sha1_to_hex(obj->sha1), err);\n }\n \n-__attribute__((format (printf, 2, 3)))\n-static int objerror(struct object *obj, const char *err, ...)\n+static int objerror(struct object *obj, const char *err)\n {\n-\tva_list params;\n-\tva_start(params, err);\n \terrors_found |= ERROR_OBJECT;\n-\tobjreport(obj, \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, \"error\", err);\n \treturn -1;\n }\n \n-__attribute__((format (printf, 3, 4)))\n-static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n+static int fsck_error_func(struct object *obj, int type, const char *message)\n {\n-\tva_list params;\n-\tva_start(params, err);\n-\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n \treturn (type == FSCK_WARN) ? 0 : 1;\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex d83b811..ed0bfc3 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,98 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FOREACH_MSG_ID(FUNC) \\\n+\t/* errors */ \\\n+\tFUNC(BAD_DATE, ERROR) \\\n+\tFUNC(BAD_EMAIL, ERROR) \\\n+\tFUNC(BAD_NAME, ERROR) \\\n+\tFUNC(BAD_PARENT_SHA1, ERROR) \\\n+\tFUNC(BAD_TIMEZONE, ERROR) \\\n+\tFUNC(BAD_TREE_SHA1, ERROR) \\\n+\tFUNC(DATE_OVERFLOW, ERROR) \\\n+\tFUNC(DUPLICATE_ENTRIES, ERROR) \\\n+\tFUNC(INVALID_OBJECT_SHA1, ERROR) \\\n+\tFUNC(INVALID_TAG_OBJECT, ERROR) \\\n+\tFUNC(INVALID_TREE, ERROR) \\\n+\tFUNC(INVALID_TYPE, ERROR) \\\n+\tFUNC(MISSING_AUTHOR, ERROR) \\\n+\tFUNC(MISSING_COMMITTER, ERROR) \\\n+\tFUNC(MISSING_EMAIL, ERROR) \\\n+\tFUNC(MISSING_GRAFT, ERROR) \\\n+\tFUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_OBJECT, ERROR) \\\n+\tFUNC(MISSING_PARENT, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_TAG, ERROR) \\\n+\tFUNC(MISSING_TAG_ENTRY, ERROR) \\\n+\tFUNC(MISSING_TAG_OBJECT, ERROR) \\\n+\tFUNC(MISSING_TREE, ERROR) \\\n+\tFUNC(MISSING_TYPE, ERROR) \\\n+\tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(NOT_SORTED, ERROR) \\\n+\tFUNC(NUL_IN_HEADER, ERROR) \\\n+\tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n+\tFUNC(UNKNOWN_TYPE, ERROR) \\\n+\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n+\tFUNC(ZERO_PADDED_DATE, ERROR) \\\n+\t/* warnings */ \\\n+\tFUNC(BAD_FILEMODE, WARN) \\\n+\tFUNC(EMPTY_NAME, WARN) \\\n+\tFUNC(FULL_PATHNAME, WARN) \\\n+\tFUNC(HAS_DOT, WARN) \\\n+\tFUNC(HAS_DOTDOT, WARN) \\\n+\tFUNC(HAS_DOTGIT, WARN) \\\n+\tFUNC(INVALID_TAG_NAME, WARN) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n+\tFUNC(NULL_SHA1, WARN) \\\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\n+#define MSG_ID(id, msg_type) FSCK_MSG_##id,\n+enum fsck_msg_id {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\tFSCK_MSG_MAX\n+};\n+#undef MSG_ID\n+\n+#define MSG_ID(id, msg_type) { FSCK_##msg_type },\n+static struct {\n+\tint msg_type;\n+} msg_id_info[FSCK_MSG_MAX + 1] = {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\t{ -1 }\n+};\n+#undef MSG_ID\n+\n+static int fsck_msg_type(enum fsck_msg_id msg_id,\n+\tstruct fsck_options *options)\n+{\n+\tint msg_type;\n+\n+\tmsg_type = msg_id_info[msg_id].msg_type;\n+\tif (options->strict && msg_type == FSCK_WARN)\n+\t\tmsg_type = FSCK_ERROR;\n+\n+\treturn msg_type;\n+}\n+\n+__attribute__((format (printf, 4, 5)))\n+static int report(struct fsck_options *options, struct object *object,\n+\tenum fsck_msg_id id, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint msg_type = fsck_msg_type(id, options), result;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tresult = options->error_func(object, msg_type, sb.buf);\n+\tstrbuf_release(&sb);\n+\tva_end(ap);\n+\n+\treturn result;\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n@@ -219,25 +311,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NULL_SHA1, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOT, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NOT_SORTED, \"not properly sorted\");\n \treturn retval;\n }\n \n@@ -250,15 +342,17 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn options->error_func(obj, FSCK_ERROR,\n-\t\t\t\t\"unterminated header: NUL at offset %d\", i);\n+\t\t\treturn report(options, obj,\n+\t\t\t\tFSCK_MSG_NUL_IN_HEADER,\n+\t\t\t\t\"unterminated header: NUL at offset %ld\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n \t\t\t\treturn 0;\n \t\t}\n \t}\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn report(options, obj,\n+\t\tFSCK_MSG_UNTERMINATED_HEADER, \"unterminated header\");\n }\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n@@ -266,28 +360,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,7 +389,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n@@ -312,13 +406,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,23 +422,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn report(options, &commit->object, FSCK_MSG_INVALID_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n@@ -376,11 +470,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_MISSING_TAG_OBJECT,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_TAG_OBJECT_NOT_TAG,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n@@ -391,48 +487,49 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \n@@ -448,7 +545,7 @@ static int fsck_tag(struct tag *tag, const char *data,\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn report(options, &tag->object, FSCK_MSG_INVALID_TAG_OBJECT, \"could not load tagged object\");\n \n \treturn fsck_tag_buffer(tag, data, size, options);\n }\n@@ -457,7 +554,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n@@ -470,22 +567,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n \t\t\tsize, options);\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn report(options, obj, FSCK_MSG_UNKNOWN_TYPE, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...)\n+int fsck_error_function(struct object *obj, int msg_type, const char *message)\n {\n-\tva_list ap;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tstrbuf_addf(&sb, \"object %s:\", sha1_to_hex(obj->sha1));\n-\n-\tva_start(ap, fmt);\n-\tstrbuf_vaddf(&sb, fmt, ap);\n-\tva_end(ap);\n-\n-\terror(\"%s\", sb.buf);\n-\tstrbuf_release(&sb);\n+\terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex 07d0ab2..f6f268a 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -17,10 +17,9 @@ struct fsck_options;\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 *err, ...);\n+typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n \n-__attribute__((format (printf, 3, 4)))\n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n+int fsck_error_function(struct object *obj, int type, const char *message);\n \n struct fsck_options {\n \tfsck_walk_func walk;\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264218","messageId":"44712302530b17207465cfaa7c4ca180acbb2769.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 03/19] fsck: Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:07:58Z","receivedAt":"2015-06-18T20:07:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"These functions will be used in the next commits to allow the user to\nask fsck to handle specific problems differently, e.g. demoting certain\nerrors to warnings. The upcoming `fsck_set_msg_types()` function has to\nhandle partial strings because we would like to be able to parse, say,\n'missingemail=warn,missingtaggerentry=warn' command line parameters\n(which will be passed by receive-pack to index-pack and unpack-objects).\n\nTo make the parsing robust, we generate strings from the enum keys, and\nusing these keys, we match up strings without dashes case-insensitively\nto the corresponding enum values.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 30 ++++++++++++++++++++++++++++--\n 1 file changed, 28 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex ed0bfc3..4595c7f 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -63,15 +63,41 @@ enum fsck_msg_id {\n };\n #undef MSG_ID\n \n-#define MSG_ID(id, msg_type) { FSCK_##msg_type },\n+#define STR(x) #x\n+#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n static struct {\n+\tconst char *id_string;\n \tint msg_type;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n-\t{ -1 }\n+\t{ NULL, -1 }\n };\n #undef MSG_ID\n \n+static int parse_msg_id(const char *text, int len)\n+{\n+\tint i, j;\n+\n+\tif (len < 0)\n+\t\tlen = strlen(text);\n+\n+\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n+\t\tconst char *key = msg_id_info[i].id_string;\n+\t\t/* match id_string case-insensitively, without underscores. */\n+\t\tfor (j = 0; j < len; j++) {\n+\t\t\tchar c = *(key++);\n+\t\t\tif (c == '_')\n+\t\t\t\tc = *(key++);\n+\t\t\tif (toupper(text[j]) != c)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (j == len && !*key)\n+\t\t\treturn i;\n+\t}\n+\n+\treturn -1;\n+}\n+\n static int fsck_msg_type(enum fsck_msg_id msg_id,\n \tstruct fsck_options *options)\n {\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264220","messageId":"ae715e3a4879e1c619fd3b32fbf081d504d9e9aa.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:08:07Z","receivedAt":"2015-06-18T20:08:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are legacy repositories out there whose older commits and tags\nhave issues that prevent pushing them when 'receive.fsckObjects' is set.\nOne real-life example is a commit object that has been hand-crafted to\nlist two authors.\n\nOften, it is not possible to fix those issues without disrupting the\nwork with said repositories, yet it is still desirable to perform checks\nby setting `receive.fsckObjects = true`. This commit is the first step\nto allow demoting specific fsck issues to mere warnings.\n\nThe `fsck_set_msg_types()` function added by this commit parses a list\nof settings in the form:\n\n\tmissingemail=warn,badname=warn,...\n\nUnfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\ngit fsck so far, but other call paths (e.g. git index-pack --strict)\nerror out *always* no matter what type was specified. Therefore, we need\nto take extra care to set all message types to FSCK_ERROR by default in\nthose cases.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n fsck.h | 10 ++++++--\n 2 files changed, 87 insertions(+), 5 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 4595c7f..7db81d2 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -103,13 +103,85 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,\n {\n \tint msg_type;\n \n-\tmsg_type = msg_id_info[msg_id].msg_type;\n-\tif (options->strict && msg_type == FSCK_WARN)\n-\t\tmsg_type = FSCK_ERROR;\n+\tassert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);\n+\n+\tif (options->msg_type)\n+\t\tmsg_type = options->msg_type[msg_id];\n+\telse {\n+\t\tmsg_type = msg_id_info[msg_id].msg_type;\n+\t\tif (options->strict && msg_type == FSCK_WARN)\n+\t\t\tmsg_type = FSCK_ERROR;\n+\t}\n \n \treturn msg_type;\n }\n \n+static inline int substrcmp(const char *string, int len, const char *match)\n+{\n+\tint match_len = strlen(match);\n+\tif (match_len != len)\n+\t\treturn -1;\n+\treturn memcmp(string, match, len);\n+}\n+\n+static int parse_msg_type(const char *str, int len)\n+{\n+\tif (len < 0)\n+\t\tlen = strlen(str);\n+\n+\tif (!substrcmp(str, len, \"error\"))\n+\t\treturn FSCK_ERROR;\n+\telse if (!substrcmp(str, len, \"warn\"))\n+\t\treturn FSCK_WARN;\n+\telse\n+\t\tdie(\"Unknown fsck message type: '%.*s'\",\n+\t\t\t\tlen, str);\n+}\n+\n+void fsck_set_msg_type(struct fsck_options *options,\n+\t\tconst char *msg_id, int msg_id_len,\n+\t\tconst char *msg_type, int msg_type_len)\n+{\n+\tint id = parse_msg_id(msg_id, msg_id_len), type;\n+\n+\tif (id < 0)\n+\t\tdie(\"Unhandled message id: %.*s\", msg_id_len, msg_id);\n+\ttype = parse_msg_type(msg_type, msg_type_len);\n+\n+\tif (!options->msg_type) {\n+\t\tint i;\n+\t\tint *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\t\tmsg_type[i] = fsck_msg_type(i, options);\n+\t\toptions->msg_type = msg_type;\n+\t}\n+\n+\toptions->msg_type[id] = type;\n+}\n+\n+void fsck_set_msg_types(struct fsck_options *options, const char *values)\n+{\n+\twhile (*values) {\n+\t\tint len = strcspn(values, \" ,|\"), equal;\n+\n+\t\tif (!len) {\n+\t\t\tvalues++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (equal = 0; equal < len; equal++)\n+\t\t\tif (values[equal] == '=' || values[equal] == ':')\n+\t\t\t\tbreak;\n+\n+\t\tif (equal == len)\n+\t\t\tdie(\"Missing '=': '%.*s'\", len, values);\n+\n+\t\tfsck_set_msg_type(options, values, equal,\n+\t\t\t\tvalues + equal + 1, len - equal - 1);\n+\t\tvalues += len;\n+\t}\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -599,6 +671,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \n int fsck_error_function(struct object *obj, int msg_type, const char *message)\n {\n+\tif (msg_type == FSCK_WARN) {\n+\t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n+\t\treturn 0;\n+\t}\n \terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex f6f268a..edb4540 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,11 @@\n \n struct fsck_options;\n \n+void fsck_set_msg_type(struct fsck_options *options,\n+\t\tconst char *msg_id, int msg_id_len,\n+\t\tconst char *msg_type, int msg_type_len);\n+void fsck_set_msg_types(struct fsck_options *options, const char *values);\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -25,10 +30,11 @@ struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n \tunsigned strict:1;\n+\tint *msg_type;\n };\n \n-#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n-#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }\n \n /* descend in all linked child objects\n  * the return value is:\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264221","messageId":"ff132d541272b2e4413934d51e67dd127ba7e64e.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 05/19] fsck (receive-pack): Allow demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:08:15Z","receivedAt":"2015-06-18T20:08:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For example, missing emails in commit and tag objects can be demoted to\nmere warnings with\n\n\tgit config receive.fsck.missingemail=warn\n\nThe value is actually a comma-separated list.\n\nIn case that the same key is listed in multiple receive.fsck.<msg-id>\nlines in the config, the latter configuration wins (this can happen for\nexample when both $HOME/.gitconfig and .git/config contain message type\nsettings).\n\nAs git receive-pack does not actually perform the checks, it hands off\nthe setting to index-pack or unpack-objects in the form of an optional\nargument to the --strict option.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/index-pack.c     |  4 ++++\n builtin/receive-pack.c   | 17 +++++++++++++++--\n builtin/unpack-objects.c |  5 +++++\n fsck.c                   |  8 ++++++++\n fsck.h                   |  1 +\n 5 files changed, 33 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 87ae9ba..98e14fe 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1633,6 +1633,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(arg, \"--strict\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n+\t\t\t} else if (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tdo_fsck_object = 1;\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 94d0571..3afe8f8 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -19,6 +19,7 @@\n #include \"tag.h\"\n #include \"gpg-interface.h\"\n #include \"sigchain.h\"\n+#include \"fsck.h\"\n \n static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n \n@@ -36,6 +37,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n+static struct strbuf fsck_msg_types = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int advertise_atomic_push = 1;\n@@ -115,6 +117,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n+\t\tif (is_valid_msg_type(var, value))\n+\t\t\tstrbuf_addf(&fsck_msg_types, \"%c%s=%s\",\n+\t\t\t\tfsck_msg_types.len ? ',' : '=', var, value);\n+\t\telse\n+\t\t\twarning(\"Skipping unknown msg id '%s'\", var);\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsckobjects\") == 0) {\n \t\treceive_fsck_objects = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1490,7 +1501,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n+\t\t\t\tfsck_msg_types.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1508,7 +1520,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n+\t\t\t\tfsck_msg_types.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 6d17040..7cc086f 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrict = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\n \t\t\t\tstruct pack_header *hdr;\n \t\t\t\tchar *c;\ndiff --git a/fsck.c b/fsck.c\nindex 7db81d2..0c7cc26 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -138,6 +138,14 @@ static int parse_msg_type(const char *str, int len)\n \t\t\t\tlen, str);\n }\n \n+int is_valid_msg_type(const char *msg_id, const char *msg_type)\n+{\n+\tif (parse_msg_id(msg_id, -1) < 0)\n+\t\treturn 0;\n+\tparse_msg_type(msg_type, -1);\n+\treturn 1;\n+}\n+\n void fsck_set_msg_type(struct fsck_options *options,\n \t\tconst char *msg_id, int msg_id_len,\n \t\tconst char *msg_type, int msg_type_len)\ndiff --git a/fsck.h b/fsck.h\nindex edb4540..738c9df 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -10,6 +10,7 @@ void fsck_set_msg_type(struct fsck_options *options,\n \t\tconst char *msg_id, int msg_id_len,\n \t\tconst char *msg_type, int msg_type_len);\n void fsck_set_msg_types(struct fsck_options *options, const char *values);\n+int is_valid_msg_type(const char *msg_id, const char *msg_type);\n \n /*\n  * callback function for fsck_walk\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264219","messageId":"d0cab23cd7755a080b4a2b9b51d65684632bf4f9.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 06/19] fsck: Report the ID of the error/warning","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:08:25Z","receivedAt":"2015-06-18T20:08:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some legacy code has objects with non-fatal fsck issues; To enable the\nuser to ignore those issues, let's print out the ID (e.g. when\nencountering \"missingemail\", the user might want to call `git config\n--add receive.fsck.missingemail=warn`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c          | 16 ++++++++++++++++\n t/t1450-fsck.sh |  4 ++--\n 2 files changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 0c7cc26..47cb686 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -190,6 +190,20 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n \t}\n }\n \n+static void append_msg_id(struct strbuf *sb, const char *msg_id)\n+{\n+\tfor (;;) {\n+\t\tchar c = *(msg_id)++;\n+\n+\t\tif (!c)\n+\t\t\tbreak;\n+\t\tif (c != '_')\n+\t\t\tstrbuf_addch(sb, tolower(c));\n+\t}\n+\n+\tstrbuf_addstr(sb, \": \");\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -198,6 +212,8 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tappend_msg_id(&sb, msg_id_info[id].id_string);\n+\n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n \tresult = options->error_func(object, msg_type, sb.buf);\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex cfb32b6..286a643 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: invalidtagname: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missingtaggerentry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264222","messageId":"07d15d7aace24adf61afe9041d07905e9c141ff8.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 07/19] fsck: Make fsck_ident() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:08:33Z","receivedAt":"2015-06-18T20:08:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_ident() identifies a problem with the ident, it should still\nadvance the pointer to the next line so that fsck can continue in the\ncase of a mere warning.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 49 +++++++++++++++++++++++++++----------------------\n 1 file changed, 27 insertions(+), 22 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 47cb686..8a1eea3 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -479,40 +479,45 @@ static int require_end_of_header(const void *data, unsigned long size,\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n+\tconst char *p = *ident;\n \tchar *end;\n \n-\tif (**ident == '<')\n+\t*ident = strchrnul(*ident, '\\n');\n+\tif (**ident == '\\n')\n+\t\t(*ident)++;\n+\n+\tif (*p == '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident == '>')\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p == '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n-\tif (**ident != '<')\n+\tif (*p != '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n-\tif ((*ident)[-1] != ' ')\n+\tif (p[-1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t(*ident)++;\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident != '>')\n+\tp++;\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p != '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n-\t(*ident)++;\n-\tif (**ident != ' ')\n+\tp++;\n+\tif (*p != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n-\t(*ident)++;\n-\tif (**ident == '0' && (*ident)[1] != ' ')\n+\tp++;\n+\tif (*p == '0' && p[1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n-\tif (date_overflows(strtoul(*ident, &end, 10)))\n+\tif (date_overflows(strtoul(p, &end, 10)))\n \t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n-\tif (end == *ident || *end != ' ')\n+\tif ((end == p || *end != ' '))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n-\t*ident = end + 1;\n-\tif ((**ident != '+' && **ident != '-') ||\n-\t    !isdigit((*ident)[1]) ||\n-\t    !isdigit((*ident)[2]) ||\n-\t    !isdigit((*ident)[3]) ||\n-\t    !isdigit((*ident)[4]) ||\n-\t    ((*ident)[5] != '\\n'))\n+\tp = end + 1;\n+\tif ((*p != '+' && *p != '-') ||\n+\t    !isdigit(p[1]) ||\n+\t    !isdigit(p[2]) ||\n+\t    !isdigit(p[3]) ||\n+\t    !isdigit(p[4]) ||\n+\t    (p[5] != '\\n'))\n \t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n-\t(*ident) += 6;\n+\tp += 6;\n \treturn 0;\n }\n \n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264224","messageId":"2094aecd48db6dbd1bede21b3c814b73dfd21875.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:08:41Z","receivedAt":"2015-06-18T20:08:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_commit() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nNote that some problems are too problematic to simply ignore. For\nexample, when the header lines are mixed up, we punt after encountering\nan incorrect line. Therefore, demoting certain warnings to errors can\nhide other problems. Example: demoting the missingauthor error to\na warning would hide a problematic committer line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 28 ++++++++++++++++++++--------\n 1 file changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 8a1eea3..31d218d 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -534,12 +534,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n-\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n+\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n-\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -548,11 +554,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \tif (graft) {\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n-\t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\telse if (graft->nr_parent != parent_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t} else {\n-\t\tif (parent_count != parent_line_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\tif (parent_count != parent_line_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264223","messageId":"e6966c2a80fef10a249537b76045e2672ad02ba2.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 09/19] fsck: Handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:08:49Z","receivedAt":"2015-06-18T20:08:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This problem has been detected in the wild, and is the primary reason\nto introduce an option to demote certain fsck errors to warnings. Let's\noffer to ignore this particular problem specifically.\n\nTechnically, we could handle such repositories by setting\nreceive.fsck.<msg-id> to missingcommitter=warn, but that could hide\nmissing tree objects in the same commit because we cannot continue\nverifying any commit object after encountering a missing committer line,\nwhile we can continue in the case of multiple author lines.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex 31d218d..856221d 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -38,6 +38,7 @@\n \tFUNC(MISSING_TREE, ERROR) \\\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NOT_SORTED, ERROR) \\\n \tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n@@ -571,6 +572,14 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n+\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n+\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\terr = fsck_ident(&buffer, &commit->object, options);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264225","messageId":"0477fc78ad957fa2debd48664a7e290eb4fbf86c.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 11/19] fsck: Add a simple test for receive.fsck.<msg-id>","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:08:59Z","receivedAt":"2015-06-18T20:08:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5504-fetch-receive-strict.sh | 21 +++++++++++++++++++++\n 1 file changed, 21 insertions(+)\n\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 69ee13c..3f7e96a 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -115,4 +115,25 @@ test_expect_success 'push with transfer.fsckobjects' '\n \ttest_cmp exp act\n '\n \n+cat >bogus-commit <<\\EOF\n+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+author Bugs Bunny 1234567890 +0000\n+committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n+\n+This commit object intentionally broken\n+EOF\n+\n+test_expect_success 'push with receive.fsck.missingemail=warn' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.missingemail warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\tgrep \"missingemail\" act\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264226","messageId":"dc0aa536c928cb038e66859b7918106b18395aaf.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 10/19] fsck: Make fsck_tag() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:09:08Z","receivedAt":"2015-06-18T20:09:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_tag() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nJust like fsck_commit(), there are certain problems that could hide other\nissues with the same tag object. For example, if the 'type' line is not\nencountered in the correct position, the 'tag' line – if there is any –\nwould not be handled at all.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 856221d..bd4bfc2 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -640,7 +640,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n \t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n-\t\tgoto done;\n+\t\tif (ret)\n+\t\t\tgoto done;\n \t}\n \tbuffer += 41;\n \n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264227","messageId":"02e9e349bbca0c6919e4dd79285232b8eec20d06.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 12/19] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:09:17Z","receivedAt":"2015-06-18T20:09:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some kinds of errors are intrinsically unrecoverable (e.g. errors while\nuncompressing objects). It does not make sense to allow demoting them to\nmere warnings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 14 ++++++++++++--\n t/t5504-fetch-receive-strict.sh | 11 +++++++++++\n 2 files changed, 23 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex bd4bfc2..2b2a360 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,12 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FSCK_FATAL -1\n+\n #define FOREACH_MSG_ID(FUNC) \\\n+\t/* fatal errors */ \\\n+\tFUNC(NUL_IN_HEADER, FATAL) \\\n+\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n \t/* errors */ \\\n \tFUNC(BAD_DATE, ERROR) \\\n \tFUNC(BAD_EMAIL, ERROR) \\\n@@ -40,10 +45,8 @@\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NOT_SORTED, ERROR) \\\n-\tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n \tFUNC(UNKNOWN_TYPE, ERROR) \\\n-\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE, WARN) \\\n@@ -157,6 +160,10 @@ void fsck_set_msg_type(struct fsck_options *options,\n \t\tdie(\"Unhandled message id: %.*s\", msg_id_len, msg_id);\n \ttype = parse_msg_type(msg_type, msg_type_len);\n \n+\tif (type != FSCK_ERROR && msg_id_info[id].msg_type == FSCK_FATAL)\n+\t\tdie(\"Cannot demote %.*s to %.*s\", msg_id_len, msg_id,\n+\t\t\t\tmsg_type_len, msg_type);\n+\n \tif (!options->msg_type) {\n \t\tint i;\n \t\tint *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n@@ -213,6 +220,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tif (msg_type == FSCK_FATAL)\n+\t\tmsg_type = FSCK_ERROR;\n+\n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n \tva_start(ap, fmt);\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 3f7e96a..0d64229 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -136,4 +136,15 @@ test_expect_success 'push with receive.fsck.missingemail=warn' '\n \tgrep \"missingemail\" act\n '\n \n+test_expect_success \\\n+\t'receive.fsck.unterminatedheader=warn triggers error' '\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.unterminatedheader warn &&\n+\ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n+\tgrep \"Cannot demote unterminatedheader\" act\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264228","messageId":"da999d68703a6318d7a2be0f11a79cbf6ccb99a7.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 13/19] fsck: Optionally ignore specific fsck issues completely","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:09:26Z","receivedAt":"2015-06-18T20:09:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An fsck issue in a legacy repository might be so common that one would\nlike not to bother the user with mentioning it at all. With this change,\nthat is possible by setting the respective message type to \"ignore\".\n\nThis change \"abuses\" the missingemail=warn test to verify that \"ignore\"\nis also accepted and works correctly. And while at it, it makes sure\nthat multiple options work, too (they are passed to unpack-objects or\nindex-pack as a comma-separated list via the --strict=... command-line\noption).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 5 +++++\n fsck.h                          | 1 +\n t/t5504-fetch-receive-strict.sh | 9 ++++++++-\n 3 files changed, 14 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 2b2a360..0f7eb22 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -137,6 +137,8 @@ static int parse_msg_type(const char *str, int len)\n \t\treturn FSCK_ERROR;\n \telse if (!substrcmp(str, len, \"warn\"))\n \t\treturn FSCK_WARN;\n+\telse if (!substrcmp(str, len, \"ignore\"))\n+\t\treturn FSCK_IGNORE;\n \telse\n \t\tdie(\"Unknown fsck message type: '%.*s'\",\n \t\t\t\tlen, str);\n@@ -220,6 +222,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tif (msg_type == FSCK_IGNORE)\n+\t\treturn 0;\n+\n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n \ndiff --git a/fsck.h b/fsck.h\nindex 738c9df..7e49372 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -3,6 +3,7 @@\n \n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n+#define FSCK_IGNORE 3\n \n struct fsck_options;\n \ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 0d64229..cb077b7 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -133,7 +133,14 @@ test_expect_success 'push with receive.fsck.missingemail=warn' '\n \tgit --git-dir=dst/.git config \\\n \t\treceive.fsck.missingemail warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missingemail\" act\n+\tgrep \"missingemail\" act &&\n+\tgit --git-dir=dst/.git branch -D bogus &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.missingemail ignore &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.baddate warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\ttest_must_fail grep \"missingemail\" act\n '\n \n test_expect_success \\\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264229","messageId":"6c5ac208f58d995fa07d64aa4b0ef4ae634e85d4.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 14/19] fsck: Allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:09:34Z","receivedAt":"2015-06-18T20:09:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'invalid tag name' and 'missing tagger entry' warnings can now be\nupgraded to errors by specifying `invalidtagname` and\n`missingtaggerentry` in the receive.fsck.<msg-id> config setting.\n\nIncidentally, the missing tagger warning is now really shown as a warning\n(as opposed to being reported with the \"error:\" prefix, as it used to be\nthe case before this commit).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                | 24 +++++++++++++++++-------\n t/t5302-pack-index.sh |  2 +-\n 2 files changed, 18 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 0f7eb22..a5e7dfb 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -10,6 +10,7 @@\n #include \"utf8.h\"\n \n #define FSCK_FATAL -1\n+#define FSCK_INFO -2\n \n #define FOREACH_MSG_ID(FUNC) \\\n \t/* fatal errors */ \\\n@@ -55,10 +56,11 @@\n \tFUNC(HAS_DOT, WARN) \\\n \tFUNC(HAS_DOTDOT, WARN) \\\n \tFUNC(HAS_DOTGIT, WARN) \\\n-\tFUNC(INVALID_TAG_NAME, WARN) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n \tFUNC(NULL_SHA1, WARN) \\\n-\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n+\t/* infos (reported as warnings, but ignored by default) */ \\\n+\tFUNC(INVALID_TAG_NAME, INFO) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -227,6 +229,8 @@ static int report(struct fsck_options *options, struct object *object,\n \n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n+\telse if (msg_type == FSCK_INFO)\n+\t\tmsg_type = FSCK_WARN;\n \n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n@@ -685,15 +689,21 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n-\tif (check_refname_format(sb.buf, 0))\n-\t\treport(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\tif (check_refname_format(sb.buf, 0)) {\n+\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n \t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \tbuffer = eol + 1;\n \n-\tif (!skip_prefix(buffer, \"tagger \", &buffer))\n+\tif (!skip_prefix(buffer, \"tagger \", &buffer)) {\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 61bc8da..3dc5ec4 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -259,7 +259,7 @@ EOF\n     thirtyeight=${tag#??} &&\n     rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&\n     git index-pack --strict tag-test-${pack1}.pack 2>err &&\n-    grep \"^error:.* expected .tagger. line\" err\n+    grep \"^warning:.* expected .tagger. line\" err\n '\n \n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264232","messageId":"dd5502af544216e743196a5c520df5255e4e35ba.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 15/19] fsck: Document the new receive.fsck.<msg-id> options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:09:43Z","receivedAt":"2015-06-18T20:09:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3e37b93..306ab7a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2205,6 +2205,20 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n+receive.fsck.<msg-id>::\n+\tWhen `receive.fsckObjects` is set to true, errors can be switched\n+\tto warnings and vice versa by configuring the `receive.fsck.<msg-id>`\n+\tsetting where the `<msg-id>` is the fsck message ID and the value\n+\tis one of `error`, `warn` or `ignore`. For convenience, fsck prefixes\n+\tthe error/warning with the message ID, e.g. \"missingemail: invalid\n+\tauthor/committer line - missing email\" means that setting\n+\t`receive.fsck.missingemail = ignore` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which would not pass pushing when `receive.fsckObjects = true`, allowing\n+the host to accept repositories with certain known issues but still catch\n+other issues.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264230","messageId":"d6eb0210ac56de264c528887f5ba0f5c67f59d08.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 16/19] fsck: Support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:09:50Z","receivedAt":"2015-06-18T20:09:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already have support in `git receive-pack` to deal with some legacy\nrepositories which have non-fatal issues.\n\nLet's make `git fsck` itself useful with such repositories, too, by\nallowing users to ignore known issues, or at least demote those issues\nto mere warnings.\n\nExample: `git -c fsck.missingemail=ignore fsck` would hide\nproblems with missing emails in author, committer and tagger lines.\n\nIn the same spirit that `git receive-pack`'s usage of the fsck machinery\ndiffers from `git fsck`'s – some of the non-fatal warnings in `git fsck`\nare fatal with `git receive-pack` when receive.fsckObjects = true, for\nexample – we strictly separate the fsck.<msg-id> from the\nreceive.fsck.<msg-id> settings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 11 +++++++++++\n builtin/fsck.c           | 12 ++++++++++++\n t/t1450-fsck.sh          | 11 +++++++++++\n 3 files changed, 34 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 306ab7a..41fd460 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1250,6 +1250,17 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n+fsck.<msg-id>::\n+\tAllows overriding the message type (error, warn or ignore) of a\n+\tspecific message ID such as `missingemail`.\n++\n+For convenience, fsck prefixes the error/warning with the message ID,\n+e.g.  \"missingemail: invalid author/committer line - missing email\" means\n+that setting `fsck.missingemail = ignore` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which cannot be repaired without disruptive changes.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex fff38fe..6de9f3e 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,6 +46,16 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n+static int fsck_config(const char *var, const char *value, void *cb)\n+{\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tfsck_set_msg_type(&fsck_obj_options, var, -1, value, -1);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n static void objreport(struct object *obj, const char *msg_type,\n \t\t\tconst char *err)\n {\n@@ -646,6 +656,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tgit_config(fsck_config, NULL);\n+\n \tfsck_head_link();\n \tfsck_object_dir(get_object_directory());\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 286a643..fe4bb03 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -287,6 +287,17 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '\n \tgrep -q \"error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff\" out\n '\n \n+test_expect_success 'force fsck to ignore double author' '\n+\tgit cat-file commit HEAD >basis &&\n+\tsed \"s/^author .*/&,&/\" <basis | tr , \\\\n >multiple-authors &&\n+\tnew=$(git hash-object -t commit -w --stdin <multiple-authors) &&\n+\ttest_when_finished \"remove_object $new\" &&\n+\tgit update-ref refs/heads/bogus \"$new\" &&\n+\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n+\ttest_must_fail git fsck &&\n+\tgit -c fsck.multipleauthors=ignore fsck\n+'\n+\n _bz='\\0'\n _bz5=\"$_bz$_bz$_bz$_bz$_bz\"\n _bz20=\"$_bz5$_bz5$_bz5$_bz5\"\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264231","messageId":"04b6afa85d18493ec21f58ebfeb46dc2a154ae3f.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:09:58Z","receivedAt":"2015-06-18T20:09:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This option avoids unpacking each and all objects, and just verifies the\nconnectivity. In particular with large repositories, this speeds up the\noperation, at the expense of missing corrupt blobs and ignoring\nunreachable objects, if any.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  7 ++++++-\n builtin/fsck.c             |  7 ++++++-\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 3 files changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 25c431d..b98fb43 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n-\t [--[no-]full] [--strict] [--verbose] [--lost-found]\n+\t [--[no-]full] [--quick] [--strict] [--verbose] [--lost-found]\n \t [--[no-]dangling] [--[no-]progress] [<object>*]\n \n DESCRIPTION\n@@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \tobject pools.  This is now default; you can turn it off\n \twith --no-full.\n \n+--quick::\n+\tCheck only the connectivity of tags, commits and tree objects. By\n+\tavoiding to unpack blobs, this speeds up the operation, at the\n+\texpense of missing corrupt objects.\n+\n --strict::\n \tEnable more strict checking, namely to catch a file mode\n \trecorded with g+w bit set, which was created by older\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 6de9f3e..75fcb5f 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -23,6 +23,7 @@ static int show_tags;\n static int show_unreachable;\n static int include_reflogs = 1;\n static int check_full = 1;\n+static int quick;\n static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n@@ -181,6 +182,8 @@ static void check_reachable_object(struct object *obj)\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (has_sha1_pack(obj->sha1))\n \t\t\treturn; /* it is in pack - forget about it */\n+\t\tif (quick && has_sha1_file(obj->sha1))\n+\t\t\treturn;\n \t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n@@ -623,6 +626,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"cache\", &keep_cache_objects, N_(\"make index objects head nodes\")),\n \tOPT_BOOL(0, \"reflogs\", &include_reflogs, N_(\"make reflogs head nodes (default)\")),\n \tOPT_BOOL(0, \"full\", &check_full, N_(\"also consider packs and alternate objects\")),\n+\tOPT_BOOL(0, \"quick\", &quick, N_(\"check only connectivity\")),\n \tOPT_BOOL(0, \"strict\", &check_strict, N_(\"enable more strict checking\")),\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n@@ -659,7 +663,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tfsck_object_dir(get_object_directory());\n+\tif (!quick)\n+\t\tfsck_object_dir(get_object_directory());\n \n \tprepare_alt_odb();\n \tfor (alt = alt_odb_list; alt; alt = alt->next) {\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex fe4bb03..471e2ea 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n \ttest_must_fail git -C missing fsck\n '\n \n+test_expect_success 'fsck --quick' '\n+\trm -rf quick &&\n+\tgit init quick &&\n+\t(\n+\t\tcd quick &&\n+\t\ttouch empty &&\n+\t\tgit add empty &&\n+\t\ttest_commit empty &&\n+\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n+\t\trm -f $empty &&\n+\t\techo invalid >$empty &&\n+\t\ttest_must_fail git fsck --strict &&\n+\t\tgit fsck --strict --quick &&\n+\t\ttree=$(git rev-parse HEAD:) &&\n+\t\tsuffix=${tree#??} &&\n+\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n+\t\trm -f $tree &&\n+\t\techo invalid >$tree &&\n+\t\ttest_must_fail git fsck --strict --quick\n+\t)\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264234","messageId":"c9da563cf9a3bb031a2b15c39d2a5066b132ee05.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:10:05Z","receivedAt":"2015-06-18T20:10:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The optional new config option `receive.fsck.skiplist` specifies the path\nto a file listing the names, i.e. SHA-1s, one per line, of objects that\nare to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nThe intended use case is for server administrators to inspect objects\nthat are reported by `git push` as being too problematic to enter the\nrepository, and to add the objects' SHA-1 to a (preferably sorted) file\nwhen the objects are legitimate, i.e. when it is determined that those\nproblematic objects should be allowed to enter the server.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt        |  7 ++++++\n builtin/receive-pack.c          |  8 ++++++\n fsck.c                          | 54 +++++++++++++++++++++++++++++++++++++++++\n fsck.h                          |  1 +\n t/t5504-fetch-receive-strict.sh | 12 +++++++++\n 5 files changed, 82 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 41fd460..5f45115 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2230,6 +2230,13 @@ which would not pass pushing when `receive.fsckObjects = true`, allowing\n the host to accept repositories with certain known issues but still catch\n other issues.\n \n+receive.fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 3afe8f8..80574f9 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -117,6 +117,14 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tstrbuf_addf(&fsck_msg_types, \"%cskiplist=%s\",\n+\t\t\tfsck_msg_types.len ? ',' : '=', path);\n+\t\treturn 0;\n+\t}\n+\n \tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n \t\tif (is_valid_msg_type(var, value))\n \t\t\tstrbuf_addf(&fsck_msg_types, \"%c%s=%s\",\ndiff --git a/fsck.c b/fsck.c\nindex a5e7dfb..9b8981e 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -8,6 +8,7 @@\n #include \"fsck.h\"\n #include \"refs.h\"\n #include \"utf8.h\"\n+#include \"sha1-array.h\"\n \n #define FSCK_FATAL -1\n #define FSCK_INFO -2\n@@ -122,6 +123,43 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,\n \treturn msg_type;\n }\n \n+static void init_skiplist(struct fsck_options *options, const char *path)\n+{\n+\tstatic struct sha1_array skiplist = SHA1_ARRAY_INIT;\n+\tint sorted, fd;\n+\tchar buffer[41];\n+\tunsigned char sha1[20];\n+\n+\tif (options->skiplist)\n+\t\tsorted = options->skiplist->sorted;\n+\telse {\n+\t\tsorted = 1;\n+\t\toptions->skiplist = &skiplist;\n+\t}\n+\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Could not open skip list: %s\", path);\n+\tfor (;;) {\n+\t\tint result = read_in_full(fd, buffer, sizeof(buffer));\n+\t\tif (result < 0)\n+\t\t\tdie_errno(\"Could not read '%s'\", path);\n+\t\tif (!result)\n+\t\t\tbreak;\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n+\t\t\tdie(\"Invalid SHA-1: %s\", buffer);\n+\t\tsha1_array_append(&skiplist, sha1);\n+\t\tif (sorted && skiplist.nr > 1 &&\n+\t\t\t\thashcmp(skiplist.sha1[skiplist.nr - 2],\n+\t\t\t\t\tsha1) > 0)\n+\t\t\tsorted = 0;\n+\t}\n+\tclose(fd);\n+\n+\tif (sorted)\n+\t\tskiplist.sorted = 1;\n+}\n+\n static inline int substrcmp(const char *string, int len, const char *match)\n {\n \tint match_len = strlen(match);\n@@ -193,6 +231,18 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n \t\t\tif (values[equal] == '=' || values[equal] == ':')\n \t\t\t\tbreak;\n \n+\t\tif (!substrcmp(values, equal, \"skiplist\")) {\n+\t\t\tchar *path = xstrndup(values + equal + 1,\n+\t\t\t\tlen - equal - 1);\n+\n+\t\t\tif (equal == len)\n+\t\t\t\tdie(\"skiplist requires a path\");\n+\t\t\tinit_skiplist(options, path);\n+\t\t\tfree(path);\n+\t\t\tvalues += len;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (equal == len)\n \t\t\tdie(\"Missing '=': '%.*s'\", len, values);\n \n@@ -227,6 +277,10 @@ static int report(struct fsck_options *options, struct object *object,\n \tif (msg_type == FSCK_IGNORE)\n \t\treturn 0;\n \n+\tif (options->skiplist && object &&\n+\t\t\tsha1_array_lookup(options->skiplist, object->sha1) >= 0)\n+\t\treturn 0;\n+\n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n \telse if (msg_type == FSCK_INFO)\ndiff --git a/fsck.h b/fsck.h\nindex 7e49372..cab9c65 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -33,6 +33,7 @@ struct fsck_options {\n \tfsck_error error_func;\n \tunsigned strict:1;\n \tint *msg_type;\n+\tstruct sha1_array *skiplist;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex cb077b7..1ada54c 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,6 +123,18 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n+test_expect_success 'push with receive.fsck.skiplist' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.skiplist SKIP &&\n+\techo $commit >dst/.git/SKIP &&\n+\tgit push --porcelain dst bogus\n+'\n+\n test_expect_success 'push with receive.fsck.missingemail=warn' '\n \tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264233","messageId":"49c65f7a3409bf7241302f2ea6c102503ddb2b8a.1434657920.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v5 19/19] fsck: support ignoring objects in `git fsck` via fsck.skiplist","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-18T20:10:13Z","receivedAt":"2015-06-18T20:10:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Identical to support in `git receive-pack for the config option\n`receive.fsck.skiplist`, we now support ignoring given objects in\n`git fsck` via `fsck.skiplist` altogether.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt |  7 +++++++\n builtin/fsck.c           | 10 ++++++++++\n 2 files changed, 17 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5f45115..5aba63a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1261,6 +1261,13 @@ that setting `fsck.missingemail = ignore` will hide that issue.\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n \n+fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 75fcb5f..ce538ac 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -54,6 +54,16 @@ static int fsck_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfsck_set_msg_types(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n \treturn git_default_config(var, value, cb);\n }\n \n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264258","messageId":"xmqq8ubgd5vt.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v5 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-18T22:11:02Z","receivedAt":"2015-06-18T22:11:02Z","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> At the moment, the git-fsck's integrity checks are targeted toward the\n> end user, i.e. the error messages are really just messages, intended for\n> human consumption.\n>\n> Under certain circumstances, some of those errors should be allowed to\n> be turned into mere warnings, though, because the cost of fixing the\n> issues might well be larger than the cost of carrying those flawed\n> objects.\n\n> Interdiff below the diffstat. It's huge. Sorry.\n\nHeh, no need to say sorry, though.  A large interdiff means you did\na lot more work, after all.\n\nI haven't had a chance to go through the all the patches, but one\nthing I noticed that did not appear in the interdiff is that some of\nthe message IDs are unclear.  For example, there are BAD_something,\nINVALID_something and MISSING_something.  The last one is in a\ndifferent category and is good, but how are the former two\ndifferenciated?  Do they follow some systematic rules, or they are\nnamed after the way how they happened to be reported in the original\ntextual error message?\n\nSome of the questionable groups are:\n\n    BAD_DATE DATE_OVERFLOW\n\n    BAD_TREE_SHA1 INVALID_OBJECT_SHA1 INVALID_TREE\n\n    BAD_PARENT_SHA1 INVALID_OBJECT_SHA1\n\nAlso it is unclear if NOT_SORTED is to be used ever for any error\nother than a tree object sorted incorrectly, or if we start noticing\na new error that something is not sorted, we will reuse this one.\n\nI also briefly wondered if fsck.skipList should be finer grained\nthan \"these are know to be broken, do not bother reporting problems\nwith them\" (e.g. I know v0.99 lacks \"tagger\" so I want to squelch\nMISSING_TAGGER_ENTRY for it, but I want to be notified on any other\nerrors).  But that only matters if we update Git to a version with a\nnew fsck that knows yet more kinds of breakages, so it is not a huge\nissue, and the simplicity of \"be silent on these objects\" is\nprobably better overall.\n\nThanks.\n"},{"id":"264269","messageId":"e3f2c023e59c3608ebbb7e88a6f18d27@www.dscho.org","threadId":"38144","inReplyTo":"xmqq8ubgd5vt.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v5 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T00:04:52Z","receivedAt":"2015-06-19T00:04:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 00:11, Junio C Hamano wrote:\n\n> I haven't had a chance to go through the all the patches, but one\n> thing I noticed that did not appear in the interdiff is that some of\n> the message IDs are unclear.  For example, there are BAD_something,\n> INVALID_something and MISSING_something.  The last one is in a\n> different category and is good, but how are the former two\n> differenciated?  Do they follow some systematic rules, or they are\n> named after the way how they happened to be reported in the original\n> textual error message?\n\nI basically made up names on the go, based on the messages.\n\n> Some of the questionable groups are:\n> \n>     BAD_DATE DATE_OVERFLOW\n\nI guess it should be BAD_DATE_OVERFLOW to be more consistent?\n\n>     BAD_TREE_SHA1 INVALID_OBJECT_SHA1 INVALID_TREE\n> \n>     BAD_PARENT_SHA1 INVALID_OBJECT_SHA1\n\nSo how about s/INVALID_/BAD_/g?\n\n> Also it is unclear if NOT_SORTED is to be used ever for any error\n> other than a tree object sorted incorrectly, or if we start noticing\n> a new error that something is not sorted, we will reuse this one.\n\ns/NOT_SORTED/TREE_&/ maybe?\n\n> I also briefly wondered if fsck.skipList should be finer grained\n> than \"these are know to be broken, do not bother reporting problems\n> with them\" (e.g. I know v0.99 lacks \"tagger\" so I want to squelch\n> MISSING_TAGGER_ENTRY for it, but I want to be notified on any other\n> errors).  But that only matters if we update Git to a version with a\n> new fsck that knows yet more kinds of breakages, so it is not a huge\n> issue, and the simplicity of \"be silent on these objects\" is\n> probably better overall.\n\nWell, the idea of skiplist is to say: \"I have inspected this object and determined that errors in it should be ignored.\" As such, it does not really matter what problems future Git versions report because the person populating the skiplist is supposed to test thoroughly, not just asking `git fsck` what is going on.\n\nAnd yes, the motivation for this feature is to keep it super-simple. ;-)\n\nCiao,\nDscho\n"},{"id":"264308","messageId":"cover.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434657920.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:32:01Z","receivedAt":"2015-06-19T13:32:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At the moment, the git-fsck's integrity checks are targeted toward the\nend user, i.e. the error messages are really just messages, intended for\nhuman consumption.\n\nUnder certain circumstances, some of those errors should be allowed to\nbe turned into mere warnings, though, because the cost of fixing the\nissues might well be larger than the cost of carrying those flawed\nobjects. For example, when an already-public repository contains a\ncommit object with two authors for years, it does not make sense to\nforce the maintainer to rewrite the history, affecting all contributors\nnegatively by forcing them to update.\n\nThis branch introduces an internal fsck API to be able to turn some of\nthe errors into warnings, and to make it easier to call the fsck\nmachinery from elsewhere in general.\n\nI am proud to report that this work has been sponsored by GitHub.\n\nChanges since v5:\n\n- The BAD_* names have been renamed to INVALID_* names\n\nInterdiff below the diffstat.\n\nJohannes Schindelin (19):\n  fsck: Introduce fsck options\n  fsck: Introduce identifiers for fsck messages\n  fsck: Provide a function to parse fsck message IDs\n  fsck: Offer a function to demote fsck errors to warnings\n  fsck (receive-pack): Allow demoting errors to warnings\n  fsck: Report the ID of the error/warning\n  fsck: Make fsck_ident() warn-friendly\n  fsck: Make fsck_commit() warn-friendly\n  fsck: Handle multiple authors in commits specially\n  fsck: Make fsck_tag() warn-friendly\n  fsck: Add a simple test for receive.fsck.<msg-id>\n  fsck: Disallow demoting grave fsck errors to warnings\n  fsck: Optionally ignore specific fsck issues completely\n  fsck: Allow upgrading fsck warnings to errors\n  fsck: Document the new receive.fsck.<msg-id> options\n  fsck: Support demoting errors to warnings\n  fsck: Introduce `git fsck --quick`\n  fsck: git receive-pack: support excluding objects from fsck'ing\n  fsck: support ignoring objects in `git fsck` via fsck.skiplist\n\n Documentation/config.txt        |  39 +++\n Documentation/git-fsck.txt      |   7 +-\n builtin/fsck.c                  |  75 ++++--\n builtin/index-pack.c            |  13 +-\n builtin/receive-pack.c          |  25 +-\n builtin/unpack-objects.c        |  16 +-\n fsck.c                          | 553 +++++++++++++++++++++++++++++++---------\n fsck.h                          |  31 ++-\n t/t1450-fsck.sh                 |  37 ++-\n t/t5302-pack-index.sh           |   2 +-\n t/t5504-fetch-receive-strict.sh |  51 ++++\n 11 files changed, 686 insertions(+), 163 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 9b8981e..f80b508 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -19,17 +19,17 @@\n \tFUNC(UNTERMINATED_HEADER, FATAL) \\\n \t/* errors */ \\\n \tFUNC(BAD_DATE, ERROR) \\\n+\tFUNC(BAD_DATE_OVERFLOW, ERROR) \\\n \tFUNC(BAD_EMAIL, ERROR) \\\n \tFUNC(BAD_NAME, ERROR) \\\n+\tFUNC(BAD_OBJECT_SHA1, ERROR) \\\n \tFUNC(BAD_PARENT_SHA1, ERROR) \\\n+\tFUNC(BAD_TAG_OBJECT, ERROR) \\\n \tFUNC(BAD_TIMEZONE, ERROR) \\\n+\tFUNC(BAD_TREE, ERROR) \\\n \tFUNC(BAD_TREE_SHA1, ERROR) \\\n-\tFUNC(DATE_OVERFLOW, ERROR) \\\n+\tFUNC(BAD_TYPE, ERROR) \\\n \tFUNC(DUPLICATE_ENTRIES, ERROR) \\\n-\tFUNC(INVALID_OBJECT_SHA1, ERROR) \\\n-\tFUNC(INVALID_TAG_OBJECT, ERROR) \\\n-\tFUNC(INVALID_TREE, ERROR) \\\n-\tFUNC(INVALID_TYPE, ERROR) \\\n \tFUNC(MISSING_AUTHOR, ERROR) \\\n \tFUNC(MISSING_COMMITTER, ERROR) \\\n \tFUNC(MISSING_EMAIL, ERROR) \\\n@@ -46,8 +46,8 @@\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n-\tFUNC(NOT_SORTED, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n+\tFUNC(TREE_NOT_SORTED, ERROR) \\\n \tFUNC(UNKNOWN_TYPE, ERROR) \\\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n@@ -60,7 +60,7 @@\n \tFUNC(NULL_SHA1, WARN) \\\n \tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n \t/* infos (reported as warnings, but ignored by default) */ \\\n-\tFUNC(INVALID_TAG_NAME, INFO) \\\n+\tFUNC(BAD_TAG_NAME, INFO) \\\n \tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n@@ -525,7 +525,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \tif (has_dup_entries)\n \t\tretval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += report(options, &item->object, FSCK_MSG_NOT_SORTED, \"not properly sorted\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, \"not properly sorted\");\n \treturn retval;\n }\n \n@@ -580,7 +580,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \tif (*p == '0' && p[1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(p, &end, 10)))\n-\t\treturn report(options, obj, FSCK_MSG_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n \tif ((end == p || *end != ' '))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n \tp = end + 1;\n@@ -659,7 +659,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn report(options, &commit->object, FSCK_MSG_INVALID_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n@@ -712,7 +712,7 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tif (ret)\n \t\t\tgoto done;\n \t}\n@@ -728,7 +728,7 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TYPE, \"invalid 'type' value\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n@@ -744,7 +744,7 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0)) {\n-\t\tret = report(options, &tag->object, FSCK_MSG_INVALID_TAG_NAME,\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,\n \t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \t\tif (ret)\n@@ -773,7 +773,7 @@ static int fsck_tag(struct tag *tag, const char *data,\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn report(options, &tag->object, FSCK_MSG_INVALID_TAG_OBJECT, \"could not load tagged object\");\n+\t\treturn report(options, &tag->object, FSCK_MSG_BAD_TAG_OBJECT, \"could not load tagged object\");\n \n \treturn fsck_tag_buffer(tag, data, size, options);\n }\n@@ -782,7 +782,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn report(options, obj, FSCK_MSG_INVALID_OBJECT_SHA1, \"no valid object to fsck\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 471e2ea..2863a8a 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,7 +231,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalidtagname: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: badtagname: invalid '\\''tag'\\'' name: wrong name format\n \twarning in tag $tag: missingtaggerentry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264309","messageId":"1befa0a0f1feead1ef9b332bdb175182db1c4b4c.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 01/19] fsck: Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:32:27Z","receivedAt":"2015-06-19T13:32:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Just like the diff machinery, we are about to introduce more settings,\ntherefore it makes sense to carry them around as a (pointer to a) struct\ncontaining all of them.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c           |  20 +++++--\n builtin/index-pack.c     |   9 +--\n builtin/unpack-objects.c |  11 ++--\n fsck.c                   | 150 +++++++++++++++++++++++------------------------\n fsck.h                   |  17 +++++-\n 5 files changed, 114 insertions(+), 93 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2679793..981dca5 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -25,6 +25,8 @@ static int include_reflogs = 1;\n static int check_full = 1;\n static int check_strict;\n static int keep_cache_objects;\n+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n static struct object_id head_oid;\n static const char *head_points_at;\n static int errors_found;\n@@ -76,7 +78,7 @@ static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n \n static struct object_array pending;\n \n-static int mark_object(struct object *obj, int type, void *data)\n+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct object *parent = data;\n \n@@ -119,7 +121,7 @@ static int mark_object(struct object *obj, int type, void *data)\n \n static void mark_object_reachable(struct object *obj)\n {\n-\tmark_object(obj, OBJ_ANY, NULL);\n+\tmark_object(obj, OBJ_ANY, NULL, NULL);\n }\n \n static int traverse_one_object(struct object *obj)\n@@ -132,7 +134,7 @@ static int traverse_one_object(struct object *obj)\n \t\tif (parse_tree(tree) < 0)\n \t\t\treturn 1; /* error already displayed */\n \t}\n-\tresult = fsck_walk(obj, mark_object, obj);\n+\tresult = fsck_walk(obj, obj, &fsck_walk_options);\n \tif (tree)\n \t\tfree_tree_buffer(tree);\n \treturn result;\n@@ -158,7 +160,7 @@ static int traverse_reachable(void)\n \treturn !!result;\n }\n \n-static int mark_used(struct object *obj, int type, void *data)\n+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn 1;\n@@ -296,9 +298,9 @@ static int fsck_obj(struct object *obj)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n \t\t\ttypename(obj->type), sha1_to_hex(obj->sha1));\n \n-\tif (fsck_walk(obj, mark_used, NULL))\n+\tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n-\tif (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))\n+\tif (fsck_object(obj, NULL, 0, &fsck_obj_options))\n \t\treturn -1;\n \n \tif (obj->type == OBJ_TREE) {\n@@ -638,6 +640,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);\n \n+\tfsck_walk_options.walk = mark_object;\n+\tfsck_obj_options.walk = mark_used;\n+\tfsck_obj_options.error_func = fsck_error_func;\n+\tif (check_strict)\n+\t\tfsck_obj_options.strict = 1;\n+\n \tif (show_progress == -1)\n \t\tshow_progress = isatty(2);\n \tif (verbose)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 48fa472..87ae9ba 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -75,6 +75,7 @@ static int nr_threads;\n static int from_stdin;\n static int strict;\n static int do_fsck_object;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n static int verbose;\n static int show_stat;\n static int check_self_contained_and_connected;\n@@ -192,7 +193,7 @@ static void cleanup_thread(void)\n #endif\n \n \n-static int mark_link(struct object *obj, int type, void *data)\n+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -838,10 +839,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (!obj)\n \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(obj, buf, size, 1,\n-\t\t\t\t    fsck_error_function))\n+\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"Error in object\"));\n-\t\t\tif (fsck_walk(obj, mark_link, NULL))\n+\t\t\tfsck_options.walk = mark_link;\n+\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n \n \t\t\tif (obj->type == OBJ_TREE) {\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex ac66672..6d17040 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -20,6 +20,7 @@ static unsigned char buffer[4096];\n static unsigned int offset, len;\n static off_t consumed_bytes;\n static git_SHA_CTX ctx;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n /*\n  * When running under --strict mode, objects whose reachability are\n@@ -178,7 +179,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)\n  * that have reachability requirements and calls this function.\n  * Verify its reachability and validity recursively and write it out.\n  */\n-static int check_object(struct object *obj, int type, void *data)\n+static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct obj_buffer *obj_buf;\n \n@@ -203,10 +204,10 @@ static int check_object(struct object *obj, int type, void *data)\n \tobj_buf = lookup_object_buffer(obj);\n \tif (!obj_buf)\n \t\tdie(\"Whoops! Cannot find object '%s'\", sha1_to_hex(obj->sha1));\n-\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1,\n-\t\t\tfsck_error_function))\n+\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"Error in object\");\n-\tif (fsck_walk(obj, check_object, NULL))\n+\tfsck_options.walk = check_object;\n+\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\tdie(\"Error on reachable objects of %s\", sha1_to_hex(obj->sha1));\n \twrite_cached_object(obj, obj_buf);\n \treturn 0;\n@@ -217,7 +218,7 @@ static void write_rest(void)\n \tunsigned i;\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tif (obj_list[i].obj)\n-\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL);\n+\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);\n \t}\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex 10bcb65..d83b811 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,7 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n-static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\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@@ -25,9 +25,9 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n \t\tif (S_ISDIR(entry.mode))\n-\t\t\tresult = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);\n+\t\t\tresult = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);\n \t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n-\t\t\tresult = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);\n+\t\t\tresult = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n \t\t\t\t\tsha1_to_hex(tree->object.sha1), entry.path, entry.mode);\n@@ -40,7 +40,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \treturn res;\n }\n \n-static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)\n+static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n \tstruct commit_list *parents;\n \tint res;\n@@ -49,14 +49,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \tif (parse_commit(commit))\n \t\treturn -1;\n \n-\tresult = walk((struct object *)commit->tree, OBJ_TREE, data);\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 \twhile (parents) {\n-\t\tresult = walk((struct object *)parents->item, OBJ_COMMIT, data);\n+\t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n@@ -66,14 +66,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \treturn res;\n }\n \n-static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)\n+static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n \tif (parse_tag(tag))\n \t\treturn -1;\n-\treturn walk(tag->tagged, OBJ_ANY, data);\n+\treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -81,11 +81,11 @@ int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n \tcase OBJ_BLOB:\n \t\treturn 0;\n \tcase OBJ_TREE:\n-\t\treturn fsck_walk_tree((struct tree *)obj, walk, data);\n+\t\treturn fsck_walk_tree((struct tree *)obj, data, options);\n \tcase OBJ_COMMIT:\n-\t\treturn fsck_walk_commit((struct commit *)obj, walk, data);\n+\t\treturn fsck_walk_commit((struct commit *)obj, data, options);\n \tcase OBJ_TAG:\n-\t\treturn fsck_walk_tag((struct tag *)obj, walk, data);\n+\t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n \tdefault:\n \t\terror(\"Unknown object type for %s\", sha1_to_hex(obj->sha1));\n \t\treturn -1;\n@@ -138,7 +138,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con\n \treturn c1 < c2 ? 0 : TREE_UNORDERED;\n }\n \n-static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n+static int fsck_tree(struct tree *item, struct fsck_options *options)\n {\n \tint retval;\n \tint has_null_sha1 = 0;\n@@ -194,7 +194,7 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \t\t * bits..\n \t\t */\n \t\tcase S_IFREG | 0664:\n-\t\t\tif (!strict)\n+\t\t\tif (!options->strict)\n \t\t\t\tbreak;\n \t\tdefault:\n \t\t\thas_bad_modes = 1;\n@@ -219,30 +219,30 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n \treturn retval;\n }\n \n static int require_end_of_header(const void *data, unsigned long size,\n-\tstruct object *obj, fsck_error error_func)\n+\tstruct object *obj, struct fsck_options *options)\n {\n \tconst char *buffer = (const char *)data;\n \tunsigned long i;\n@@ -250,7 +250,7 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn error_func(obj, FSCK_ERROR,\n+\t\t\treturn options->error_func(obj, FSCK_ERROR,\n \t\t\t\t\"unterminated header: NUL at offset %d\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n@@ -258,36 +258,36 @@ static int require_end_of_header(const void *data, unsigned long size,\n \t\t}\n \t}\n \n-\treturn error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n }\n \n-static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)\n+static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,30 +295,30 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n \n static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n \tunsigned parent_count, parent_line_count = 0;\n \tint err;\n \n-\tif (require_end_of_header(buffer, size, &commit->object, error_func))\n+\tif (require_end_of_header(buffer, size, &commit->object, options))\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,39 +328,39 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n \n static int fsck_commit(struct commit *commit, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tconst char *buffer = data ?  data : get_commit_buffer(commit, &size);\n-\tint ret = fsck_commit_buffer(commit, buffer, size, error_func);\n+\tint ret = fsck_commit_buffer(commit, buffer, size, options);\n \tif (!data)\n \t\tunuse_commit_buffer(commit, buffer);\n \treturn ret;\n }\n \n static int fsck_tag_buffer(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char sha1[20];\n \tint ret = 0;\n@@ -376,65 +376,65 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n \t\t}\n \t}\n \n-\tif (require_end_of_header(buffer, size, &tag->object, error_func))\n+\tif (require_end_of_header(buffer, size, &tag->object, options))\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n \telse\n-\t\tret = fsck_ident(&buffer, &tag->object, error_func);\n+\t\tret = fsck_ident(&buffer, &tag->object, options);\n \n done:\n \tstrbuf_release(&sb);\n@@ -443,34 +443,34 @@ done:\n }\n \n static int fsck_tag(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n \n-\treturn fsck_tag_buffer(tag, data, size, error_func);\n+\treturn fsck_tag_buffer(tag, data, size, options);\n }\n \n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func)\n+\tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n \tif (obj->type == OBJ_TREE)\n-\t\treturn fsck_tree((struct tree *) obj, strict, error_func);\n+\t\treturn fsck_tree((struct tree *) obj, options);\n \tif (obj->type == OBJ_COMMIT)\n \t\treturn fsck_commit((struct commit *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \tif (obj->type == OBJ_TAG)\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \n-\treturn error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex d1e6387..07d0ab2 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -4,6 +4,8 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n \n+struct fsck_options;\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -12,7 +14,7 @@\n  *     <0\terror signaled and abort\n  *     >0\terror signaled and do not abort\n  */\n-typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);\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 *err, ...);\n@@ -20,6 +22,15 @@ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);\n __attribute__((format (printf, 3, 4)))\n int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n \n+struct fsck_options {\n+\tfsck_walk_func walk;\n+\tfsck_error error_func;\n+\tunsigned strict:1;\n+};\n+\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+\n /* descend in all linked child objects\n  * the return value is:\n  *    -1\terror in processing the object\n@@ -27,9 +38,9 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n  *    >0\treturn value of the first signaled error >0 (in the case of no other errors)\n  *    0\t\teverything OK\n  */\n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n /* If NULL is passed for data, we assume the object is local and read it. */\n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func);\n+\tstruct fsck_options *options);\n \n #endif\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264310","messageId":"d36f30f8a9e4466862699f9f438b937c6279253e.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 02/19] fsck: Introduce identifiers for fsck messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:32:39Z","receivedAt":"2015-06-19T13:32:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Instead of specifying whether a message by the fsck machinery constitutes\nan error or a warning, let's specify an identifier relating to the\nconcrete problem that was encountered. This is necessary for upcoming\nsupport to be able to demote certain errors to warnings.\n\nIn the process, simplify the requirements on the calling code: instead of\nhaving to handle full-blown varargs in every callback, we now send a\nstring buffer ready to be used by the callback.\n\nWe could use a simple enum for the message IDs here, but we want to\nguarantee that the enum values are associated with the appropriate\nmessage types (i.e. error or warning?). Besides, we want to introduce a\nparser in the next commit that maps the string representation to the\nenum value, hence we use the slightly ugly preprocessor construct that\nis extensible for use with said parser.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c |  26 +++-----\n fsck.c         | 201 +++++++++++++++++++++++++++++++++++++++++----------------\n fsck.h         |   5 +-\n 3 files changed, 154 insertions(+), 78 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 981dca5..fff38fe 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,33 +46,23 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n-static void objreport(struct object *obj, const char *severity,\n-                      const char *err, va_list params)\n+static void objreport(struct object *obj, const char *msg_type,\n+\t\t\tconst char *err)\n {\n-\tfprintf(stderr, \"%s in %s %s: \",\n-\t        severity, typename(obj->type), sha1_to_hex(obj->sha1));\n-\tvfprintf(stderr, err, params);\n-\tfputs(\"\\n\", stderr);\n+\tfprintf(stderr, \"%s in %s %s: %s\\n\",\n+\t\tmsg_type, typename(obj->type), sha1_to_hex(obj->sha1), err);\n }\n \n-__attribute__((format (printf, 2, 3)))\n-static int objerror(struct object *obj, const char *err, ...)\n+static int objerror(struct object *obj, const char *err)\n {\n-\tva_list params;\n-\tva_start(params, err);\n \terrors_found |= ERROR_OBJECT;\n-\tobjreport(obj, \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, \"error\", err);\n \treturn -1;\n }\n \n-__attribute__((format (printf, 3, 4)))\n-static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n+static int fsck_error_func(struct object *obj, int type, const char *message)\n {\n-\tva_list params;\n-\tva_start(params, err);\n-\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n \treturn (type == FSCK_WARN) ? 0 : 1;\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex d83b811..ab24618 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,98 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FOREACH_MSG_ID(FUNC) \\\n+\t/* errors */ \\\n+\tFUNC(BAD_DATE, ERROR) \\\n+\tFUNC(BAD_DATE_OVERFLOW, ERROR) \\\n+\tFUNC(BAD_EMAIL, ERROR) \\\n+\tFUNC(BAD_NAME, ERROR) \\\n+\tFUNC(BAD_OBJECT_SHA1, ERROR) \\\n+\tFUNC(BAD_PARENT_SHA1, ERROR) \\\n+\tFUNC(BAD_TAG_OBJECT, ERROR) \\\n+\tFUNC(BAD_TIMEZONE, ERROR) \\\n+\tFUNC(BAD_TREE, ERROR) \\\n+\tFUNC(BAD_TREE_SHA1, ERROR) \\\n+\tFUNC(BAD_TYPE, ERROR) \\\n+\tFUNC(DUPLICATE_ENTRIES, ERROR) \\\n+\tFUNC(MISSING_AUTHOR, ERROR) \\\n+\tFUNC(MISSING_COMMITTER, ERROR) \\\n+\tFUNC(MISSING_EMAIL, ERROR) \\\n+\tFUNC(MISSING_GRAFT, ERROR) \\\n+\tFUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_OBJECT, ERROR) \\\n+\tFUNC(MISSING_PARENT, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_TAG, ERROR) \\\n+\tFUNC(MISSING_TAG_ENTRY, ERROR) \\\n+\tFUNC(MISSING_TAG_OBJECT, ERROR) \\\n+\tFUNC(MISSING_TREE, ERROR) \\\n+\tFUNC(MISSING_TYPE, ERROR) \\\n+\tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(NUL_IN_HEADER, ERROR) \\\n+\tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n+\tFUNC(TREE_NOT_SORTED, ERROR) \\\n+\tFUNC(UNKNOWN_TYPE, ERROR) \\\n+\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n+\tFUNC(ZERO_PADDED_DATE, ERROR) \\\n+\t/* warnings */ \\\n+\tFUNC(BAD_FILEMODE, WARN) \\\n+\tFUNC(BAD_TAG_NAME, WARN) \\\n+\tFUNC(EMPTY_NAME, WARN) \\\n+\tFUNC(FULL_PATHNAME, WARN) \\\n+\tFUNC(HAS_DOT, WARN) \\\n+\tFUNC(HAS_DOTDOT, WARN) \\\n+\tFUNC(HAS_DOTGIT, WARN) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n+\tFUNC(NULL_SHA1, WARN) \\\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\n+#define MSG_ID(id, msg_type) FSCK_MSG_##id,\n+enum fsck_msg_id {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\tFSCK_MSG_MAX\n+};\n+#undef MSG_ID\n+\n+#define MSG_ID(id, msg_type) { FSCK_##msg_type },\n+static struct {\n+\tint msg_type;\n+} msg_id_info[FSCK_MSG_MAX + 1] = {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\t{ -1 }\n+};\n+#undef MSG_ID\n+\n+static int fsck_msg_type(enum fsck_msg_id msg_id,\n+\tstruct fsck_options *options)\n+{\n+\tint msg_type;\n+\n+\tmsg_type = msg_id_info[msg_id].msg_type;\n+\tif (options->strict && msg_type == FSCK_WARN)\n+\t\tmsg_type = FSCK_ERROR;\n+\n+\treturn msg_type;\n+}\n+\n+__attribute__((format (printf, 4, 5)))\n+static int report(struct fsck_options *options, struct object *object,\n+\tenum fsck_msg_id id, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint msg_type = fsck_msg_type(id, options), result;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tresult = options->error_func(object, msg_type, sb.buf);\n+\tstrbuf_release(&sb);\n+\tva_end(ap);\n+\n+\treturn result;\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n@@ -219,25 +311,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NULL_SHA1, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOT, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, \"not properly sorted\");\n \treturn retval;\n }\n \n@@ -250,15 +342,17 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn options->error_func(obj, FSCK_ERROR,\n-\t\t\t\t\"unterminated header: NUL at offset %d\", i);\n+\t\t\treturn report(options, obj,\n+\t\t\t\tFSCK_MSG_NUL_IN_HEADER,\n+\t\t\t\t\"unterminated header: NUL at offset %ld\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n \t\t\t\treturn 0;\n \t\t}\n \t}\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn report(options, obj,\n+\t\tFSCK_MSG_UNTERMINATED_HEADER, \"unterminated header\");\n }\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n@@ -266,28 +360,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,7 +389,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n@@ -312,13 +406,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,23 +422,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n@@ -376,11 +470,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_MISSING_TAG_OBJECT,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_TAG_OBJECT_NOT_TAG,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n@@ -391,48 +487,49 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\treport(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,\n+\t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \n@@ -448,7 +545,7 @@ static int fsck_tag(struct tag *tag, const char *data,\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn report(options, &tag->object, FSCK_MSG_BAD_TAG_OBJECT, \"could not load tagged object\");\n \n \treturn fsck_tag_buffer(tag, data, size, options);\n }\n@@ -457,7 +554,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n@@ -470,22 +567,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n \t\t\tsize, options);\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn report(options, obj, FSCK_MSG_UNKNOWN_TYPE, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...)\n+int fsck_error_function(struct object *obj, int msg_type, const char *message)\n {\n-\tva_list ap;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tstrbuf_addf(&sb, \"object %s:\", sha1_to_hex(obj->sha1));\n-\n-\tva_start(ap, fmt);\n-\tstrbuf_vaddf(&sb, fmt, ap);\n-\tva_end(ap);\n-\n-\terror(\"%s\", sb.buf);\n-\tstrbuf_release(&sb);\n+\terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex 07d0ab2..f6f268a 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -17,10 +17,9 @@ struct fsck_options;\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 *err, ...);\n+typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n \n-__attribute__((format (printf, 3, 4)))\n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n+int fsck_error_function(struct object *obj, int type, const char *message);\n \n struct fsck_options {\n \tfsck_walk_func walk;\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264311","messageId":"8129a20ee6befc74eb45b6225edaaa1b61647d08.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 03/19] fsck: Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:32:56Z","receivedAt":"2015-06-19T13:32:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"These functions will be used in the next commits to allow the user to\nask fsck to handle specific problems differently, e.g. demoting certain\nerrors to warnings. The upcoming `fsck_set_msg_types()` function has to\nhandle partial strings because we would like to be able to parse, say,\n'missingemail=warn,missingtaggerentry=warn' command line parameters\n(which will be passed by receive-pack to index-pack and unpack-objects).\n\nTo make the parsing robust, we generate strings from the enum keys, and\nusing these keys, we match up strings without dashes case-insensitively\nto the corresponding enum values.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 30 ++++++++++++++++++++++++++++--\n 1 file changed, 28 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex ab24618..da5717c 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -63,15 +63,41 @@ enum fsck_msg_id {\n };\n #undef MSG_ID\n \n-#define MSG_ID(id, msg_type) { FSCK_##msg_type },\n+#define STR(x) #x\n+#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n static struct {\n+\tconst char *id_string;\n \tint msg_type;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n-\t{ -1 }\n+\t{ NULL, -1 }\n };\n #undef MSG_ID\n \n+static int parse_msg_id(const char *text, int len)\n+{\n+\tint i, j;\n+\n+\tif (len < 0)\n+\t\tlen = strlen(text);\n+\n+\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n+\t\tconst char *key = msg_id_info[i].id_string;\n+\t\t/* match id_string case-insensitively, without underscores. */\n+\t\tfor (j = 0; j < len; j++) {\n+\t\t\tchar c = *(key++);\n+\t\t\tif (c == '_')\n+\t\t\t\tc = *(key++);\n+\t\t\tif (toupper(text[j]) != c)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (j == len && !*key)\n+\t\t\treturn i;\n+\t}\n+\n+\treturn -1;\n+}\n+\n static int fsck_msg_type(enum fsck_msg_id msg_id,\n \tstruct fsck_options *options)\n {\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264313","messageId":"44acafb2cf0a98e5ad75e3da24ba0e7453e6118f.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:33:12Z","receivedAt":"2015-06-19T13:33:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are legacy repositories out there whose older commits and tags\nhave issues that prevent pushing them when 'receive.fsckObjects' is set.\nOne real-life example is a commit object that has been hand-crafted to\nlist two authors.\n\nOften, it is not possible to fix those issues without disrupting the\nwork with said repositories, yet it is still desirable to perform checks\nby setting `receive.fsckObjects = true`. This commit is the first step\nto allow demoting specific fsck issues to mere warnings.\n\nThe `fsck_set_msg_types()` function added by this commit parses a list\nof settings in the form:\n\n\tmissingemail=warn,badname=warn,...\n\nUnfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\ngit fsck so far, but other call paths (e.g. git index-pack --strict)\nerror out *always* no matter what type was specified. Therefore, we need\nto take extra care to set all message types to FSCK_ERROR by default in\nthose cases.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n fsck.h | 10 ++++++--\n 2 files changed, 87 insertions(+), 5 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex da5717c..8c3caff 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -103,13 +103,85 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,\n {\n \tint msg_type;\n \n-\tmsg_type = msg_id_info[msg_id].msg_type;\n-\tif (options->strict && msg_type == FSCK_WARN)\n-\t\tmsg_type = FSCK_ERROR;\n+\tassert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);\n+\n+\tif (options->msg_type)\n+\t\tmsg_type = options->msg_type[msg_id];\n+\telse {\n+\t\tmsg_type = msg_id_info[msg_id].msg_type;\n+\t\tif (options->strict && msg_type == FSCK_WARN)\n+\t\t\tmsg_type = FSCK_ERROR;\n+\t}\n \n \treturn msg_type;\n }\n \n+static inline int substrcmp(const char *string, int len, const char *match)\n+{\n+\tint match_len = strlen(match);\n+\tif (match_len != len)\n+\t\treturn -1;\n+\treturn memcmp(string, match, len);\n+}\n+\n+static int parse_msg_type(const char *str, int len)\n+{\n+\tif (len < 0)\n+\t\tlen = strlen(str);\n+\n+\tif (!substrcmp(str, len, \"error\"))\n+\t\treturn FSCK_ERROR;\n+\telse if (!substrcmp(str, len, \"warn\"))\n+\t\treturn FSCK_WARN;\n+\telse\n+\t\tdie(\"Unknown fsck message type: '%.*s'\",\n+\t\t\t\tlen, str);\n+}\n+\n+void fsck_set_msg_type(struct fsck_options *options,\n+\t\tconst char *msg_id, int msg_id_len,\n+\t\tconst char *msg_type, int msg_type_len)\n+{\n+\tint id = parse_msg_id(msg_id, msg_id_len), type;\n+\n+\tif (id < 0)\n+\t\tdie(\"Unhandled message id: %.*s\", msg_id_len, msg_id);\n+\ttype = parse_msg_type(msg_type, msg_type_len);\n+\n+\tif (!options->msg_type) {\n+\t\tint i;\n+\t\tint *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\t\tmsg_type[i] = fsck_msg_type(i, options);\n+\t\toptions->msg_type = msg_type;\n+\t}\n+\n+\toptions->msg_type[id] = type;\n+}\n+\n+void fsck_set_msg_types(struct fsck_options *options, const char *values)\n+{\n+\twhile (*values) {\n+\t\tint len = strcspn(values, \" ,|\"), equal;\n+\n+\t\tif (!len) {\n+\t\t\tvalues++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tfor (equal = 0; equal < len; equal++)\n+\t\t\tif (values[equal] == '=' || values[equal] == ':')\n+\t\t\t\tbreak;\n+\n+\t\tif (equal == len)\n+\t\t\tdie(\"Missing '=': '%.*s'\", len, values);\n+\n+\t\tfsck_set_msg_type(options, values, equal,\n+\t\t\t\tvalues + equal + 1, len - equal - 1);\n+\t\tvalues += len;\n+\t}\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -599,6 +671,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \n int fsck_error_function(struct object *obj, int msg_type, const char *message)\n {\n+\tif (msg_type == FSCK_WARN) {\n+\t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n+\t\treturn 0;\n+\t}\n \terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex f6f268a..edb4540 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,11 @@\n \n struct fsck_options;\n \n+void fsck_set_msg_type(struct fsck_options *options,\n+\t\tconst char *msg_id, int msg_id_len,\n+\t\tconst char *msg_type, int msg_type_len);\n+void fsck_set_msg_types(struct fsck_options *options, const char *values);\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -25,10 +30,11 @@ struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n \tunsigned strict:1;\n+\tint *msg_type;\n };\n \n-#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n-#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }\n \n /* descend in all linked child objects\n  * the return value is:\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264315","messageId":"50eca92605316c86ef3fe3e9ba52595c4eced887.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 05/19] fsck (receive-pack): Allow demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:33:25Z","receivedAt":"2015-06-19T13:33:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For example, missing emails in commit and tag objects can be demoted to\nmere warnings with\n\n\tgit config receive.fsck.missingemail=warn\n\nThe value is actually a comma-separated list.\n\nIn case that the same key is listed in multiple receive.fsck.<msg-id>\nlines in the config, the latter configuration wins (this can happen for\nexample when both $HOME/.gitconfig and .git/config contain message type\nsettings).\n\nAs git receive-pack does not actually perform the checks, it hands off\nthe setting to index-pack or unpack-objects in the form of an optional\nargument to the --strict option.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/index-pack.c     |  4 ++++\n builtin/receive-pack.c   | 17 +++++++++++++++--\n builtin/unpack-objects.c |  5 +++++\n fsck.c                   |  8 ++++++++\n fsck.h                   |  1 +\n 5 files changed, 33 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 87ae9ba..98e14fe 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1633,6 +1633,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(arg, \"--strict\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n+\t\t\t} else if (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tdo_fsck_object = 1;\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 94d0571..3afe8f8 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -19,6 +19,7 @@\n #include \"tag.h\"\n #include \"gpg-interface.h\"\n #include \"sigchain.h\"\n+#include \"fsck.h\"\n \n static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n \n@@ -36,6 +37,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n+static struct strbuf fsck_msg_types = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int advertise_atomic_push = 1;\n@@ -115,6 +117,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n+\t\tif (is_valid_msg_type(var, value))\n+\t\t\tstrbuf_addf(&fsck_msg_types, \"%c%s=%s\",\n+\t\t\t\tfsck_msg_types.len ? ',' : '=', var, value);\n+\t\telse\n+\t\t\twarning(\"Skipping unknown msg id '%s'\", var);\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsckobjects\") == 0) {\n \t\treceive_fsck_objects = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1490,7 +1501,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n+\t\t\t\tfsck_msg_types.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1508,7 +1520,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n+\t\t\t\tfsck_msg_types.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 6d17040..7cc086f 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrict = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\n \t\t\t\tstruct pack_header *hdr;\n \t\t\t\tchar *c;\ndiff --git a/fsck.c b/fsck.c\nindex 8c3caff..8e6faa8 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -138,6 +138,14 @@ static int parse_msg_type(const char *str, int len)\n \t\t\t\tlen, str);\n }\n \n+int is_valid_msg_type(const char *msg_id, const char *msg_type)\n+{\n+\tif (parse_msg_id(msg_id, -1) < 0)\n+\t\treturn 0;\n+\tparse_msg_type(msg_type, -1);\n+\treturn 1;\n+}\n+\n void fsck_set_msg_type(struct fsck_options *options,\n \t\tconst char *msg_id, int msg_id_len,\n \t\tconst char *msg_type, int msg_type_len)\ndiff --git a/fsck.h b/fsck.h\nindex edb4540..738c9df 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -10,6 +10,7 @@ void fsck_set_msg_type(struct fsck_options *options,\n \t\tconst char *msg_id, int msg_id_len,\n \t\tconst char *msg_type, int msg_type_len);\n void fsck_set_msg_types(struct fsck_options *options, const char *values);\n+int is_valid_msg_type(const char *msg_id, const char *msg_type);\n \n /*\n  * callback function for fsck_walk\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264314","messageId":"d0c24a7643acd3a1a17ab3abf0ba54ab352b3e41.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 06/19] fsck: Report the ID of the error/warning","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:33:34Z","receivedAt":"2015-06-19T13:33:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some legacy code has objects with non-fatal fsck issues; To enable the\nuser to ignore those issues, let's print out the ID (e.g. when\nencountering \"missingemail\", the user might want to call `git config\n--add receive.fsck.missingemail=warn`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c          | 16 ++++++++++++++++\n t/t1450-fsck.sh |  4 ++--\n 2 files changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 8e6faa8..0b3e18f 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -190,6 +190,20 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n \t}\n }\n \n+static void append_msg_id(struct strbuf *sb, const char *msg_id)\n+{\n+\tfor (;;) {\n+\t\tchar c = *(msg_id)++;\n+\n+\t\tif (!c)\n+\t\t\tbreak;\n+\t\tif (c != '_')\n+\t\t\tstrbuf_addch(sb, tolower(c));\n+\t}\n+\n+\tstrbuf_addstr(sb, \": \");\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -198,6 +212,8 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tappend_msg_id(&sb, msg_id_info[id].id_string);\n+\n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n \tresult = options->error_func(object, msg_type, sb.buf);\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex cfb32b6..d6d3b13 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: badtagname: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missingtaggerentry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264316","messageId":"3e2deca4addda073f9b80e47865d2a5c95cea6e2.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 07/19] fsck: Make fsck_ident() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:33:50Z","receivedAt":"2015-06-19T13:33:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_ident() identifies a problem with the ident, it should still\nadvance the pointer to the next line so that fsck can continue in the\ncase of a mere warning.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 49 +++++++++++++++++++++++++++----------------------\n 1 file changed, 27 insertions(+), 22 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 0b3e18f..9faaf53 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -479,40 +479,45 @@ static int require_end_of_header(const void *data, unsigned long size,\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n+\tconst char *p = *ident;\n \tchar *end;\n \n-\tif (**ident == '<')\n+\t*ident = strchrnul(*ident, '\\n');\n+\tif (**ident == '\\n')\n+\t\t(*ident)++;\n+\n+\tif (*p == '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident == '>')\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p == '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n-\tif (**ident != '<')\n+\tif (*p != '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n-\tif ((*ident)[-1] != ' ')\n+\tif (p[-1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t(*ident)++;\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident != '>')\n+\tp++;\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p != '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n-\t(*ident)++;\n-\tif (**ident != ' ')\n+\tp++;\n+\tif (*p != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n-\t(*ident)++;\n-\tif (**ident == '0' && (*ident)[1] != ' ')\n+\tp++;\n+\tif (*p == '0' && p[1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n-\tif (date_overflows(strtoul(*ident, &end, 10)))\n+\tif (date_overflows(strtoul(p, &end, 10)))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n-\tif (end == *ident || *end != ' ')\n+\tif ((end == p || *end != ' '))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n-\t*ident = end + 1;\n-\tif ((**ident != '+' && **ident != '-') ||\n-\t    !isdigit((*ident)[1]) ||\n-\t    !isdigit((*ident)[2]) ||\n-\t    !isdigit((*ident)[3]) ||\n-\t    !isdigit((*ident)[4]) ||\n-\t    ((*ident)[5] != '\\n'))\n+\tp = end + 1;\n+\tif ((*p != '+' && *p != '-') ||\n+\t    !isdigit(p[1]) ||\n+\t    !isdigit(p[2]) ||\n+\t    !isdigit(p[3]) ||\n+\t    !isdigit(p[4]) ||\n+\t    (p[5] != '\\n'))\n \t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n-\t(*ident) += 6;\n+\tp += 6;\n \treturn 0;\n }\n \n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264319","messageId":"1ce6b2b32cb94d9697056d1181bb1fe396c64a5b.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:33:58Z","receivedAt":"2015-06-19T13:33:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_commit() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nNote that some problems are too problematic to simply ignore. For\nexample, when the header lines are mixed up, we punt after encountering\nan incorrect line. Therefore, demoting certain warnings to errors can\nhide other problems. Example: demoting the missingauthor error to\na warning would hide a problematic committer line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 28 ++++++++++++++++++++--------\n 1 file changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 9faaf53..9fe9f48 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -534,12 +534,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n-\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n+\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n-\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -548,11 +554,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \tif (graft) {\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n-\t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\telse if (graft->nr_parent != parent_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t} else {\n-\t\tif (parent_count != parent_line_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\tif (parent_count != parent_line_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264318","messageId":"eb82bbfc658e8af1db03f7197b38aaaec33fd4ad.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 09/19] fsck: Handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:34:07Z","receivedAt":"2015-06-19T13:34:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This problem has been detected in the wild, and is the primary reason\nto introduce an option to demote certain fsck errors to warnings. Let's\noffer to ignore this particular problem specifically.\n\nTechnically, we could handle such repositories by setting\nreceive.fsck.<msg-id> to missingcommitter=warn, but that could hide\nmissing tree objects in the same commit because we cannot continue\nverifying any commit object after encountering a missing committer line,\nwhile we can continue in the case of multiple author lines.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/fsck.c b/fsck.c\nindex 9fe9f48..0cfa4d0 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -38,6 +38,7 @@\n \tFUNC(MISSING_TREE, ERROR) \\\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n \tFUNC(TREE_NOT_SORTED, ERROR) \\\n@@ -571,6 +572,14 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n+\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n+\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\terr = fsck_ident(&buffer, &commit->object, options);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264317","messageId":"851b7fdb435c193abf0e20d0f0e171849e302aff.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 10/19] fsck: Make fsck_tag() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:34:14Z","receivedAt":"2015-06-19T13:34:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_tag() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nJust like fsck_commit(), there are certain problems that could hide other\nissues with the same tag object. For example, if the 'type' line is not\nencountered in the correct position, the 'tag' line – if there is any –\nwould not be handled at all.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 0cfa4d0..21e3052 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -640,7 +640,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n \t\tret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n-\t\tgoto done;\n+\t\tif (ret)\n+\t\t\tgoto done;\n \t}\n \tbuffer += 41;\n \n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264320","messageId":"18f00f86adf7f9e8a252e857e60d369270fdbe33.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 11/19] fsck: Add a simple test for receive.fsck.<msg-id>","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:34:28Z","receivedAt":"2015-06-19T13:34:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5504-fetch-receive-strict.sh | 21 +++++++++++++++++++++\n 1 file changed, 21 insertions(+)\n\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 69ee13c..3f7e96a 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -115,4 +115,25 @@ test_expect_success 'push with transfer.fsckobjects' '\n \ttest_cmp exp act\n '\n \n+cat >bogus-commit <<\\EOF\n+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+author Bugs Bunny 1234567890 +0000\n+committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n+\n+This commit object intentionally broken\n+EOF\n+\n+test_expect_success 'push with receive.fsck.missingemail=warn' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.missingemail warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\tgrep \"missingemail\" act\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264321","messageId":"c87d71326fc75773f137eee0ef6a8964645f0b74.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 12/19] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:34:36Z","receivedAt":"2015-06-19T13:34:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some kinds of errors are intrinsically unrecoverable (e.g. errors while\nuncompressing objects). It does not make sense to allow demoting them to\nmere warnings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 14 ++++++++++++--\n t/t5504-fetch-receive-strict.sh | 11 +++++++++++\n 2 files changed, 23 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 21e3052..a4fbce3 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,12 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FSCK_FATAL -1\n+\n #define FOREACH_MSG_ID(FUNC) \\\n+\t/* fatal errors */ \\\n+\tFUNC(NUL_IN_HEADER, FATAL) \\\n+\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n \t/* errors */ \\\n \tFUNC(BAD_DATE, ERROR) \\\n \tFUNC(BAD_DATE_OVERFLOW, ERROR) \\\n@@ -39,11 +44,9 @@\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n-\tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n \tFUNC(TREE_NOT_SORTED, ERROR) \\\n \tFUNC(UNKNOWN_TYPE, ERROR) \\\n-\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE, WARN) \\\n@@ -157,6 +160,10 @@ void fsck_set_msg_type(struct fsck_options *options,\n \t\tdie(\"Unhandled message id: %.*s\", msg_id_len, msg_id);\n \ttype = parse_msg_type(msg_type, msg_type_len);\n \n+\tif (type != FSCK_ERROR && msg_id_info[id].msg_type == FSCK_FATAL)\n+\t\tdie(\"Cannot demote %.*s to %.*s\", msg_id_len, msg_id,\n+\t\t\t\tmsg_type_len, msg_type);\n+\n \tif (!options->msg_type) {\n \t\tint i;\n \t\tint *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n@@ -213,6 +220,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tif (msg_type == FSCK_FATAL)\n+\t\tmsg_type = FSCK_ERROR;\n+\n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n \tva_start(ap, fmt);\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 3f7e96a..0d64229 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -136,4 +136,15 @@ test_expect_success 'push with receive.fsck.missingemail=warn' '\n \tgrep \"missingemail\" act\n '\n \n+test_expect_success \\\n+\t'receive.fsck.unterminatedheader=warn triggers error' '\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.unterminatedheader warn &&\n+\ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n+\tgrep \"Cannot demote unterminatedheader\" act\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264322","messageId":"12e0b6157d95cf259b43a167dfa6e2b77dba60a6.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 13/19] fsck: Optionally ignore specific fsck issues completely","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:34:47Z","receivedAt":"2015-06-19T13:34:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An fsck issue in a legacy repository might be so common that one would\nlike not to bother the user with mentioning it at all. With this change,\nthat is possible by setting the respective message type to \"ignore\".\n\nThis change \"abuses\" the missingemail=warn test to verify that \"ignore\"\nis also accepted and works correctly. And while at it, it makes sure\nthat multiple options work, too (they are passed to unpack-objects or\nindex-pack as a comma-separated list via the --strict=... command-line\noption).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 5 +++++\n fsck.h                          | 1 +\n t/t5504-fetch-receive-strict.sh | 9 ++++++++-\n 3 files changed, 14 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex a4fbce3..b728646 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -137,6 +137,8 @@ static int parse_msg_type(const char *str, int len)\n \t\treturn FSCK_ERROR;\n \telse if (!substrcmp(str, len, \"warn\"))\n \t\treturn FSCK_WARN;\n+\telse if (!substrcmp(str, len, \"ignore\"))\n+\t\treturn FSCK_IGNORE;\n \telse\n \t\tdie(\"Unknown fsck message type: '%.*s'\",\n \t\t\t\tlen, str);\n@@ -220,6 +222,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tif (msg_type == FSCK_IGNORE)\n+\t\treturn 0;\n+\n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n \ndiff --git a/fsck.h b/fsck.h\nindex 738c9df..7e49372 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -3,6 +3,7 @@\n \n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n+#define FSCK_IGNORE 3\n \n struct fsck_options;\n \ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 0d64229..cb077b7 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -133,7 +133,14 @@ test_expect_success 'push with receive.fsck.missingemail=warn' '\n \tgit --git-dir=dst/.git config \\\n \t\treceive.fsck.missingemail warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missingemail\" act\n+\tgrep \"missingemail\" act &&\n+\tgit --git-dir=dst/.git branch -D bogus &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.missingemail ignore &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.baddate warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\ttest_must_fail grep \"missingemail\" act\n '\n \n test_expect_success \\\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264325","messageId":"49189b5b9e8ad194c6408b30db6f493c53595021.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 14/19] fsck: Allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:34:54Z","receivedAt":"2015-06-19T13:34:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'invalid tag name' and 'missing tagger entry' warnings can now be\nupgraded to errors by specifying `invalidtagname` and\n`missingtaggerentry` in the receive.fsck.<msg-id> config setting.\n\nIncidentally, the missing tagger warning is now really shown as a warning\n(as opposed to being reported with the \"error:\" prefix, as it used to be\nthe case before this commit).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                | 24 +++++++++++++++++-------\n t/t5302-pack-index.sh |  2 +-\n 2 files changed, 18 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex b728646..dedad01 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -10,6 +10,7 @@\n #include \"utf8.h\"\n \n #define FSCK_FATAL -1\n+#define FSCK_INFO -2\n \n #define FOREACH_MSG_ID(FUNC) \\\n \t/* fatal errors */ \\\n@@ -50,15 +51,16 @@\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE, WARN) \\\n-\tFUNC(BAD_TAG_NAME, WARN) \\\n \tFUNC(EMPTY_NAME, WARN) \\\n \tFUNC(FULL_PATHNAME, WARN) \\\n \tFUNC(HAS_DOT, WARN) \\\n \tFUNC(HAS_DOTDOT, WARN) \\\n \tFUNC(HAS_DOTGIT, WARN) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n \tFUNC(NULL_SHA1, WARN) \\\n-\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n+\t/* infos (reported as warnings, but ignored by default) */ \\\n+\tFUNC(BAD_TAG_NAME, INFO) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -227,6 +229,8 @@ static int report(struct fsck_options *options, struct object *object,\n \n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n+\telse if (msg_type == FSCK_INFO)\n+\t\tmsg_type = FSCK_WARN;\n \n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n@@ -685,15 +689,21 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n-\tif (check_refname_format(sb.buf, 0))\n-\t\treport(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,\n+\tif (check_refname_format(sb.buf, 0)) {\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,\n \t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \tbuffer = eol + 1;\n \n-\tif (!skip_prefix(buffer, \"tagger \", &buffer))\n+\tif (!skip_prefix(buffer, \"tagger \", &buffer)) {\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 61bc8da..3dc5ec4 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -259,7 +259,7 @@ EOF\n     thirtyeight=${tag#??} &&\n     rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&\n     git index-pack --strict tag-test-${pack1}.pack 2>err &&\n-    grep \"^error:.* expected .tagger. line\" err\n+    grep \"^warning:.* expected .tagger. line\" err\n '\n \n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264323","messageId":"e6abae4f1ccd972946b788a7a76406ac9134856f.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 15/19] fsck: Document the new receive.fsck.<msg-id> options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:35:05Z","receivedAt":"2015-06-19T13:35:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3e37b93..306ab7a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2205,6 +2205,20 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n+receive.fsck.<msg-id>::\n+\tWhen `receive.fsckObjects` is set to true, errors can be switched\n+\tto warnings and vice versa by configuring the `receive.fsck.<msg-id>`\n+\tsetting where the `<msg-id>` is the fsck message ID and the value\n+\tis one of `error`, `warn` or `ignore`. For convenience, fsck prefixes\n+\tthe error/warning with the message ID, e.g. \"missingemail: invalid\n+\tauthor/committer line - missing email\" means that setting\n+\t`receive.fsck.missingemail = ignore` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which would not pass pushing when `receive.fsckObjects = true`, allowing\n+the host to accept repositories with certain known issues but still catch\n+other issues.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264327","messageId":"770c9e16cddb803296bfb75d3067f0cf238f0221.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 16/19] fsck: Support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:35:12Z","receivedAt":"2015-06-19T13:35:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already have support in `git receive-pack` to deal with some legacy\nrepositories which have non-fatal issues.\n\nLet's make `git fsck` itself useful with such repositories, too, by\nallowing users to ignore known issues, or at least demote those issues\nto mere warnings.\n\nExample: `git -c fsck.missingemail=ignore fsck` would hide\nproblems with missing emails in author, committer and tagger lines.\n\nIn the same spirit that `git receive-pack`'s usage of the fsck machinery\ndiffers from `git fsck`'s – some of the non-fatal warnings in `git fsck`\nare fatal with `git receive-pack` when receive.fsckObjects = true, for\nexample – we strictly separate the fsck.<msg-id> from the\nreceive.fsck.<msg-id> settings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 11 +++++++++++\n builtin/fsck.c           | 12 ++++++++++++\n t/t1450-fsck.sh          | 11 +++++++++++\n 3 files changed, 34 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 306ab7a..41fd460 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1250,6 +1250,17 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n+fsck.<msg-id>::\n+\tAllows overriding the message type (error, warn or ignore) of a\n+\tspecific message ID such as `missingemail`.\n++\n+For convenience, fsck prefixes the error/warning with the message ID,\n+e.g.  \"missingemail: invalid author/committer line - missing email\" means\n+that setting `fsck.missingemail = ignore` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which cannot be repaired without disruptive changes.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex fff38fe..6de9f3e 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,6 +46,16 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n+static int fsck_config(const char *var, const char *value, void *cb)\n+{\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tfsck_set_msg_type(&fsck_obj_options, var, -1, value, -1);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n static void objreport(struct object *obj, const char *msg_type,\n \t\t\tconst char *err)\n {\n@@ -646,6 +656,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tgit_config(fsck_config, NULL);\n+\n \tfsck_head_link();\n \tfsck_object_dir(get_object_directory());\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex d6d3b13..922c346 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -287,6 +287,17 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '\n \tgrep -q \"error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff\" out\n '\n \n+test_expect_success 'force fsck to ignore double author' '\n+\tgit cat-file commit HEAD >basis &&\n+\tsed \"s/^author .*/&,&/\" <basis | tr , \\\\n >multiple-authors &&\n+\tnew=$(git hash-object -t commit -w --stdin <multiple-authors) &&\n+\ttest_when_finished \"remove_object $new\" &&\n+\tgit update-ref refs/heads/bogus \"$new\" &&\n+\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n+\ttest_must_fail git fsck &&\n+\tgit -c fsck.multipleauthors=ignore fsck\n+'\n+\n _bz='\\0'\n _bz5=\"$_bz$_bz$_bz$_bz$_bz\"\n _bz20=\"$_bz5$_bz5$_bz5$_bz5\"\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264326","messageId":"5f1c4c16027b00ef80490d67bec5e948481153ec.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:35:27Z","receivedAt":"2015-06-19T13:35:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This option avoids unpacking each and all objects, and just verifies the\nconnectivity. In particular with large repositories, this speeds up the\noperation, at the expense of missing corrupt blobs and ignoring\nunreachable objects, if any.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  7 ++++++-\n builtin/fsck.c             |  7 ++++++-\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 3 files changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 25c431d..b98fb43 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n-\t [--[no-]full] [--strict] [--verbose] [--lost-found]\n+\t [--[no-]full] [--quick] [--strict] [--verbose] [--lost-found]\n \t [--[no-]dangling] [--[no-]progress] [<object>*]\n \n DESCRIPTION\n@@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \tobject pools.  This is now default; you can turn it off\n \twith --no-full.\n \n+--quick::\n+\tCheck only the connectivity of tags, commits and tree objects. By\n+\tavoiding to unpack blobs, this speeds up the operation, at the\n+\texpense of missing corrupt objects.\n+\n --strict::\n \tEnable more strict checking, namely to catch a file mode\n \trecorded with g+w bit set, which was created by older\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 6de9f3e..75fcb5f 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -23,6 +23,7 @@ static int show_tags;\n static int show_unreachable;\n static int include_reflogs = 1;\n static int check_full = 1;\n+static int quick;\n static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n@@ -181,6 +182,8 @@ static void check_reachable_object(struct object *obj)\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (has_sha1_pack(obj->sha1))\n \t\t\treturn; /* it is in pack - forget about it */\n+\t\tif (quick && has_sha1_file(obj->sha1))\n+\t\t\treturn;\n \t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n@@ -623,6 +626,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"cache\", &keep_cache_objects, N_(\"make index objects head nodes\")),\n \tOPT_BOOL(0, \"reflogs\", &include_reflogs, N_(\"make reflogs head nodes (default)\")),\n \tOPT_BOOL(0, \"full\", &check_full, N_(\"also consider packs and alternate objects\")),\n+\tOPT_BOOL(0, \"quick\", &quick, N_(\"check only connectivity\")),\n \tOPT_BOOL(0, \"strict\", &check_strict, N_(\"enable more strict checking\")),\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n@@ -659,7 +663,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tfsck_object_dir(get_object_directory());\n+\tif (!quick)\n+\t\tfsck_object_dir(get_object_directory());\n \n \tprepare_alt_odb();\n \tfor (alt = alt_odb_list; alt; alt = alt->next) {\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 922c346..2863a8a 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n \ttest_must_fail git -C missing fsck\n '\n \n+test_expect_success 'fsck --quick' '\n+\trm -rf quick &&\n+\tgit init quick &&\n+\t(\n+\t\tcd quick &&\n+\t\ttouch empty &&\n+\t\tgit add empty &&\n+\t\ttest_commit empty &&\n+\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n+\t\trm -f $empty &&\n+\t\techo invalid >$empty &&\n+\t\ttest_must_fail git fsck --strict &&\n+\t\tgit fsck --strict --quick &&\n+\t\ttree=$(git rev-parse HEAD:) &&\n+\t\tsuffix=${tree#??} &&\n+\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n+\t\trm -f $tree &&\n+\t\techo invalid >$tree &&\n+\t\ttest_must_fail git fsck --strict --quick\n+\t)\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264328","messageId":"e843f9f1defca543d3f2eb3143cf9fee8c72f695.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:35:34Z","receivedAt":"2015-06-19T13:35:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The optional new config option `receive.fsck.skiplist` specifies the path\nto a file listing the names, i.e. SHA-1s, one per line, of objects that\nare to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nThe intended use case is for server administrators to inspect objects\nthat are reported by `git push` as being too problematic to enter the\nrepository, and to add the objects' SHA-1 to a (preferably sorted) file\nwhen the objects are legitimate, i.e. when it is determined that those\nproblematic objects should be allowed to enter the server.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt        |  7 ++++++\n builtin/receive-pack.c          |  8 ++++++\n fsck.c                          | 54 +++++++++++++++++++++++++++++++++++++++++\n fsck.h                          |  1 +\n t/t5504-fetch-receive-strict.sh | 12 +++++++++\n 5 files changed, 82 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 41fd460..5f45115 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2230,6 +2230,13 @@ which would not pass pushing when `receive.fsckObjects = true`, allowing\n the host to accept repositories with certain known issues but still catch\n other issues.\n \n+receive.fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 3afe8f8..80574f9 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -117,6 +117,14 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tstrbuf_addf(&fsck_msg_types, \"%cskiplist=%s\",\n+\t\t\tfsck_msg_types.len ? ',' : '=', path);\n+\t\treturn 0;\n+\t}\n+\n \tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n \t\tif (is_valid_msg_type(var, value))\n \t\t\tstrbuf_addf(&fsck_msg_types, \"%c%s=%s\",\ndiff --git a/fsck.c b/fsck.c\nindex dedad01..f80b508 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -8,6 +8,7 @@\n #include \"fsck.h\"\n #include \"refs.h\"\n #include \"utf8.h\"\n+#include \"sha1-array.h\"\n \n #define FSCK_FATAL -1\n #define FSCK_INFO -2\n@@ -122,6 +123,43 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,\n \treturn msg_type;\n }\n \n+static void init_skiplist(struct fsck_options *options, const char *path)\n+{\n+\tstatic struct sha1_array skiplist = SHA1_ARRAY_INIT;\n+\tint sorted, fd;\n+\tchar buffer[41];\n+\tunsigned char sha1[20];\n+\n+\tif (options->skiplist)\n+\t\tsorted = options->skiplist->sorted;\n+\telse {\n+\t\tsorted = 1;\n+\t\toptions->skiplist = &skiplist;\n+\t}\n+\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Could not open skip list: %s\", path);\n+\tfor (;;) {\n+\t\tint result = read_in_full(fd, buffer, sizeof(buffer));\n+\t\tif (result < 0)\n+\t\t\tdie_errno(\"Could not read '%s'\", path);\n+\t\tif (!result)\n+\t\t\tbreak;\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n+\t\t\tdie(\"Invalid SHA-1: %s\", buffer);\n+\t\tsha1_array_append(&skiplist, sha1);\n+\t\tif (sorted && skiplist.nr > 1 &&\n+\t\t\t\thashcmp(skiplist.sha1[skiplist.nr - 2],\n+\t\t\t\t\tsha1) > 0)\n+\t\t\tsorted = 0;\n+\t}\n+\tclose(fd);\n+\n+\tif (sorted)\n+\t\tskiplist.sorted = 1;\n+}\n+\n static inline int substrcmp(const char *string, int len, const char *match)\n {\n \tint match_len = strlen(match);\n@@ -193,6 +231,18 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n \t\t\tif (values[equal] == '=' || values[equal] == ':')\n \t\t\t\tbreak;\n \n+\t\tif (!substrcmp(values, equal, \"skiplist\")) {\n+\t\t\tchar *path = xstrndup(values + equal + 1,\n+\t\t\t\tlen - equal - 1);\n+\n+\t\t\tif (equal == len)\n+\t\t\t\tdie(\"skiplist requires a path\");\n+\t\t\tinit_skiplist(options, path);\n+\t\t\tfree(path);\n+\t\t\tvalues += len;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (equal == len)\n \t\t\tdie(\"Missing '=': '%.*s'\", len, values);\n \n@@ -227,6 +277,10 @@ static int report(struct fsck_options *options, struct object *object,\n \tif (msg_type == FSCK_IGNORE)\n \t\treturn 0;\n \n+\tif (options->skiplist && object &&\n+\t\t\tsha1_array_lookup(options->skiplist, object->sha1) >= 0)\n+\t\treturn 0;\n+\n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n \telse if (msg_type == FSCK_INFO)\ndiff --git a/fsck.h b/fsck.h\nindex 7e49372..cab9c65 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -33,6 +33,7 @@ struct fsck_options {\n \tfsck_error error_func;\n \tunsigned strict:1;\n \tint *msg_type;\n+\tstruct sha1_array *skiplist;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex cb077b7..1ada54c 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,6 +123,18 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n+test_expect_success 'push with receive.fsck.skiplist' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.skiplist SKIP &&\n+\techo $commit >dst/.git/SKIP &&\n+\tgit push --porcelain dst bogus\n+'\n+\n test_expect_success 'push with receive.fsck.missingemail=warn' '\n \tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264324","messageId":"692a41377d0bd47934170ea9b22a68f9dd893f38.1434720655.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v6 19/19] fsck: support ignoring objects in `git fsck` via fsck.skiplist","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T13:35:41Z","receivedAt":"2015-06-19T13:35:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Identical to support in `git receive-pack for the config option\n`receive.fsck.skiplist`, we now support ignoring given objects in\n`git fsck` via `fsck.skiplist` altogether.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt |  7 +++++++\n builtin/fsck.c           | 10 ++++++++++\n 2 files changed, 17 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5f45115..5aba63a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1261,6 +1261,13 @@ that setting `fsck.missingemail = ignore` will hide that issue.\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n \n+fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 75fcb5f..ce538ac 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -54,6 +54,16 @@ static int fsck_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n+\t\tconst char *path = is_absolute_path(value) ?\n+\t\t\tvalue : git_path(\"%s\", value);\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfsck_set_msg_types(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n \treturn git_default_config(var, value, cb);\n }\n \n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264344","messageId":"xmqqfv5na9hd.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"e3f2c023e59c3608ebbb7e88a6f18d27@www.dscho.org","subject":"Re: [PATCH v5 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T17:33:50Z","receivedAt":"2015-06-19T17:33:50Z","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> I basically made up names on the go, based on the messages.\n>\n>> Some of the questionable groups are:\n>> \n>>     BAD_DATE DATE_OVERFLOW\n>\n> I guess it should be BAD_DATE_OVERFLOW to be more consistent?\n\nI am not sure about \"consistency\", but surely a common prefix would\nhelp readers to group things.  But for this particular group, I was\nwondering if singling out \"integer overflow\", \"zero stuffed\ntimestamp\", etc. into such a finer sub-errors of \"you have a bad\ntimestamp\" was beneficial.\n\n>>     BAD_TREE_SHA1 INVALID_OBJECT_SHA1 INVALID_TREE\n>> \n>>     BAD_PARENT_SHA1 INVALID_OBJECT_SHA1\n>\n> So how about s/INVALID_/BAD_/g?\n\nIt is not just about distinction between INVAID and BAD.\n\nI was basically wondering what rule decides which one among\nBAD_TREE_SHA1, INVALID_OBJECT_SHA1 and INVALID_TREE I would get when\nI have a random non-hexdigit string in various places, e.g. after\n'tree ' in the object header of a commit object, after 'tag ' in a\ntag object that says 'type tree', etc.\n\n>> Also it is unclear if NOT_SORTED is to be used ever for any error\n>> other than a tree object sorted incorrectly, or if we start noticing\n>> a new error that something is not sorted, we will reuse this one.\n>\n> s/NOT_SORTED/TREE_&/ maybe?\n\nIf that error is specific to tree sorting order, then that would be\na definite improvement.\n\nThanks.\n"},{"id":"264357","messageId":"xmqqd20r8qrm.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"1befa0a0f1feead1ef9b332bdb175182db1c4b4c.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 01/19] fsck: Introduce fsck options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T19:03:25Z","receivedAt":"2015-06-19T19:03:25Z","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> diff --git a/builtin/index-pack.c b/builtin/index-pack.c\n> index 48fa472..87ae9ba 100644\n> --- a/builtin/index-pack.c\n> +++ b/builtin/index-pack.c\n> @@ -75,6 +75,7 @@ static int nr_threads;\n>  static int from_stdin;\n>  static int strict;\n>  static int do_fsck_object;\n> +static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n\nSo there is a global fsck_options used throughout the entire\nsession here.\n\n> @@ -838,10 +839,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n>  \t\t\tif (!obj)\n>  \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n>  \t\t\tif (do_fsck_object &&\n> -\t\t\t    fsck_object(obj, buf, size, 1,\n> -\t\t\t\t    fsck_error_function))\n> +\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n>  \t\t\t\tdie(_(\"Error in object\"));\n\nAnd that is used here to inspect each and every object we encounter.\n\n> -\t\t\tif (fsck_walk(obj, mark_link, NULL))\n> +\t\t\tfsck_options.walk = mark_link;\n\nThen we do a call to fsck_walk() starting from this object, letting\nmark_link() to inspect it and set the LINK bit.\n\n> +\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n>  \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n\nSince nobody else sets fsck_options.walk to any other value, and\nnobody else calls fsck_walk(), shouldn't that assignment be done\nonly once somewhere a lot higher in the callchain?  The apparent\n\"overriding while inspecting this object\" that does not have any\ncorresponding \"now we are done, so revert it to the original value\"\npuzzled me, and I am sure it would puzzle future readers of this\ncode.\n"},{"id":"264358","messageId":"xmqq8ubf8qlp.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"d36f30f8a9e4466862699f9f438b937c6279253e.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 02/19] fsck: Introduce identifiers for fsck messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T19:06:58Z","receivedAt":"2015-06-19T19:06:58Z","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> Instead of specifying whether a message by the fsck machinery constitutes\n> an error or a warning, let's specify an identifier relating to the\n> concrete problem that was encountered. This is necessary for upcoming\n> support to be able to demote certain errors to warnings.\n>\n> In the process, simplify the requirements on the calling code: instead of\n> having to handle full-blown varargs in every callback, we now send a\n> string buffer ready to be used by the callback.\n>\n> We could use a simple enum for the message IDs here, but we want to\n> guarantee that the enum values are associated with the appropriate\n> message types (i.e. error or warning?). Besides, we want to introduce a\n> parser in the next commit that maps the string representation to the\n> enum value, hence we use the slightly ugly preprocessor construct that\n> is extensible for use with said parser.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n\nNicely implemented.  Looks good.\n"},{"id":"264360","messageId":"xmqq4mm38qbk.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"8129a20ee6befc74eb45b6225edaaa1b61647d08.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 03/19] fsck: Provide a function to parse fsck message IDs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T19:13:03Z","receivedAt":"2015-06-19T19:13:03Z","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> +#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n>  static struct {\n> +\tconst char *id_string;\n>  \tint msg_type;\n>  } msg_id_info[FSCK_MSG_MAX + 1] = {\n>  \tFOREACH_MSG_ID(MSG_ID)\n> -\t{ -1 }\n> +\t{ NULL, -1 }\n>  };\n>  #undef MSG_ID\n>  \n> +static int parse_msg_id(const char *text, int len)\n> +{\n> +\tint i, j;\n> +\n> +\tif (len < 0)\n> +\t\tlen = strlen(text);\n> +\n> +\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n\nI wonder an array without sentinel at the end with ARRAY_SIZE() may\nbe a leaner way to do these, especially as this is all limited to\nthis single file.\n\n> +\t\tconst char *key = msg_id_info[i].id_string;\n> +\t\t/* match id_string case-insensitively, without underscores. */\n> +\t\tfor (j = 0; j < len; j++) {\n> +\t\t\tchar c = *(key++);\n> +\t\t\tif (c == '_')\n> +\t\t\t\tc = *(key++);\n\ns/if/while/ perhaps?\n\n> +\t\t\tif (toupper(text[j]) != c)\n\nI know the performance would not matter very much but calling\ntoupper() for each letter in the user input FSCK_MSG_MAX times\nsounds rather inefficient.\n\nWould it make sense to make the caller upcase instead (or upcase\nupfront in the function)?\n\n> +\t\t\t\tbreak;\n> +\t\t}\n> +\t\tif (j == len && !*key)\n> +\t\t\treturn i;\n> +\t}\n> +\n> +\treturn -1;\n> +}\n> +\n>  static int fsck_msg_type(enum fsck_msg_id msg_id,\n>  \tstruct fsck_options *options)\n>  {\n"},{"id":"264361","messageId":"xmqqzj3v7b58.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"44acafb2cf0a98e5ad75e3da24ba0e7453e6118f.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T19:26:11Z","receivedAt":"2015-06-19T19:26:11Z","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> diff --git a/fsck.c b/fsck.c\n> index da5717c..8c3caff 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -103,13 +103,85 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,\n>  {\n>  \tint msg_type;\n>  \n> -\tmsg_type = msg_id_info[msg_id].msg_type;\n> -\tif (options->strict && msg_type == FSCK_WARN)\n> -\t\tmsg_type = FSCK_ERROR;\n> +\tassert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);\n> +\n> +\tif (options->msg_type)\n> +\t\tmsg_type = options->msg_type[msg_id];\n> +\telse {\n> +\t\tmsg_type = msg_id_info[msg_id].msg_type;\n> +\t\tif (options->strict && msg_type == FSCK_WARN)\n> +\t\t\tmsg_type = FSCK_ERROR;\n> +\t}\n>  \n>  \treturn msg_type;\n>  }\n\nNice.\n\n> +static inline int substrcmp(const char *string, int len, const char *match)\n> +{\n> +\tint match_len = strlen(match);\n> +\tif (match_len != len)\n> +\t\treturn -1;\n> +\treturn memcmp(string, match, len);\n> +}\n\nWhat this does looks suspiciously like starts_with(), but its name\n\"substrcmp()\" does not give any hint that this is about the beginnig\npart of \"string\"; if anything, it gives a wrong hint that it may be\nany substring.  prefixcmp() might be a better name but that was the\nold name for !starts_with() so we cannot use it here.  It is a\nmouthful, but starts_with_counted() may be.\n\nBut the whole thing may be moot.\n\nIf we take the \"why not upcase the end-user string upfront\"\nsuggestion from the previous review, fsck_set_msg_types() would have\nan upcased copy of the end-user string that it can muck with; it can\nturn \"badfoo=error,poorbar=...\" into \"BADFOO=error,POORBAR=...\"\nthat is stored in its own writable memory (possibly a strbuf), and\nat that point it can afford to NUL-terminate BADFOO=error after\nfinding where one specification ends with strcspn() before calling\nfsck_set_msg_type(), which in turn calls parse_msg_type().\n\nSo all parse_msg_type() needs to do is just !strcmp().\n\n> +\n> +static int parse_msg_type(const char *str, int len)\n> +{\n> +\tif (len < 0)\n> +\t\tlen = strlen(str);\n> +\n> +\tif (!substrcmp(str, len, \"error\"))\n> +\t\treturn FSCK_ERROR;\n> +\telse if (!substrcmp(str, len, \"warn\"))\n> +\t\treturn FSCK_WARN;\n\n> +\telse\n> +\t\tdie(\"Unknown fsck message type: '%.*s'\",\n> +\t\t\t\tlen, str);\n> +}\n> +\n> +void fsck_set_msg_type(struct fsck_options *options,\n> +\t\tconst char *msg_id, int msg_id_len,\n> +\t\tconst char *msg_type, int msg_type_len)\n> +{\n> +\tint id = parse_msg_id(msg_id, msg_id_len), type;\n> +\n> +\tif (id < 0)\n> +\t\tdie(\"Unhandled message id: %.*s\", msg_id_len, msg_id);\n> +\ttype = parse_msg_type(msg_type, msg_type_len);\n> +\n> +\tif (!options->msg_type) {\n> +\t\tint i;\n> +\t\tint *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n> +\t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n> +\t\t\tmsg_type[i] = fsck_msg_type(i, options);\n> +\t\toptions->msg_type = msg_type;\n> +\t}\n> +\n> +\toptions->msg_type[id] = type;\n> +}\n> +\n> +void fsck_set_msg_types(struct fsck_options *options, const char *values)\n> +{\n> +\twhile (*values) {\n> +\t\tint len = strcspn(values, \" ,|\"), equal;\n> +\n> +\t\tif (!len) {\n> +\t\t\tvalues++;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tfor (equal = 0; equal < len; equal++)\n> +\t\t\tif (values[equal] == '=' || values[equal] == ':')\n> +\t\t\t\tbreak;\n> +\n> +\t\tif (equal == len)\n> +\t\t\tdie(\"Missing '=': '%.*s'\", len, values);\n> +\n> +\t\tfsck_set_msg_type(options, values, equal,\n> +\t\t\t\tvalues + equal + 1, len - equal - 1);\n> +\t\tvalues += len;\n> +\t}\n> +}\n> +\n>  __attribute__((format (printf, 4, 5)))\n>  static int report(struct fsck_options *options, struct object *object,\n>  \tenum fsck_msg_id id, const char *fmt, ...)\n> @@ -599,6 +671,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n>  \n>  int fsck_error_function(struct object *obj, int msg_type, const char *message)\n>  {\n> +\tif (msg_type == FSCK_WARN) {\n> +\t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n> +\t\treturn 0;\n> +\t}\n>  \terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n>  \treturn 1;\n>  }\n> diff --git a/fsck.h b/fsck.h\n> index f6f268a..edb4540 100644\n> --- a/fsck.h\n> +++ b/fsck.h\n> @@ -6,6 +6,11 @@\n>  \n>  struct fsck_options;\n>  \n> +void fsck_set_msg_type(struct fsck_options *options,\n> +\t\tconst char *msg_id, int msg_id_len,\n> +\t\tconst char *msg_type, int msg_type_len);\n> +void fsck_set_msg_types(struct fsck_options *options, const char *values);\n> +\n>  /*\n>   * callback function for fsck_walk\n>   * type is the expected type of the object or OBJ_ANY\n> @@ -25,10 +30,11 @@ struct fsck_options {\n>  \tfsck_walk_func walk;\n>  \tfsck_error error_func;\n>  \tunsigned strict:1;\n> +\tint *msg_type;\n>  };\n>  \n> -#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n> -#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n> +#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n> +#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }\n>  \n>  /* descend in all linked child objects\n>   * the return value is:\n"},{"id":"264362","messageId":"xmqqvbej7b13.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"d0c24a7643acd3a1a17ab3abf0ba54ab352b3e41.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 06/19] fsck: Report the ID of the error/warning","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T19:28:40Z","receivedAt":"2015-06-19T19:28:40Z","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> Some legacy code has objects with non-fatal fsck issues; To enable the\n> user to ignore those issues, let's print out the ID (e.g. when\n> encountering \"missingemail\", the user might want to call `git config\n> --add receive.fsck.missingemail=warn`).\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c          | 16 ++++++++++++++++\n>  t/t1450-fsck.sh |  4 ++--\n>  2 files changed, 18 insertions(+), 2 deletions(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index 8e6faa8..0b3e18f 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -190,6 +190,20 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n>  \t}\n>  }\n>  \n> +static void append_msg_id(struct strbuf *sb, const char *msg_id)\n> +{\n> +\tfor (;;) {\n> +\t\tchar c = *(msg_id)++;\n> +\n> +\t\tif (!c)\n> +\t\t\tbreak;\n> +\t\tif (c != '_')\n> +\t\t\tstrbuf_addch(sb, tolower(c));\n> +\t}\n> +\n> +\tstrbuf_addstr(sb, \": \");\n> +}\n> +\n>  __attribute__((format (printf, 4, 5)))\n>  static int report(struct fsck_options *options, struct object *object,\n>  \tenum fsck_msg_id id, const char *fmt, ...)\n> @@ -198,6 +212,8 @@ static int report(struct fsck_options *options, struct object *object,\n>  \tstruct strbuf sb = STRBUF_INIT;\n>  \tint msg_type = fsck_msg_type(id, options), result;\n>  \n> +\tappend_msg_id(&sb, msg_id_info[id].id_string);\n\n\nNice.  The append function can be made a bit more context sensitive\nto upcase a char immediately after _ to make it easier to cut and\npaste into \"git config\" and keep the result readable, I think.\n\n\tgit config --add receive.fsck.missingEmail=warn\n\n>  \tva_start(ap, fmt);\n>  \tstrbuf_vaddf(&sb, fmt, ap);\n>  \tresult = options->error_func(object, msg_type, sb.buf);\n> diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\n> index cfb32b6..d6d3b13 100755\n> --- a/t/t1450-fsck.sh\n> +++ b/t/t1450-fsck.sh\n> @@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n>  \tgit fsck --tags 2>out &&\n>  \n>  \tcat >expect <<-EOF &&\n> -\twarning in tag $tag: invalid '\\''tag'\\'' name: wrong name format\n> -\twarning in tag $tag: invalid format - expected '\\''tagger'\\'' line\n> +\twarning in tag $tag: badtagname: invalid '\\''tag'\\'' name: wrong name format\n> +\twarning in tag $tag: missingtaggerentry: invalid format - expected '\\''tagger'\\'' line\n>  \tEOF\n>  \ttest_cmp expect out\n>  '\n"},{"id":"264363","messageId":"8eb30cab0d359420bc037ab89b21c4b3@www.dscho.org","threadId":"38144","inReplyTo":"xmqqfv5na9hd.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v5 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T19:43:05Z","receivedAt":"2015-06-19T19:43:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nfirst of all: the improvements discussed here are already part of v6.\n\nOn 2015-06-19 19:33, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> I basically made up names on the go, based on the messages.\n>>\n>>> Some of the questionable groups are:\n>>>\n>>>     BAD_DATE DATE_OVERFLOW\n>>\n>> I guess it should be BAD_DATE_OVERFLOW to be more consistent?\n> \n> I am not sure about \"consistency\", but surely a common prefix would\n> help readers to group things.  But for this particular group, I was\n> wondering if singling out \"integer overflow\", \"zero stuffed\n> timestamp\", etc. into such a finer sub-errors of \"you have a bad\n> timestamp\" was beneficial.\n\nWell, someone thought it a good idea to print different error messages, and I took that as an indicator that there is merit in being able to distinguish these issues from one another.\n \n>>>     BAD_TREE_SHA1 INVALID_OBJECT_SHA1 INVALID_TREE\n>>>\n>>>     BAD_PARENT_SHA1 INVALID_OBJECT_SHA1\n>>\n>> So how about s/INVALID_/BAD_/g?\n> \n> It is not just about distinction between INVAID and BAD.\n> \n> I was basically wondering what rule decides which one among\n> BAD_TREE_SHA1, INVALID_OBJECT_SHA1 and INVALID_TREE I would get when\n> I have a random non-hexdigit string in various places, e.g. after\n> 'tree ' in the object header of a commit object, after 'tag ' in a\n> tag object that says 'type tree', etc.\n\nTo be honest, I think the IDs do not really matter as much as your comment makes it sound: the IDs purpose is solely to be able to configure the message type (read: whether to error out, warn or ignore the issue). The real information is in the actual message (and I did not change any message, therefore I could not make things worse than they are right now).\n\nExample: you would never read BAD_TREE_SHA1, but instead: \"badtreesha1: invalid 'tree' line format - bad sha1\".\n\nFor BAD_OBJECT_SHA1 (as it is now called), there are actually two code paths generating the error: \"invalid 'object' line format - bad sha1\" and \"no valid object to fsck\".\n\nAnd for BAD_TREE it is: \"could not load commit's tree <SHA1>\".\n\nThus, from the error message it should be really clear what is going on.\n\n>>> Also it is unclear if NOT_SORTED is to be used ever for any error\n>>> other than a tree object sorted incorrectly, or if we start noticing\n>>> a new error that something is not sorted, we will reuse this one.\n>>\n>> s/NOT_SORTED/TREE_&/ maybe?\n> \n> If that error is specific to tree sorting order, then that would be\n> a definite improvement.\n\nYes, that is the case. Tree objects are assumed to list their contents in order, and this ID applies to the problem where a tree object's list is out of order.\n\nAs I said, I already made both discussed changes part of v6.\n\nCiao,\nDscho\n"},{"id":"264364","messageId":"xmqqr3p77a3j.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"3e2deca4addda073f9b80e47865d2a5c95cea6e2.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 07/19] fsck: Make fsck_ident() warn-friendly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T19:48:48Z","receivedAt":"2015-06-19T19:48:48Z","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> When fsck_ident() identifies a problem with the ident, it should still\n> advance the pointer to the next line so that fsck can continue in the\n> case of a mere warning.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n\nMakes sense.\n"},{"id":"264366","messageId":"xmqqmvzv78z9.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"1ce6b2b32cb94d9697056d1181bb1fe396c64a5b.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:12:58Z","receivedAt":"2015-06-19T20:12:58Z","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> When fsck_commit() identifies a problem with the commit, it should try\n> to make it possible to continue checking the commit object, in case the\n> user wants to demote the detected errors to mere warnings.\n\nThat makes sense.\n\n> Note that some problems are too problematic to simply ignore. For\n> example, when the header lines are mixed up, we punt after encountering\n> an incorrect line. Therefore, demoting certain warnings to errors can\n> hide other problems. Example: demoting the missingauthor error to\n> a warning would hide a problematic committer line.\n\nIs this a warning to end-users (which should be better in the doc),\nor \"because some of them are too problematic to ignore\" that forgot\nto add the explanation \"hence we do not keep going in this code\"\n(which should be in the log message if that is what is going on)?\n\nI notice that there are many instances of\n\n\tif (object does not pass some test)\n\t\treturn report(...);\n\nthat do not do \"err = report(); if (err) return;\" in this function\nafter applying this patch.\n\nI think that answers the above question.  The answer is \"because\nsome are too problematic, even after this patch, we give up parsing\nthe remainder of the commit object once we hit certain errors,\nleaving some other errors that appear later in the object\nundetected\".\n\nI think that is a sensible design decision, but the proposed log\nmessage forgets to say so.\n\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c | 28 ++++++++++++++++++++--------\n>  1 file changed, 20 insertions(+), 8 deletions(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index 9faaf53..9fe9f48 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -534,12 +534,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n>  \n>  \tif (!skip_prefix(buffer, \"tree \", &buffer))\n>  \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n> -\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n> -\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n> +\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n> +\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n> +\t\tif (err)\n> +\t\t\treturn err;\n> +\t}\n\nI do not think this \"if (err) return err;\" that uses the return\nvalue of report(), makes sense.\n\nAs all the errors that use this pattern are isolated ones that does\nnot break parsing of the remainder (e.g. author ident had an extra >\nin it may break \"author \" but that does not prevent us from checking\n\"committer \").\n\nYour report() switches its return value based on the user setting;\nspecifically, it returns 0 if the user tells us to ignore/skip or\nwarn.  Which means that the user will see all warnings, but we stop\nat the first error.\n\nShouldn't we continue regardless of the end-user setting in order to\nshow errors on other fields, too?\n"},{"id":"264367","messageId":"xmqqioaj78tl.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"eb82bbfc658e8af1db03f7197b38aaaec33fd4ad.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 09/19] fsck: Handle multiple authors in commits specially","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:16:22Z","receivedAt":"2015-06-19T20:16:22Z","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>  \terr = fsck_ident(&buffer, &commit->object, options);\n>  \tif (err)\n>  \t\treturn err;\n> +\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n> +\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n> +\t\tif (err)\n> +\t\t\treturn err;\n> +\t\terr = fsck_ident(&buffer, &commit->object, options);\n> +\t\tif (err)\n> +\t\t\treturn err;\n> +\t}\n\nHmph, naively I would have expected that you wouldn't need an\nextra call to fsck_ident() here, and instead would see something\nlike this:\n\n\tauthor_count = 0;\n\twhile (skip_prefix(\"author \")) {\n        \tauthor_count++;\n                ... do the existing check as-is ...\n\t}\n        if (author_count < 1)\n        \terr |= report(missing author);\n\telse if (author_count > 1)\n        \terr |= report(multiple authors);\n"},{"id":"264368","messageId":"xmqqegl778pk.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"851b7fdb435c193abf0e20d0f0e171849e302aff.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 10/19] fsck: Make fsck_tag() warn-friendly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:18:47Z","receivedAt":"2015-06-19T20:18:47Z","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> When fsck_tag() identifies a problem with the commit, it should try\n> to make it possible to continue checking the commit object, in case the\n> user wants to demote the detected errors to mere warnings.\n\nI agree with that.  But if FSCK_MSG_BAD_OBJECT_SHA1 is an ignorable\nerror, why should we still have a conditional \"goto done\" here?\n\nShouldn't we be parsing the object the same way regardless?\n\n>\n> Just like fsck_commit(), there are certain problems that could hide other\n> issues with the same tag object. For example, if the 'type' line is not\n> encountered in the correct position, the 'tag' line – if there is any –\n> would not be handled at all.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c | 3 ++-\n>  1 file changed, 2 insertions(+), 1 deletion(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index 0cfa4d0..21e3052 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -640,7 +640,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n>  \t}\n>  \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n>  \t\tret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n> -\t\tgoto done;\n> +\t\tif (ret)\n> +\t\t\tgoto done;\n>  \t}\n>  \tbuffer += 41;\n"},{"id":"264369","messageId":"xmqqa8vv78ld.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"c87d71326fc75773f137eee0ef6a8964645f0b74.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 12/19] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:21:18Z","receivedAt":"2015-06-19T20:21:18Z","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> Some kinds of errors are intrinsically unrecoverable (e.g. errors while\n> uncompressing objects). It does not make sense to allow demoting them to\n> mere warnings.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  fsck.c                          | 14 ++++++++++++--\n>  t/t5504-fetch-receive-strict.sh | 11 +++++++++++\n>  2 files changed, 23 insertions(+), 2 deletions(-)\n>\n> diff --git a/fsck.c b/fsck.c\n> index 21e3052..a4fbce3 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -9,7 +9,12 @@\n>  #include \"refs.h\"\n>  #include \"utf8.h\"\n>  \n> +#define FSCK_FATAL -1\n> +\n>  #define FOREACH_MSG_ID(FUNC) \\\n> +\t/* fatal errors */ \\\n> +\tFUNC(NUL_IN_HEADER, FATAL) \\\n> +\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n>  \t/* errors */ \\\n>  \tFUNC(BAD_DATE, ERROR) \\\n>  \tFUNC(BAD_DATE_OVERFLOW, ERROR) \\\n> @@ -39,11 +44,9 @@\n>  \tFUNC(MISSING_TYPE, ERROR) \\\n>  \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n>  \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n> -\tFUNC(NUL_IN_HEADER, ERROR) \\\n>  \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n>  \tFUNC(TREE_NOT_SORTED, ERROR) \\\n>  \tFUNC(UNKNOWN_TYPE, ERROR) \\\n> -\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n\nI think the end result very much makes a good sense, but why didn't\nthis list enumerate the errors in the above \"final\" order from the\nbeginning in 02/19?\n"},{"id":"264370","messageId":"xmqq616j78ik.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"49189b5b9e8ad194c6408b30db6f493c53595021.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 14/19] fsck: Allow upgrading fsck warnings to errors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:22:59Z","receivedAt":"2015-06-19T20:22:59Z","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>  #define FSCK_FATAL -1\n> +#define FSCK_INFO -2\n>  \n>  #define FOREACH_MSG_ID(FUNC) \\\n>  \t/* fatal errors */ \\\n> @@ -50,15 +51,16 @@\n>  \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n>  \t/* warnings */ \\\n>  \tFUNC(BAD_FILEMODE, WARN) \\\n> -\tFUNC(BAD_TAG_NAME, WARN) \\\n>  \tFUNC(EMPTY_NAME, WARN) \\\n>  \tFUNC(FULL_PATHNAME, WARN) \\\n>  \tFUNC(HAS_DOT, WARN) \\\n>  \tFUNC(HAS_DOTDOT, WARN) \\\n>  \tFUNC(HAS_DOTGIT, WARN) \\\n> -\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n>  \tFUNC(NULL_SHA1, WARN) \\\n> -\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n> +\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n> +\t/* infos (reported as warnings, but ignored by default) */ \\\n> +\tFUNC(BAD_TAG_NAME, INFO) \\\n> +\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n\nExactly the same comment as 12/19 applies to this change; not only\ncomplaints but also \"result makes sense\" part.\n"},{"id":"264371","messageId":"xmqq1th77829.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"5f1c4c16027b00ef80490d67bec5e948481153ec.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:32:46Z","receivedAt":"2015-06-19T20:32:46Z","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> This option avoids unpacking each and all objects, and just verifies the\n> connectivity.\n\nThat sounds like marketing ;-)\n\n\"Wow this does not unpack unnecessarily, wait, it needs to unpack\nand parse 3 out of 4 kinds of objects?\"\n\nJokes aside, given that you should regularly repack your repository\nanyway, I do not think it is such a big downside that this mode\nmisses a corrupt objects, and the 1 out of 4 kinds of objects,\ni.e. blobs, occupy major part of the repository storage, so this new\nmode probably makes sense.\n\n> diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\n> index 922c346..2863a8a 100755\n> --- a/t/t1450-fsck.sh\n> +++ b/t/t1450-fsck.sh\n> @@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n>  \ttest_must_fail git -C missing fsck\n>  '\n>  \n> +test_expect_success 'fsck --quick' '\n> +\trm -rf quick &&\n> +\tgit init quick &&\n> +\t(\n> +\t\tcd quick &&\n> +\t\ttouch empty &&\n> +\t\tgit add empty &&\n> +\t\ttest_commit empty &&\n> +\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n> +\t\trm -f $empty &&\n> +\t\techo invalid >$empty &&\n> +\t\ttest_must_fail git fsck --strict &&\n> +\t\tgit fsck --strict --quick &&\n> +\t\ttree=$(git rev-parse HEAD:) &&\n> +\t\tsuffix=${tree#??} &&\n> +\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n> +\t\trm -f $tree &&\n> +\t\techo invalid >$tree &&\n> +\t\ttest_must_fail git fsck --strict --quick\n> +\t)\n> +'\n> +\n>  test_done\n\nI see a few impedance mismatch here.  For --quick, I would have\nexpected that the addition would be in t/perf/, not here.\n\nAlso the fact that quickness comes by cheating on blobs is an\nimplementation detail; in the future, perhaps somebody may come up\nwith a way to do a quick fsck while making sure blob corruption is\nalso detected.  The new test that expects \"--quick\" to ignore a\ncorrupt blob forbids such a progress.\n\nIf the option name was \"--ignore-corrupt-blob\", then the above\nchange is 100% justified, though.\n"},{"id":"264375","messageId":"xmqqwpyz5t66.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"e843f9f1defca543d3f2eb3143cf9fee8c72f695.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:39:45Z","receivedAt":"2015-06-19T20:39:45Z","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> +\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n> +\t\tconst char *path = is_absolute_path(value) ?\n> +\t\t\tvalue : git_path(\"%s\", value);\n\nThis \"either absolute or inside $GIT_DIR\" looks somewhat strange to\nme.  Shouldn't we mimick what \"git config --path\" does instead?\n"},{"id":"264373","messageId":"xmqqsi9n5t5d.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"692a41377d0bd47934170ea9b22a68f9dd893f38.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 19/19] fsck: support ignoring objects in `git fsck` via fsck.skiplist","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:40:14Z","receivedAt":"2015-06-19T20:40:14Z","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> +\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n> +\t\tconst char *path = is_absolute_path(value) ?\n> +\t\t\tvalue : git_path(\"%s\", value);\n\nSame comment as 18/19.\n"},{"id":"264374","messageId":"95e42f21de69ab5299c03ce6ad107037@www.dscho.org","threadId":"38144","inReplyTo":"xmqq1th77829.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T20:42:44Z","receivedAt":"2015-06-19T20:42:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 22:32, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> This option avoids unpacking each and all objects, and just verifies the\n>> connectivity.\n> \n> That sounds like marketing ;-)\n> \n> \"Wow this does not unpack unnecessarily, wait, it needs to unpack\n> and parse 3 out of 4 kinds of objects?\"\n\nHah, you caught me there. I wanted to say \"blob objects\".\n\n> Jokes aside, given that you should regularly repack your repository\n> anyway, I do not think it is such a big downside that this mode\n> misses a corrupt objects, and the 1 out of 4 kinds of objects,\n> i.e. blobs, occupy major part of the repository storage, so this new\n> mode probably makes sense.\n\nIt actually makes a ton of sense as a kind of light-weight check ;-) Try it, it is really much, much faster than a full fsck.\n\n>> diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\n>> index 922c346..2863a8a 100755\n>> --- a/t/t1450-fsck.sh\n>> +++ b/t/t1450-fsck.sh\n>> @@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n>>  \ttest_must_fail git -C missing fsck\n>>  '\n>>\n>> +test_expect_success 'fsck --quick' '\n>> +\trm -rf quick &&\n>> +\tgit init quick &&\n>> +\t(\n>> +\t\tcd quick &&\n>> +\t\ttouch empty &&\n>> +\t\tgit add empty &&\n>> +\t\ttest_commit empty &&\n>> +\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n>> +\t\trm -f $empty &&\n>> +\t\techo invalid >$empty &&\n>> +\t\ttest_must_fail git fsck --strict &&\n>> +\t\tgit fsck --strict --quick &&\n>> +\t\ttree=$(git rev-parse HEAD:) &&\n>> +\t\tsuffix=${tree#??} &&\n>> +\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n>> +\t\trm -f $tree &&\n>> +\t\techo invalid >$tree &&\n>> +\t\ttest_must_fail git fsck --strict --quick\n>> +\t)\n>> +'\n>> +\n>>  test_done\n> \n> I see a few impedance mismatch here.  For --quick, I would have\n> expected that the addition would be in t/perf/, not here.\n> \n> Also the fact that quickness comes by cheating on blobs is an\n> implementation detail; in the future, perhaps somebody may come up\n> with a way to do a quick fsck while making sure blob corruption is\n> also detected.  The new test that expects \"--quick\" to ignore a\n> corrupt blob forbids such a progress.\n> \n> If the option name was \"--ignore-corrupt-blob\", then the above\n> change is 100% justified, though.\n\nThe meaning of \"quick\" that I was thinking of was not the same as \"fast\", but more like \"just a quick check\". As in \"quick & dirty\" ;-)\n\nThe point is not to ignore corrupt blobs, by the way, it is to check the connectivity only, and save substantial amounts of time doing so.\n\nCan you think of a name for the option that is as short as `--quick` but means the same as `--connectivity-only`?\n\nCiao,\nDscho\n"},{"id":"264376","messageId":"ba0cb32edf26639a8a00ad9d17b49e32@www.dscho.org","threadId":"38144","inReplyTo":"xmqqmvzv78z9.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T20:52:28Z","receivedAt":"2015-06-19T20:52:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 22:12, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> Note that some problems are too problematic to simply ignore. For\n>> example, when the header lines are mixed up, we punt after encountering\n>> an incorrect line. Therefore, demoting certain warnings to errors can\n>> hide other problems. Example: demoting the missingauthor error to\n>> a warning would hide a problematic committer line.\n> \n> Is this a warning to end-users (which should be better in the doc),\n> or \"because some of them are too problematic to ignore\" that forgot\n> to add the explanation \"hence we do not keep going in this code\"\n> (which should be in the log message if that is what is going on)?\n\nIt was intended to offer the explanation for the design decision you commented on later:\n\n> I notice that there are many instances of\n> \n> \tif (object does not pass some test)\n> \t\treturn report(...);\n> \n> that do not do \"err = report(); if (err) return;\" in this function\n> after applying this patch.\n> \n> I think that answers the above question.  The answer is \"because\n> some are too problematic, even after this patch, we give up parsing\n> the remainder of the commit object once we hit certain errors,\n> leaving some other errors that appear later in the object\n> undetected\".\n> \n> I think that is a sensible design decision, but the proposed log\n> message forgets to say so.\n> \n>> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>> ---\n>>  fsck.c | 28 ++++++++++++++++++++--------\n>>  1 file changed, 20 insertions(+), 8 deletions(-)\n>>\n>> diff --git a/fsck.c b/fsck.c\n>> index 9faaf53..9fe9f48 100644\n>> --- a/fsck.c\n>> +++ b/fsck.c\n>> @@ -534,12 +534,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n>>\n>>  \tif (!skip_prefix(buffer, \"tree \", &buffer))\n>>  \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n>> -\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n>> -\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n>> +\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n>> +\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n>> +\t\tif (err)\n>> +\t\t\treturn err;\n>> +\t}\n> \n> I do not think this \"if (err) return err;\" that uses the return\n> value of report(), makes sense.\n> \n> As all the errors that use this pattern are isolated ones that does\n> not break parsing of the remainder (e.g. author ident had an extra >\n> in it may break \"author \" but that does not prevent us from checking\n> \"committer \").\n> \n> Your report() switches its return value based on the user setting;\n> specifically, it returns 0 if the user tells us to ignore/skip or\n> warn.  Which means that the user will see all warnings, but we stop\n> at the first error.\n> \n> Shouldn't we continue regardless of the end-user setting in order to\n> show errors on other fields, too?\n\nI can make that happen, but please note that this is a change of behavior: we always stopped upon the first error.\n\nIt was my intention not to change behavior in that way without a proper reason, and I saw none.\n\nI actually see a really good reason to *keep* the current behavior: one of the most prominent users of this code path is `git receive-pack --strict`. It is used heavily by GitHub to ensure at least a certain level of validity of pushed objects. Now, for this use case it is easy to see that you want to stop *as soon as an error was encountered*. And as GitHub sponsors my work on this patch series, my main aim is to support their use case.\n\nHaving said that, I agree that it could actually make sense for `git fsck` to show all errors, or at least to have an option to do so.\n\nBut that is a story for another night ;-)\nDscho\n"},{"id":"264377","messageId":"xmqqoakb5sk2.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"95e42f21de69ab5299c03ce6ad107037@www.dscho.org","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T20:53:01Z","receivedAt":"2015-06-19T20:53: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> Can you think of a name for the option that is as short as `--quick`\n> but means the same as `--connectivity-only`?\n\nNo I can't.  I think `--connectivity-only` is a very good name that\nis unfortunately a mouthful, I agree that we need a name that is as\nshort as `--xxxxx` that means the same as `--connectivity-only`.  I\ndo not think `--quick` is that word; it does not mean such a thing.\n"},{"id":"264380","messageId":"xmqqk2uz5s6b.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"ba0cb32edf26639a8a00ad9d17b49e32@www.dscho.org","subject":"Re: [PATCH v6 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T21:01:16Z","receivedAt":"2015-06-19T21:01:16Z","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>> I do not think this \"if (err) return err;\" that uses the return\n>> value of report(), makes sense.\n>> \n>> As all the errors that use this pattern are isolated ones that does\n>> not break parsing of the remainder (e.g. author ident had an extra >\n>> in it may break \"author \" but that does not prevent us from checking\n>> \"committer \").\n>> \n>> Your report() switches its return value based on the user setting;\n>> specifically, it returns 0 if the user tells us to ignore/skip or\n>> warn.  Which means that the user will see all warnings, but we stop\n>> at the first error.\n>> \n>> Shouldn't we continue regardless of the end-user setting in order to\n>> show errors on other fields, too?\n>\n> I can make that happen, but please note that this is a change of\n> behavior: we always stopped upon the first error.\n\nYeah, and we always died when we saw error, without giving users an\noption to turn it down.  So?\n\n> It was my intention not to change behavior in that way without a\n> proper reason, and I saw none.\n\nWhat would be the end-user experience if you stopped at the first\nerror?  You see an error, add an \"fsck.<msg-id> = ignore\" and rerun,\nonly to find another error and rinse and repeat?  Wouldn't you\nrather see all of them and add the \"ignore\" to cover them in one go?\n\n> I actually see a really good reason to *keep* the current behavior:\n> one of the most prominent users of this code path is `git receive-pack\n> --strict`. It is used heavily by GitHub to ensure at least a certain\n> level of validity of pushed objects. Now, for this use case it is easy\n> to see that you want to stop *as soon as an error was\n> encountered*. And as GitHub sponsors my work on this patch series, my\n> main aim is to support their use case.\n\nWhile I understand that use case, I do not think stopping after\nshowing three more errors in a single commit would make much\ndifference in the bigger picture.\n"},{"id":"264382","messageId":"fdcad96a0b8509eba316c79d2ce8be45@www.dscho.org","threadId":"38144","inReplyTo":"xmqqioaj78tl.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 09/19] fsck: Handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T21:04:17Z","receivedAt":"2015-06-19T21:04:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 22:16, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>>  \terr = fsck_ident(&buffer, &commit->object, options);\n>>  \tif (err)\n>>  \t\treturn err;\n>> +\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n>> +\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n>> +\t\tif (err)\n>> +\t\t\treturn err;\n>> +\t\terr = fsck_ident(&buffer, &commit->object, options);\n>> +\t\tif (err)\n>> +\t\t\treturn err;\n>> +\t}\n> \n> Hmph, naively I would have expected that you wouldn't need an\n> extra call to fsck_ident() here, and instead would see something\n> like this:\n> \n> \tauthor_count = 0;\n> \twhile (skip_prefix(\"author \")) {\n>         \tauthor_count++;\n>                 ... do the existing check as-is ...\n> \t}\n>         if (author_count < 1)\n>         \terr |= report(missing author);\n> \telse if (author_count > 1)\n>         \terr |= report(multiple authors);\n\nGood idea! I fixed this in my branch and it will be part of v7.\n\nCiao,\nDscho\n"},{"id":"264381","messageId":"daaa8919802f7ace91cbc051bba6e42b@www.dscho.org","threadId":"38144","inReplyTo":"xmqqegl778pk.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 10/19] fsck: Make fsck_tag() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T21:06:08Z","receivedAt":"2015-06-19T21:06:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 22:18, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> When fsck_tag() identifies a problem with the commit, it should try\n>> to make it possible to continue checking the commit object, in case the\n>> user wants to demote the detected errors to mere warnings.\n> \n> I agree with that.  But if FSCK_MSG_BAD_OBJECT_SHA1 is an ignorable\n> error, why should we still have a conditional \"goto done\" here?\n> \n> Shouldn't we be parsing the object the same way regardless?\n\nSame reason as I mentioned before: in `git receive-pack --strict` we want to fail early, there is no sense to keep going when we know that nothing of the rest will make it to the server.\n\nCiao,\nDscho\n"},{"id":"264383","messageId":"4e523e191f979706eca5d6ddcd221553@www.dscho.org","threadId":"38144","inReplyTo":"xmqqa8vv78ld.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 12/19] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T21:09:29Z","receivedAt":"2015-06-19T21:09:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 22:21, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> Some kinds of errors are intrinsically unrecoverable (e.g. errors while\n>> uncompressing objects). It does not make sense to allow demoting them to\n>> mere warnings.\n>>\n>> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>> ---\n>>  fsck.c                          | 14 ++++++++++++--\n>>  t/t5504-fetch-receive-strict.sh | 11 +++++++++++\n>>  2 files changed, 23 insertions(+), 2 deletions(-)\n>>\n>> diff --git a/fsck.c b/fsck.c\n>> index 21e3052..a4fbce3 100644\n>> --- a/fsck.c\n>> +++ b/fsck.c\n>> @@ -9,7 +9,12 @@\n>>  #include \"refs.h\"\n>>  #include \"utf8.h\"\n>>\n>> +#define FSCK_FATAL -1\n>> +\n>>  #define FOREACH_MSG_ID(FUNC) \\\n>> +\t/* fatal errors */ \\\n>> +\tFUNC(NUL_IN_HEADER, FATAL) \\\n>> +\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n>>  \t/* errors */ \\\n>>  \tFUNC(BAD_DATE, ERROR) \\\n>>  \tFUNC(BAD_DATE_OVERFLOW, ERROR) \\\n>> @@ -39,11 +44,9 @@\n>>  \tFUNC(MISSING_TYPE, ERROR) \\\n>>  \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n>>  \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n>> -\tFUNC(NUL_IN_HEADER, ERROR) \\\n>>  \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n>>  \tFUNC(TREE_NOT_SORTED, ERROR) \\\n>>  \tFUNC(UNKNOWN_TYPE, ERROR) \\\n>> -\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n> \n> I think the end result very much makes a good sense, but why didn't\n> this list enumerate the errors in the above \"final\" order from the\n> beginning in 02/19?\n\nBecause they are alphabetically ordered, within message type categories, that is; this helped me develop with more ease (you do not want to know how many hundreds of times I ran an interactive rebase on all of these patches...).\n\nAnd from the point of a development story (which a patch series is), it would puzzle me, as a reader, if those two out of all the others were in front in 02/19, when they are no different from the others at that stage.\n\nCiao,\nDscho\n"},{"id":"264384","messageId":"b003ad9a31c790dd0dafea8d81839322@www.dscho.org","threadId":"38144","inReplyTo":"xmqq616j78ik.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 14/19] fsck: Allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T21:10:33Z","receivedAt":"2015-06-19T21:10:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 22:22, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>>  #define FSCK_FATAL -1\n>> +#define FSCK_INFO -2\n>>\n>>  #define FOREACH_MSG_ID(FUNC) \\\n>>  \t/* fatal errors */ \\\n>> @@ -50,15 +51,16 @@\n>>  \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n>>  \t/* warnings */ \\\n>>  \tFUNC(BAD_FILEMODE, WARN) \\\n>> -\tFUNC(BAD_TAG_NAME, WARN) \\\n>>  \tFUNC(EMPTY_NAME, WARN) \\\n>>  \tFUNC(FULL_PATHNAME, WARN) \\\n>>  \tFUNC(HAS_DOT, WARN) \\\n>>  \tFUNC(HAS_DOTDOT, WARN) \\\n>>  \tFUNC(HAS_DOTGIT, WARN) \\\n>> -\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n>>  \tFUNC(NULL_SHA1, WARN) \\\n>> -\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n>> +\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n>> +\t/* infos (reported as warnings, but ignored by default) */ \\\n>> +\tFUNC(BAD_TAG_NAME, INFO) \\\n>> +\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n> \n> Exactly the same comment as 12/19 applies to this change; not only\n> complaints but also \"result makes sense\" part.\n\nAnd my explanation is the same ;-) At 02/19 time, it would just puzzle me, as a reader, to see special treatment without any good reason.\n\nCiao,\nDscho\n"},{"id":"264387","messageId":"118145527541c82e33599259c8e3e050@www.dscho.org","threadId":"38144","inReplyTo":"xmqqvbej7b13.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 06/19] fsck: Report the ID of the error/warning","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-19T21:34:19Z","receivedAt":"2015-06-19T21:34:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 21:28, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> Some legacy code has objects with non-fatal fsck issues; To enable the\n>> user to ignore those issues, let's print out the ID (e.g. when\n>> encountering \"missingemail\", the user might want to call `git config\n>> --add receive.fsck.missingemail=warn`).\n>>\n>> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>> ---\n>>  fsck.c          | 16 ++++++++++++++++\n>>  t/t1450-fsck.sh |  4 ++--\n>>  2 files changed, 18 insertions(+), 2 deletions(-)\n>>\n>> diff --git a/fsck.c b/fsck.c\n>> index 8e6faa8..0b3e18f 100644\n>> --- a/fsck.c\n>> +++ b/fsck.c\n>> @@ -190,6 +190,20 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n>>  \t}\n>>  }\n>>\n>> +static void append_msg_id(struct strbuf *sb, const char *msg_id)\n>> +{\n>> +\tfor (;;) {\n>> +\t\tchar c = *(msg_id)++;\n>> +\n>> +\t\tif (!c)\n>> +\t\t\tbreak;\n>> +\t\tif (c != '_')\n>> +\t\t\tstrbuf_addch(sb, tolower(c));\n>> +\t}\n>> +\n>> +\tstrbuf_addstr(sb, \": \");\n>> +}\n>> +\n>>  __attribute__((format (printf, 4, 5)))\n>>  static int report(struct fsck_options *options, struct object *object,\n>>  \tenum fsck_msg_id id, const char *fmt, ...)\n>> @@ -198,6 +212,8 @@ static int report(struct fsck_options *options, struct object *object,\n>>  \tstruct strbuf sb = STRBUF_INIT;\n>>  \tint msg_type = fsck_msg_type(id, options), result;\n>>\n>> +\tappend_msg_id(&sb, msg_id_info[id].id_string);\n> \n> \n> Nice.  The append function can be made a bit more context sensitive\n> to upcase a char immediately after _ to make it easier to cut and\n> paste into \"git config\" and keep the result readable, I think.\n> \n> \tgit config --add receive.fsck.missingEmail=warn\n\nOkay. I camelCased the IDs; it is a bit sore on my eyes in the command-line output, and the config variables are case-insensitive, anyway, but your wish is my command... I changed it locally, it will be part of v7.\n\nCiao,\nDscho\n"},{"id":"264388","messageId":"CAPc5daUm=h0+itLR-QcL2tMbeQ+oUe5p9Ki8qAsj47OFRX4LTw@mail.gmail.com","threadId":"38144","inReplyTo":"118145527541c82e33599259c8e3e050@www.dscho.org","subject":"Re: [PATCH v6 06/19] fsck: Report the ID of the error/warning","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T23:26:16Z","receivedAt":"2015-06-19T23:26:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"That \"... can be made ...\" was not my wish but more like \"the way the\ncode is structured it is possible for somebody to do such a thing\neasily, well done\" compliment ;-)\n\nThe message names will have to be shown somewhere in the\ndocumentation, and in Documentation/ we try to use camelCase to show\nthe word boundary; it would be better to match that, as this output is\nmeant to be used there.\n\nOn Fri, Jun 19, 2015 at 2:34 PM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> Hi Junio,\n>\n> On 2015-06-19 21:28, Junio C Hamano wrote:\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>>\n>>> Some legacy code has objects with non-fatal fsck issues; To enable the\n>>> user to ignore those issues, let's print out the ID (e.g. when\n>>> encountering \"missingemail\", the user might want to call `git config\n>>> --add receive.fsck.missingemail=warn`).\n>>>\n>>> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>>> ---\n>>>  fsck.c          | 16 ++++++++++++++++\n>>>  t/t1450-fsck.sh |  4 ++--\n>>>  2 files changed, 18 insertions(+), 2 deletions(-)\n>>>\n>>> diff --git a/fsck.c b/fsck.c\n>>> index 8e6faa8..0b3e18f 100644\n>>> --- a/fsck.c\n>>> +++ b/fsck.c\n>>> @@ -190,6 +190,20 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n>>>      }\n>>>  }\n>>>\n>>> +static void append_msg_id(struct strbuf *sb, const char *msg_id)\n>>> +{\n>>> +    for (;;) {\n>>> +            char c = *(msg_id)++;\n>>> +\n>>> +            if (!c)\n>>> +                    break;\n>>> +            if (c != '_')\n>>> +                    strbuf_addch(sb, tolower(c));\n>>> +    }\n>>> +\n>>> +    strbuf_addstr(sb, \": \");\n>>> +}\n>>> +\n>>>  __attribute__((format (printf, 4, 5)))\n>>>  static int report(struct fsck_options *options, struct object *object,\n>>>      enum fsck_msg_id id, const char *fmt, ...)\n>>> @@ -198,6 +212,8 @@ static int report(struct fsck_options *options, struct object *object,\n>>>      struct strbuf sb = STRBUF_INIT;\n>>>      int msg_type = fsck_msg_type(id, options), result;\n>>>\n>>> +    append_msg_id(&sb, msg_id_info[id].id_string);\n>>\n>>\n>> Nice.  The append function can be made a bit more context sensitive\n>> to upcase a char immediately after _ to make it easier to cut and\n>> paste into \"git config\" and keep the result readable, I think.\n>>\n>>       git config --add receive.fsck.missingEmail=warn\n>\n> Okay. I camelCased the IDs; it is a bit sore on my eyes in the command-line output, and the config variables are case-insensitive, anyway, but your wish is my command... I changed it locally, it will be part of v7.\n>\n> Ciao,\n> Dscho\n"},{"id":"264389","messageId":"CAPc5daWuxBYyA03_WxeEEuDN4BPW58tTTK2yLxVnjR7sRwMhoQ@mail.gmail.com","threadId":"38144","inReplyTo":"4e523e191f979706eca5d6ddcd221553@www.dscho.org","subject":"Re: [PATCH v6 12/19] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T23:30:21Z","receivedAt":"2015-06-19T23:30:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ahh, I didn't see that they were not grouped by object types, features\nor any meaningful axis.\nThat explains it (i.e. I can now understand why the original list was\nordered differently from the final order).\n\n\nOn Fri, Jun 19, 2015 at 2:09 PM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> Hi Junio,\n>\n> On 2015-06-19 22:21, Junio C Hamano wrote:\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>>\n>>> Some kinds of errors are intrinsically unrecoverable (e.g. errors while\n>>> uncompressing objects). It does not make sense to allow demoting them to\n>>> mere warnings.\n>>>\n>>> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n>>> ---\n>>>  fsck.c                          | 14 ++++++++++++--\n>>>  t/t5504-fetch-receive-strict.sh | 11 +++++++++++\n>>>  2 files changed, 23 insertions(+), 2 deletions(-)\n>>>\n>>> diff --git a/fsck.c b/fsck.c\n>>> index 21e3052..a4fbce3 100644\n>>> --- a/fsck.c\n>>> +++ b/fsck.c\n>>> @@ -9,7 +9,12 @@\n>>>  #include \"refs.h\"\n>>>  #include \"utf8.h\"\n>>>\n>>> +#define FSCK_FATAL -1\n>>> +\n>>>  #define FOREACH_MSG_ID(FUNC) \\\n>>> +    /* fatal errors */ \\\n>>> +    FUNC(NUL_IN_HEADER, FATAL) \\\n>>> +    FUNC(UNTERMINATED_HEADER, FATAL) \\\n>>>      /* errors */ \\\n>>>      FUNC(BAD_DATE, ERROR) \\\n>>>      FUNC(BAD_DATE_OVERFLOW, ERROR) \\\n>>> @@ -39,11 +44,9 @@\n>>>      FUNC(MISSING_TYPE, ERROR) \\\n>>>      FUNC(MISSING_TYPE_ENTRY, ERROR) \\\n>>>      FUNC(MULTIPLE_AUTHORS, ERROR) \\\n>>> -    FUNC(NUL_IN_HEADER, ERROR) \\\n>>>      FUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n>>>      FUNC(TREE_NOT_SORTED, ERROR) \\\n>>>      FUNC(UNKNOWN_TYPE, ERROR) \\\n>>> -    FUNC(UNTERMINATED_HEADER, ERROR) \\\n>>\n>> I think the end result very much makes a good sense, but why didn't\n>> this list enumerate the errors in the above \"final\" order from the\n>> beginning in 02/19?\n>\n> Because they are alphabetically ordered, within message type categories, that is; this helped me develop with more ease (you do not want to know how many hundreds of times I ran an interactive rebase on all of these patches...).\n>\n> And from the point of a development story (which a patch series is), it would puzzle me, as a reader, if those two out of all the others were in front in 02/19, when they are no different from the others at that stage.\n>\n> Ciao,\n> Dscho\n"},{"id":"264390","messageId":"xmqqfv5n5knp.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"xmqqk2uz5s6b.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-19T23:43:38Z","receivedAt":"2015-06-19T23:43:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> What would be the end-user experience if you stopped at the first\n> error?  You see an error, add an \"fsck.<msg-id> = ignore\" and rerun,\n> only to find another error and rinse and repeat?  Wouldn't you\n> rather see all of them and add the \"ignore\" to cover them in one go?\n>\n>> I actually see a really good reason to *keep* the current behavior:\n>> one of the most prominent users of this code path is `git receive-pack\n>> --strict`. It is used heavily by GitHub to ensure at least a certain\n>> level of validity of pushed objects. Now, for this use case it is easy\n>> to see that you want to stop *as soon as an error was\n>> encountered*. And as GitHub sponsors my work on this patch series, my\n>> main aim is to support their use case.\n>\n> While I understand that use case, I do not think stopping after\n> showing three more errors in a single commit would make much\n> difference in the bigger picture.\n\nI actually changed my mind.  The above talks about the value given\nto the end user by noticing as many errors in a single object, but\nI'd think fsck.<msg-id> is pretty much useless as a tool to keep\nusing a repository with malformed object in its history.  When you\nare told object d6602ec is bad (that's the v0.99 tag that does not\nhave tagger field), you would never want to say \"in this repository,\nany tag without tagger is allowed\", because you would still want to\ncatch and prevent future breakages of the same kind in new tags.\n\nAnd the way to do so is to say \"I know the object d6602ec is bad, so\ndo not report breakage you find to me\" by using the skip-list.  For\nthat use case, showing _all_ errors (or warnings for that matter)\ndoes not add any value.\n\nSo let's stop at the first error as your patch did.\n"},{"id":"264391","messageId":"20150619235748.GA23394@odin.ulthar.us","threadId":"38144","inReplyTo":"xmqqoakb5sk2.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Scott Schmit","fromEmail":"i.grok@comcast.net","sentAt":"2015-06-19T23:57:48Z","receivedAt":"2015-06-19T23:57:48Z","isPatch":true,"sender":{"key":"i.grok@comcast.net","avatar":null},"body":"On Fri, Jun 19, 2015 at 01:53:01PM -0700, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Can you think of a name for the option that is as short as `--quick`\n> > but means the same as `--connectivity-only`?\n> \n> No I can't.  I think `--connectivity-only` is a very good name that\n> is unfortunately a mouthful, I agree that we need a name that is as\n> short as `--xxxxx` that means the same as `--connectivity-only`.  I\n> do not think `--quick` is that word; it does not mean such a thing.\n\nHow about `--linkage` or `--links`?\n\n-- \nScott Schmit\n"},{"id":"264394","messageId":"xmqqbngb5ag0.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"20150619235748.GA23394@odin.ulthar.us","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-20T03:24:15Z","receivedAt":"2015-06-20T03:24:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Scott Schmit <i.grok@comcast.net> writes:\n\n> On Fri, Jun 19, 2015 at 01:53:01PM -0700, Junio C Hamano wrote:\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> \n>> > Can you think of a name for the option that is as short as `--quick`\n>> > but means the same as `--connectivity-only`?\n>> \n>> No I can't.  I think `--connectivity-only` is a very good name that\n>> is unfortunately a mouthful, I agree that we need a name that is as\n>> short as `--xxxxx` that means the same as `--connectivity-only`.  I\n>> do not think `--quick` is that word; it does not mean such a thing.\n>\n> How about `--linkage` or `--links`?\n\nEven though \"link\" may be shorter than \"connectivity\", the real\ndifficulty is to come up with a phrase that conveys the \"-only\" part\nof the fully spelled name, which is more important, without spending\n5 letters that take to say \"-only\".\n"},{"id":"264395","messageId":"xmqq7fqz5abm.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"95e42f21de69ab5299c03ce6ad107037@www.dscho.org","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-20T03:26:53Z","receivedAt":"2015-06-20T03:26:53Z","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>> Jokes aside, given that you should regularly repack your repository\n>> anyway, I do not think it is such a big downside that this mode\n>> misses a corrupt objects, and the 1 out of 4 kinds of objects,\n>> i.e. blobs, occupy major part of the repository storage, so this new\n>> mode probably makes sense.\n>\n> It actually makes a ton of sense as a kind of light-weight check ;-)\n\nYes, didn't I agree that it makes sense already?\n\n> The meaning of \"quick\" that I was thinking of was not the same as\n> \"fast\", but more like \"just a quick check\". As in \"quick & dirty\" ;-)\n\nSure.\n\n> The point is not to ignore corrupt blobs, by the way, it is to check\n> the connectivity only, and save substantial amounts of time doing so.\n\nYeah, I understand that; after all, that is exactly why I said \"make\nsure it does not notice corrupt blobs\" is not a good test.\n\nOn the other hand, you are also checking that it notices a broken\ntree (which makes it impossible to complete connectivity check),\nwhich is a good test.\n"},{"id":"264399","messageId":"792e5fab01e2fc7f156b8d0123364acf@www.dscho.org","threadId":"38144","inReplyTo":"xmqqd20r8qrm.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 01/19] fsck: Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-20T12:33:41Z","receivedAt":"2015-06-20T12:33:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 21:03, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> diff --git a/builtin/index-pack.c b/builtin/index-pack.c\n>> index 48fa472..87ae9ba 100644\n>> --- a/builtin/index-pack.c\n>> +++ b/builtin/index-pack.c\n>> @@ -75,6 +75,7 @@ static int nr_threads;\n>>  static int from_stdin;\n>>  static int strict;\n>>  static int do_fsck_object;\n>> +static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n> \n> So there is a global fsck_options used throughout the entire\n> session here.\n> \n>> @@ -838,10 +839,10 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n>>  \t\t\tif (!obj)\n>>  \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n>>  \t\t\tif (do_fsck_object &&\n>> -\t\t\t    fsck_object(obj, buf, size, 1,\n>> -\t\t\t\t    fsck_error_function))\n>> +\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n>>  \t\t\t\tdie(_(\"Error in object\"));\n> \n> And that is used here to inspect each and every object we encounter.\n> \n>> -\t\t\tif (fsck_walk(obj, mark_link, NULL))\n>> +\t\t\tfsck_options.walk = mark_link;\n> \n> Then we do a call to fsck_walk() starting from this object, letting\n> mark_link() to inspect it and set the LINK bit.\n> \n>> +\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n>>  \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n> \n> Since nobody else sets fsck_options.walk to any other value, and\n> nobody else calls fsck_walk(), shouldn't that assignment be done\n> only once somewhere a lot higher in the callchain?  The apparent\n> \"overriding while inspecting this object\" that does not have any\n> corresponding \"now we are done, so revert it to the original value\"\n> puzzled me, and I am sure it would puzzle future readers of this\n> code.\n\nGood point. I guess I was really wary that a configured walk function might change the behavior of `fsck_object()`. But after inspecting the code paths carefully, I conclude that the walk function is really only used in the `fsck_walk_*()` family of functions.\n\nI changed that locally, will be part of v7.\n\nCiao,\nDscho\n"},{"id":"264400","messageId":"9b7c42e6ec81fd70015b8fe9ff2bd137@www.dscho.org","threadId":"38144","inReplyTo":"xmqqwpyz5t66.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-20T12:45:51Z","receivedAt":"2015-06-20T12:45:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 22:39, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> +\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n>> +\t\tconst char *path = is_absolute_path(value) ?\n>> +\t\t\tvalue : git_path(\"%s\", value);\n> \n> This \"either absolute or inside $GIT_DIR\" looks somewhat strange to\n> me.  Shouldn't we mimick what \"git config --path\" does instead?\n\nOkay. That would also support user paths.\n\nThere is a problem, though: `git_config_pathname()` accepts a `const char **` parameter to set the path, yet I need to `free()` that pointer afterwards because it has been obtained through `expand_user_path()` which detaches that buffer from a `strbuf`.\n\nIn regular use cases, that does not matter much because the path obtained via `git_config_pathname()` is typically stored in a global variable, but in my case, I append it to a global variable.\n\nOf course I could cast the value to a `char *` and `free()` it, but that would assume too much about the implementation detail of `git_config_pathname()` for my taste.\n\nThe easiest cop out would be to recapitulate the code in `git_config_pathname()`, but I think the correct solution would be to change the signature of `git_config_pathname()` to accept a `char **` instead. Do you agree? However, the diff looks really ugly:\n\n-- snipsnap --\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 254477f..946b31b 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1494,7 +1494,7 @@ static int git_commit_config(const char *k, const char *v, void *cb)\n \tint status;\n \n \tif (!strcmp(k, \"commit.template\"))\n-\t\treturn git_config_pathname(&template_file, k, v);\n+\t\treturn git_config_pathname((char **)&template_file, k, v);\n \tif (!strcmp(k, \"commit.status\")) {\n \t\tinclude_status = git_config_bool(k, v);\n \t\treturn 0;\ndiff --git a/builtin/config.c b/builtin/config.c\nindex 7188405..a249faa 100644\n--- a/builtin/config.c\n+++ b/builtin/config.c\n@@ -130,7 +130,7 @@ static int format_config(struct strbuf *buf, const char *key_, const char *value\n \t\telse\n \t\t\tsprintf(value, \"%d\", v);\n \t} else if (types == TYPE_PATH) {\n-\t\tif (git_config_pathname(&vptr, key_, value_) < 0)\n+\t\tif (git_config_pathname((char **)&vptr, key_, value_) < 0)\n \t\t\treturn -1;\n \t\tmust_free_vptr = 1;\n \t} else if (value_) {\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex 4335738..0572cae 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -177,7 +177,8 @@ free_return:\n static int git_init_db_config(const char *k, const char *v, void *cb)\n {\n \tif (!strcmp(k, \"init.templatedir\"))\n-\t\treturn git_config_pathname(&init_db_template_dir, k, v);\n+\t\treturn git_config_pathname((char **)&init_db_template_dir,\n+\t\t\t\tk, v);\n \n \treturn 0;\n }\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 8781049..8751173 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -743,7 +743,8 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \tif (!strcmp(var, \"format.signature\"))\n \t\treturn git_config_string(&signature, var, value);\n \tif (!strcmp(var, \"format.signaturefile\"))\n-\t\treturn git_config_pathname(&signature_file, var, value);\n+\t\treturn git_config_pathname((char **)&signature_file,\n+\t\t\t\tvar, value);\n \tif (!strcmp(var, \"format.coverletter\")) {\n \t\tif (value && !strcasecmp(value, \"auto\")) {\n \t\t\tconfig_cover_letter = COVER_AUTO;\ndiff --git a/cache.h b/cache.h\nindex 4f55466..19af52e 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1442,7 +1442,7 @@ extern int git_config_bool_or_int(const char *, const char *, int *);\n extern int git_config_bool(const char *, const char *);\n extern int git_config_maybe_bool(const char *, const char *);\n extern int git_config_string(const char **, const char *, const char *);\n-extern int git_config_pathname(const char **, const char *, const char *);\n+extern int git_config_pathname(char **, const char *, const char *);\n extern int git_config_set_in_file(const char *, const char *, const char *);\n extern int git_config_set(const char *, const char *);\n extern int git_config_parse_key(const char *, char **, int *);\ndiff --git a/config.c b/config.c\nindex 29fa012..51781b8 100644\n--- a/config.c\n+++ b/config.c\n@@ -670,7 +670,7 @@ int git_config_string(const char **dest, const char *var, const char *value)\n \treturn 0;\n }\n \n-int git_config_pathname(const char **dest, const char *var, const char *value)\n+int git_config_pathname(char **dest, const char *var, const char *value)\n {\n \tif (!value)\n \t\treturn config_error_nonbool(var);\n@@ -714,7 +714,8 @@ static int git_default_core_config(const char *var, const char *value)\n \t}\n \n \tif (!strcmp(var, \"core.attributesfile\"))\n-\t\treturn git_config_pathname(&git_attributes_file, var, value);\n+\t\treturn git_config_pathname((char **)&git_attributes_file,\n+\t\t\t\tvar, value);\n \n \tif (!strcmp(var, \"core.bare\")) {\n \t\tis_bare_repository_cfg = git_config_bool(var, value);\n@@ -862,7 +863,7 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn git_config_string(&askpass_program, var, value);\n \n \tif (!strcmp(var, \"core.excludesfile\"))\n-\t\treturn git_config_pathname(&excludes_file, var, value);\n+\t\treturn git_config_pathname((char **)&excludes_file, var, value);\n \n \tif (!strcmp(var, \"core.whitespace\")) {\n \t\tif (!value)\n@@ -989,7 +990,8 @@ static int git_default_push_config(const char *var, const char *value)\n static int git_default_mailmap_config(const char *var, const char *value)\n {\n \tif (!strcmp(var, \"mailmap.file\"))\n-\t\treturn git_config_pathname(&git_mailmap_file, var, value);\n+\t\treturn git_config_pathname((char **)&git_mailmap_file,\n+\t\t\t\tvar, value);\n \tif (!strcmp(var, \"mailmap.blob\"))\n \t\treturn git_config_string(&git_mailmap_blob, var, value);\n \n@@ -1506,7 +1508,7 @@ int git_configset_get_pathname(struct config_set *cs, const char *key, const cha\n {\n \tconst char *value;\n \tif (!git_configset_get_value(cs, key, &value))\n-\t\treturn git_config_pathname(dest, key, value);\n+\t\treturn git_config_pathname((char **)dest, key, value);\n \telse\n \t\treturn 1;\n }\ndiff --git a/diff.c b/diff.c\nindex 87b16d5..e029b75 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -204,7 +204,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)\n \tif (!strcmp(var, \"diff.wordregex\"))\n \t\treturn git_config_string(&diff_word_regex_cfg, var, value);\n \tif (!strcmp(var, \"diff.orderfile\"))\n-\t\treturn git_config_pathname(&diff_order_file_cfg, var, value);\n+\t\treturn git_config_pathname((char **)&diff_order_file_cfg,\n+\t\t\t\tvar, value);\n \n \tif (!strcmp(var, \"diff.ignoresubmodules\"))\n \t\thandle_ignore_submodules_arg(&default_diff_options, value);\n"},{"id":"264408","messageId":"xmqq4mm2l25m.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"9b7c42e6ec81fd70015b8fe9ff2bd137@www.dscho.org","subject":"Re: [PATCH v6 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-20T17:28:53Z","receivedAt":"2015-06-20T17:28:53Z","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> There is a problem, though: `git_config_pathname()` accepts a\n> `const char **` parameter to set the path, yet I need to `free()`\n> that pointer afterwards because it has been obtained through\n> `expand_user_path()` which detaches that buffer from a `strbuf`.\n\n\"I have 'const char *' because I do not ever change the string\nmyself after getting it from an API function, but free() does not\nwant to free a const pointer\" occurs sometimes in our codebase and\nit is OK to cast the constness away (many callsites to free()\nalready do so).\n"},{"id":"264423","messageId":"558643CA.6000303@alum.mit.edu","threadId":"38144","inReplyTo":"xmqqoakb5sk2.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Michael Haggerty","fromEmail":"mhagger@alum.mit.edu","sentAt":"2015-06-21T04:55:38Z","receivedAt":"2015-06-21T04:55:38Z","isPatch":true,"sender":{"key":"mhagger@alum.mit.edu","avatar":"https://avatars.githubusercontent.com/u/119718?v=4"},"body":"On 06/19/2015 10:53 PM, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> Can you think of a name for the option that is as short as `--quick`\n>> but means the same as `--connectivity-only`?\n> \n> No I can't.  I think `--connectivity-only` is a very good name that\n> is unfortunately a mouthful, I agree that we need a name that is as\n> short as `--xxxxx` that means the same as `--connectivity-only`.  I\n> do not think `--quick` is that word; it does not mean such a thing.\n\n`--connectivity-only` says that \"of all the things that fsck can do,\nskip everything except for the connectivity check\". But the switch\nreally affects not the connectivity part of the checks (that part is\ndone in either case), but the blob part. So, if we ignore the length of\nthe option name for a moment, it seems like the options should be\nsomething like `--check-blob-integrity`/`--no-check-blob-integrity`. The\ndefault would remain `--check-blob-integrity` of course, but\n\n* Someday there might be a config setting that people can use to change\nthe default behavior of fsck to `--no-check-blob-integrity`.\n* Someday there might be other expensive types of checks [1] that we\nwant to turn on/off independent of blob integrity checks.\n\nBut now that I'm writing this, a silly question occurs to me: Do we need\nan overall option like this at all? If I demote all blob-integrity\nchecks to \"ignore\" via the mechanism that you have added, then shouldn't\nfsck automatically detect that it doesn't have to open the blobs at all\nand enable this speedup automatically? So maybe\n`--(no-)?check-blob-integrity` is actually a shorthand for turning a few\nmore specific checks on/off at once.\n\nAs for thinking of a shorter name for the option: assuming the blob\nintegrity checks can be turned on and off independently as described\nabove, then I think it is reasonable to *also* add a `--quick` option\ndefined as\n\n--quick: Skip some expensive checks, dramatically reducing the\n    runtime of `git fsck`. Currently this is equivalent to\n    `--no-check-blob-integrity`.\n\nIn the future if we invent other expensive checks we might also add them\nto the list of things that are skipped by `--quick`.\n\nMichael\n\n[1] For example, if LFS or something like it every became part of\nstandard Git, one could imagine a super-expensive\n`--check-lfs-object-availability` check that would would default to OFF\nbut sometimes turn on by hand.\n\n-- \nMichael Haggerty\nmhagger@alum.mit.edu\n"},{"id":"264424","messageId":"000201d0abe0$7f3ccf20$7db66d60$@nexbridge.com","threadId":"38144","inReplyTo":"558643CA.6000303@alum.mit.edu","subject":"RE: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Randall S. Becker","fromEmail":"rsbecker@nexbridge.com","sentAt":"2015-06-21T05:09:47Z","receivedAt":"2015-06-21T05:09:47Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On June 21, 2015 12:56 AM, Michael Haggerty wrote:\n> On 06/19/2015 10:53 PM, Junio C Hamano wrote:\n> > Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> >\n> >> Can you think of a name for the option that is as short as `--quick`\n> >> but means the same as `--connectivity-only`?\n> >\n> > No I can't.  I think `--connectivity-only` is a very good name that is\n> > unfortunately a mouthful, I agree that we need a name that is as short\n> > as `--xxxxx` that means the same as `--connectivity-only`.  I do not\n> > think `--quick` is that word; it does not mean such a thing.\n> \n> `--connectivity-only` says that \"of all the things that fsck can do, skip\neverything\n> except for the connectivity check\". But the switch really affects not the\n> connectivity part of the checks (that part is done in either case), but\nthe blob\n> part. So, if we ignore the length of the option name for a moment, it\nseems like\n> the options should be something like\n`--check-blob-integrity`/`--no-check-blob-\n> integrity`. The default would remain `--check-blob-integrity` of course,\nbut\n> \n> * Someday there might be a config setting that people can use to change\nthe\n> default behavior of fsck to `--no-check-blob-integrity`.\n> * Someday there might be other expensive types of checks [1] that we want\nto\n> turn on/off independent of blob integrity checks.\n> \n> But now that I'm writing this, a silly question occurs to me: Do we need\nan\n> overall option like this at all? If I demote all blob-integrity checks to\n\"ignore\"\n> via the mechanism that you have added, then shouldn't fsck automatically\n> detect that it doesn't have to open the blobs at all and enable this\nspeedup\n> automatically? So maybe `--(no-)?check-blob-integrity` is actually a\nshorthand\n> for turning a few more specific checks on/off at once.\n> \n> As for thinking of a shorter name for the option: assuming the blob\nintegrity\n> checks can be turned on and off independently as described above, then I\nthink\n> it is reasonable to *also* add a `--quick` option defined as\n> \n> --quick: Skip some expensive checks, dramatically reducing the\n>     runtime of `git fsck`. Currently this is equivalent to\n>     `--no-check-blob-integrity`.\n> \n> In the future if we invent other expensive checks we might also add them\nto the\n> list of things that are skipped by `--quick`.\n\nSynonym suggestions: --links or --relations\nI was going to include --refs but that may be ambiguous. Links also has\nmeaning so it's probably out and --hitch may just be silly and needlessly\nintroducing a new term.\n\nCheers,\nRandall\n\n-- Brief whoami: NonStop&UNIX developer since approximately\nUNIX(421664400)/NonStop(211288444200000000)\n-- In my real life, I talk too much.\n"},{"id":"264427","messageId":"0fe9bc944a232d40fa94b659bbdf5c97@www.dscho.org","threadId":"38144","inReplyTo":"558643CA.6000303@alum.mit.edu","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-21T12:01:16Z","receivedAt":"2015-06-21T12:01:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Michael,\n\nOn 2015-06-21 06:55, Michael Haggerty wrote:\n> On 06/19/2015 10:53 PM, Junio C Hamano wrote:\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>>\n>>> Can you think of a name for the option that is as short as `--quick`\n>>> but means the same as `--connectivity-only`?\n>>\n>> No I can't.  I think `--connectivity-only` is a very good name that\n>> is unfortunately a mouthful, I agree that we need a name that is as\n>> short as `--xxxxx` that means the same as `--connectivity-only`.  I\n>> do not think `--quick` is that word; it does not mean such a thing.\n> \n> `--connectivity-only` says that \"of all the things that fsck can do,\n> skip everything except for the connectivity check\". But the switch\n> really affects not the connectivity part of the checks (that part is\n> done in either case), but the blob part.\n\nRight, so `--skip-blobs` would be a better name, I guess, if you follow Junio's reasoning.\n\nBut...\n\n> [...]\n> As for thinking of a shorter name for the option: assuming the blob\n> integrity checks can be turned on and off independently as described\n> above, then I think it is reasonable to *also* add a `--quick` option\n> defined as\n> \n> --quick: Skip some expensive checks, dramatically reducing the\n>     runtime of `git fsck`. Currently this is equivalent to\n>     `--no-check-blob-integrity`.\n\nThis was my idea, without bothering to introduce the `--no-check-blob-integrity`. I was really thinking along the lines: If you just want to check quickly whether your repository is in good shape, without wanting to check too deeply, then `--quick` is your friend. I just *happened* to think of skipping blobs as a way to trade off accuracy for speed, but really, the reason why I introduced `--quick` was to have a way to check much faster if somewhat less thoroughly.\n\nCiao,\nDscho\n"},{"id":"264436","messageId":"de90ffde976a5fdcc580472387ca20ae@www.dscho.org","threadId":"38144","inReplyTo":"xmqq4mm38qbk.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 03/19] fsck: Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-21T13:46:42Z","receivedAt":"2015-06-21T13:46:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 21:13, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> +#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n>>  static struct {\n>> +\tconst char *id_string;\n>>  \tint msg_type;\n>>  } msg_id_info[FSCK_MSG_MAX + 1] = {\n>>  \tFOREACH_MSG_ID(MSG_ID)\n>> -\t{ -1 }\n>> +\t{ NULL, -1 }\n>>  };\n>>  #undef MSG_ID\n>>\n>> +static int parse_msg_id(const char *text, int len)\n>> +{\n>> +\tint i, j;\n>> +\n>> +\tif (len < 0)\n>> +\t\tlen = strlen(text);\n>> +\n>> +\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n> \n> I wonder an array without sentinel at the end with ARRAY_SIZE() may\n> be a leaner way to do these, especially as this is all limited to\n> this single file.\n\nThe real reason is that I cannot prevent a trailing comma with the way I construct the array, so FSCK_MSG_MAX is a natural way to support compilers that do not allow something like\n\n    enum { A, B, C, };\n\n>> +\t\tconst char *key = msg_id_info[i].id_string;\n>> +\t\t/* match id_string case-insensitively, without underscores. */\n>> +\t\tfor (j = 0; j < len; j++) {\n>> +\t\t\tchar c = *(key++);\n>> +\t\t\tif (c == '_')\n>> +\t\t\t\tc = *(key++);\n> \n> s/if/while/ perhaps?\n\nActually, I want to prevent double underscores so that no ambiguity occurs between the camelCased version of, say, JUNIO_HAMANO and JUNIO__HAMANO.\n\nI inserted an `assert()`;\n\n>> +\t\t\tif (toupper(text[j]) != c)\n> \n> I know the performance would not matter very much but calling\n> toupper() for each letter in the user input FSCK_MSG_MAX times\n> sounds rather inefficient.\n> \n> Would it make sense to make the caller upcase instead (or upcase\n> upfront in the function)?\n\nAs you said, performance plays a lesser role here than simplicity. The strings we receive here are passed in read-only, therefore I would have to `strdup()` them first and then make sure to `free()` them. That was too un-simple for my taste.\n\nHaving said that, if you disagree, I will introduce the complexity, though. Do you want me to do that?\n\nCiao,\nDscho\n"},{"id":"264438","messageId":"bc06c48f005ad1c32dea2edbfa466208@www.dscho.org","threadId":"38144","inReplyTo":"xmqqzj3v7b58.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-21T13:59:29Z","receivedAt":"2015-06-21T13:59:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 21:26, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> +static inline int substrcmp(const char *string, int len, const char *match)\n>> +{\n>> +\tint match_len = strlen(match);\n>> +\tif (match_len != len)\n>> +\t\treturn -1;\n>> +\treturn memcmp(string, match, len);\n>> +}\n> \n> What this does looks suspiciously like starts_with(), but its name\n> \"substrcmp()\" does not give any hint that this is about the beginnig\n> part of \"string\"; if anything, it gives a wrong hint that it may be\n> any substring.  prefixcmp() might be a better name but that was the\n> old name for !starts_with() so we cannot use it here.  It is a\n> mouthful, but starts_with_counted() may be.\n\nIt is actually not `prefixcmp()` because it requires that `match` has precisely `len` bytes, while `prefixcmp()` or `starts_with()` would not care as long as the first `strlen(match)` bytes of `string` match `match`.\n\nMaybe `fixed_length_strcmp()` would be an appropriate name, but it is pretty long and *still* does not convey exactly what this function is about.\n\nAlso please note that this is a `static inline` function, i.e. its definition does not bleed outside of this file.\n\nSo I hope that you are satisfied with simply adding\n\n    /** compares a counted string to a NUL-terminated one. */\n\nas comment above the `substrcmp()` function?\n\n> But the whole thing may be moot.\n> \n> If we take the \"why not upcase the end-user string upfront\"\n> suggestion from the previous review, fsck_set_msg_types() would have\n> an upcased copy of the end-user string that it can muck with; it can\n> turn \"badfoo=error,poorbar=...\" into \"BADFOO=error,POORBAR=...\"\n> that is stored in its own writable memory (possibly a strbuf), and\n> at that point it can afford to NUL-terminate BADFOO=error after\n> finding where one specification ends with strcspn() before calling\n> fsck_set_msg_type(), which in turn calls parse_msg_type().\n\nHmm. I really do not like that kind of thinking, i.e. having to duplicate, then modify data to be able to call the API, only to have to modify the data back afterwards, and eventually having to unallocate the data in all code paths. That feels just very inelegant to me.\n\nI agree that it cannot be helped, sometimes, when you *have* to do exactly such mucking with data on a copy, be it to avoid additional complexity or poor performance. But I simply do not see the need for that here.\n\nCiao,\nDscho\n"},{"id":"264442","messageId":"a9e5e7c2580ed138d3fc9576799fe1df@www.dscho.org","threadId":"38144","inReplyTo":"000201d0abe0$7f3ccf20$7db66d60$@nexbridge.com","subject":"RE: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-21T14:40:35Z","receivedAt":"2015-06-21T14:40:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Randall,\n\nOn 2015-06-21 07:09, Randall S. Becker wrote:\n> On June 21, 2015 12:56 AM, Michael Haggerty wrote:\n>\n>> As for thinking of a shorter name for the option: assuming the blob integrity\n>> checks can be turned on and off independently as described above, then I think\n>> it is reasonable to *also* add a `--quick` option defined as\n>>\n>> --quick: Skip some expensive checks, dramatically reducing the\n>>     runtime of `git fsck`. Currently this is equivalent to\n>>     `--no-check-blob-integrity`.\n>>\n>> In the future if we invent other expensive checks we might also add them to the\n>> list of things that are skipped by `--quick`.\n> \n> Synonym suggestions: --links or --relations\n> I was going to include --refs but that may be ambiguous. Links also has\n> meaning so it's probably out and --hitch may just be silly and needlessly\n> introducing a new term.\n\nI appreciate your input, but I think in this case, \"links\" and \"relations\" would just add new terminology, and I would like to keep using existing terminology as much as possible (because we already have a confusingly large glossary). I also see \"link\" as a problematic term because it resembles \"gitlink\" -- which means something entirely different.\n\nMy favorite is still `--quick` because in contrast to Junio, to me it *has* a connotation of \"less thoroughly\". \"Get rich quick\" just does not mean \"Earn a lot of money by doing a thorough job, just faster than you usually would\".\n\nIf Junio insists, I will of course rename it to `--skip-blobs` because that is really what we do. However, it has been my experience over and over again that letting implementation details bleed through to the user interface always comes back to haunt you[*1*]. In this case, the purpose of the option really just was to cut a few corners to run `git fsck` more quickly. The fact that the corner I cut here is to skip unpacking of the blobs just happens to be the implementation detail.\n\nSo, Junio, what is it: keep `--quick` and clarify in the documentation that we cut corners (and which corners), or rename it to `--skip-blobs`? Your call.\n\nCiao,\nDscho\n\nFootnote *1*: You would not believe how often I had to apologize for renaming the `edit-patch-series` script to `interactive rebase`. You see, technically it is correct to call it an interactive rebase: it really is that, from a purely technical, soul-less point of view. From the users' point of view, however, the name `edit-patch-series` would relate exactly what the command is *about*, as opposed to *how* it works. To most users, calling it \"rebase -i\" is as understandable as \"sympodial fasciculation and regrafting of disconnate vernates\" (and don't ask me what that even means).\n"},{"id":"264444","messageId":"xmqqioahj849.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"558643CA.6000303@alum.mit.edu","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-21T17:15:18Z","receivedAt":"2015-06-21T17:15:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Michael Haggerty <mhagger@alum.mit.edu> writes:\n\n> But now that I'm writing this, a silly question occurs to me: Do we need\n> an overall option like this at all? If I demote all blob-integrity\n> checks to \"ignore\" via the mechanism that you have added, then shouldn't\n> fsck automatically detect that it doesn't have to open the blobs at all\n> and enable this speedup automatically?\n\nThat's brilliant.\n\nJust to make sure I am reading you correctly, you mean the current\noverall structure:\n\n\tif (! \"is the blob loadable and well-formed?\") {\n\t\treport(\"BAD BLOB\");\n                ... which is equivalent to ...\n                if (\"is bad_blob ignored?\")\n\t\t\t; /* no-op */\n\t\telse {\n\t\t\toutput \"BAD BLOB\";\n                        if (\"is bad_blob an error?\")\n\t\t\t\treturn 1; /* error */\n\t\t}\n\t}\n        ... other checks ...\n\ncan be turned into this structure:\n\n        if (\"is bad_blob ignored?\")\n\t\t;\n\telse if (! \"is the blob loadable and well-formed?\") {\n\t\treport(\"BAD BLOB\");\n                ... which would be equivalent to ...\n\t\toutput \"BAD BLOB\";\n                if (\"is bad_blob an error?\")\n\t\t\treturn 1; /* error */\n\t}\n        ... other checks ...\n\nI think that makes tons of sense.  With one minor caveat.  In the\nabove \"rewrite\" I deliberately described report() in the updated\nflow to always output, but that would force all checkers to adopt\n\"do not unconditionally check; if we do not report, do not even\nbother checking\", which (1) would be a large change to what Dscho\ncurrently have, and (2) might not apply to certain kinds of error\nconditions.\n\nBut that minor caveat is easily addressed by keeping the \"if we are\nset to ignore this error, just return 0\" in report().  Some codepaths\n(like the \"BAD BLOB\" above) may not exercise it by bypassing the\ncall to report() upfront and that is perfectly fine.\n\nI like that idea.\n\n> So maybe\n> `--(no-)?check-blob-integrity` is actually a shorthand for turning a few\n> more specific checks on/off at once.\n>\n> As for thinking of a shorter name for the option: assuming the blob\n> integrity checks can be turned on and off independently as described\n> above, then I think it is reasonable to *also* add a `--quick` option\n> defined as\n>\n> --quick: Skip some expensive checks, dramatically reducing the\n>     runtime of `git fsck`. Currently this is equivalent to\n>     `--no-check-blob-integrity`.\n>\n> In the future if we invent other expensive checks we might also add them\n> to the list of things that are skipped by `--quick`.\n\nYes, that is doubly brilliant. Taken together with the auto-skipping\nof the checks based on the report settings, it makes it unnecessary\nto even introduce --[no-]check-blob-integrity or any other new\nknobs.\n\nVery well analysed.  I am happy with the \"--quick\" with that\ndefinition.\n\nThanks.\n"},{"id":"264445","messageId":"xmqqegl5j75m.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"bc06c48f005ad1c32dea2edbfa466208@www.dscho.org","subject":"Re: [PATCH v6 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-21T17:36:05Z","receivedAt":"2015-06-21T17:36:05Z","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> Hmm. I really do not like that kind of thinking, i.e. having to\n> duplicate, then modify data to be able to call the API, only to have\n> to modify the data back afterwards, and eventually having to\n> unallocate the data in all code paths. That feels just very inelegant\n> to me.\n\nYou can see in our codebase that I have avoided touching end-user\nstrings by using a substring in place by introducing a new API\nfunction or using an existing API function that takes <ptr, len> for\nexactly that reason.  There also are cases where we are better off\nif we make a copy upfront at a very high level in the callchain if\nthat makes the processing of that string deeper in the callchain\nmuch simpler without customized helpers that take counted strings.\n\nAnd 03/19 and this one taken together, I think it is an example of\nthe latter [*1*].\n\nYou not only need to invent counted string comparison in 04/19 but\nalso need upcasing byte-by-byte comparison in a loop in 03/19; both\nof which can be made much simpler if you massaged the end-user input\n\"foo=error,bar=ignore\" into \"FOO=error,BAR=ignore\" and allowed the\ncode to loop over it to turn ',' into NUL while parsing each\nindividual piece (i.e. \"FOO=error\").\n\nSo contrary to what you said in response to my review on 03/19, I\nview this as not \"adding complexity\" but its total opposite.  It is\nto make the code and logic much less complex by paying the price for\none copied (and massaged) string.\n\nHaving said all that, as long as the result functions correctly, I\nsuspect that it may not matter much either way.  As I already said,\n\"we can demote this error to a warning (or ignored)\" is much less\nuseful in practice than the \"we know this and that object in this\nproject does not pass fsck, so please do not bother checking\" in my\nmind, and that makes me think that this part of the series is much\nless important than the skip-list thing.  Unnecessary complexity in\nthe code and otherwise useless helper functions may be something\nthat can be simplified following my advice, but that can be done as\na code clean-up, simplification and optimization by somebody else\nlater if they really cared.\n\nThanks.\n\n\n[Footnote]\n\n*1* Everything I say in my messages is what \"I think\", so saying \"I\nthink\" before saying what I think is redundant, but this one needs\nthat, because I think ;-) it is in the \"taste\" territory to view\neach individual case and decide which one of these two opposite\napproaches is more appropriate.\n"},{"id":"264446","messageId":"8545f1b636f157b280f15c615c1e1756@www.dscho.org","threadId":"38144","inReplyTo":"xmqqegl5j75m.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-21T18:23:22Z","receivedAt":"2015-06-21T18:23:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-21 19:36, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> Hmm. I really do not like that kind of thinking, i.e. having to\n>> duplicate, then modify data to be able to call the API, only to have\n>> to modify the data back afterwards, and eventually having to\n>> unallocate the data in all code paths. That feels just very inelegant\n>> to me.\n> \n> You can see in our codebase that I have avoided touching end-user\n> strings by using a substring in place by introducing a new API\n> function or using an existing API function that takes <ptr, len> for\n> exactly that reason.  There also are cases where we are better off\n> if we make a copy upfront at a very high level in the callchain if\n> that makes the processing of that string deeper in the callchain\n> much simpler without customized helpers that take counted strings.\n> \n> And 03/19 and this one taken together, I think it is an example of\n> the latter [*1*].\n> \n> You not only need to invent counted string comparison in 04/19 but\n> also need upcasing byte-by-byte comparison in a loop in 03/19; both\n> of which can be made much simpler if you massaged the end-user input\n> \"foo=error,bar=ignore\" into \"FOO=error,BAR=ignore\" and allowed the\n> code to loop over it to turn ',' into NUL while parsing each\n> individual piece (i.e. \"FOO=error\").\n> \n> So contrary to what you said in response to my review on 03/19, I\n> view this as not \"adding complexity\" but its total opposite.  It is\n> to make the code and logic much less complex by paying the price for\n> one copied (and massaged) string.\n\nHow about I implement your suggestion tomorrow, then show the diff between the two versions and we can assess what looks to be simpler (i.e. more maintainable)?\n\nBTW I agree about the skip-list feature being the most important outcome of this patch series; It only occurred to me as a useful feature at the very end of my work on the fsck IDs, but then I did not want to throw away all of that previous work ;-)\n\nCiao,\nDscho\n"},{"id":"264450","messageId":"3faa92b10274ce8cfebe340761f73505@www.dscho.org","threadId":"38144","inReplyTo":"xmqqioahj849.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-21T18:27:53Z","receivedAt":"2015-06-21T18:27:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-21 19:15, Junio C Hamano wrote:\n> Michael Haggerty <mhagger@alum.mit.edu> writes:\n> \n>> But now that I'm writing this, a silly question occurs to me: Do we need\n>> an overall option like this at all? If I demote all blob-integrity\n>> checks to \"ignore\" via the mechanism that you have added, then shouldn't\n>> fsck automatically detect that it doesn't have to open the blobs at all\n>> and enable this speedup automatically?\n> \n> That's brilliant.\n> \n> Just to make sure I am reading you correctly, you mean the current\n> overall structure:\n> \n> [...]\n\nThe way I read Michael's mail, he actually meant something different: if all of the blob-related errors/warnings are switched to \"ignore\", simply skip unpacking the blobs. If I read this correctly, I would like to point out that introducing a future blob-related check would require scripts to be changed to benefit from the speed-up, unless the `--quick` option is *also* introduced.\n\nThe way I read your suggestion, however, was to introduce *yet another* message ID: BAD_BLOB. If that one is turned to \"ignore\", we simply won't look at blobs' contents at all.\n\nI like that idea, together with:\n\n>> --quick: Skip some expensive checks, dramatically reducing the\n>>     runtime of `git fsck`. Currently this is equivalent to\n>>     `--no-check-blob-integrity`.\n\n... where this would become `-c fsck.badBlob=ignore`.\n\n>> In the future if we invent other expensive checks we might also add them\n>> to the list of things that are skipped by `--quick`.\n> \n> Yes, that is doubly brilliant. Taken together with the auto-skipping\n> of the checks based on the report settings, it makes it unnecessary\n> to even introduce --[no-]check-blob-integrity or any other new\n> knobs.\n> \n> Very well analysed.  I am happy with the \"--quick\" with that\n> definition.\n\nDid I understand you correctly? If so, I will gladly implement this tomorrow and send out v7.\n\nCiao,\nDscho\n"},{"id":"264453","messageId":"xmqq616gkiez.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"8545f1b636f157b280f15c615c1e1756@www.dscho.org","subject":"Re: [PATCH v6 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-21T18:47:32Z","receivedAt":"2015-06-21T18:47: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> How about I implement your suggestion tomorrow, then show the diff\n> between the two versions and we can assess what looks to be simpler\n> (i.e. more maintainable)?\n\nI'm indifferent at this point, partly because as we agree that what\nyou have as long as it works is ok, and more importantly, Michael's\nsuggestion to turn \"check unconditionally and only control if we\nignore or warn or error out the result\" into \"do not even check if\nwe are told to ignore\" is a much more prouctive thing to spend your\ntime on.\n"},{"id":"264457","messageId":"xmqq1th4kdeq.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"3faa92b10274ce8cfebe340761f73505@www.dscho.org","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-21T20:35:41Z","receivedAt":"2015-06-21T20:35:41Z","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> On 2015-06-21 19:15, Junio C Hamano wrote:\n> Michael Haggerty <mhagger@alum.mit.edu> writes:\n>> That's brilliant.\n>> \n>> Just to make sure I am reading you correctly, you mean the current\n>> overall structure:\n>> \n>> [...]\n>\n> The way I read Michael's mail, he actually meant something different:\n> if all of the blob-related errors/warnings are switched to \"ignore\",\n> simply skip unpacking the blobs.\n\nThat is how I read his mail, too.\n\nBut because IIRC we do not check anything special with blob other\nthan we can read it correctly, my description of \"overall structure\"\nstayed at a very high conceptual level.  The unpacking may happen at\na much higher level in the code, i.e. it comes way before this part\nof the logic flow:\n\n        if (\"is bad_blob ignored?\")\n\t\t;\n\telse if (! \"is the blob loadable and well-formed?\") {\n\nin which case \"is bad blobs ignored?\" check may have to happen\nbefore we unpack the object.\n\nAnd I do not suggest introducing yet another BAD_BLOB error class; I\nwould have guessed that you already have an error class for objects\nthat are not stored correctly (be it truncated loose object, checksum\nmismatch in the packed base object, or corrupt delta in pack).\n\nIt so happens that blob is the only type of object that does not\nhave outgoing links that is needed for connectivity check, so even\nif you allow to ignore \"error class for objects that are not stored\ncorrectly\", you would still have to read trees, commits and tags;\nit would be a natural consequence of ignoring that class of errors\nthat you would get a quick-and-dirty fsck by not unpacking blobs.\n\nOf course, that assumes that you can tell an object is a blob\nwithout unpacking.  If a tree entry mentions an object to be a blob\nby having 100644 as its mode, unless you unpack the object pointed\nat by that tree entry to make sure it is a blob, you wouldn't be\nable to detect a case where a non-blob object is stored with 100644\nmode, which would be an error in the containing tree object that we\nmay want to detect.  I am not sure if \"skipping inflation of blobs,\nbut still ensure connectivity and tree integrity\" is really a viable\nmode of quick-and-dirty operation.  I would imagine you would need\nto lose a bit more than \"we don't bother reading blobs\" (which is OK\nby me, but I am just pointing out that (1) I do not mean to say we\nshould add BAD_BLOB as a new class, and (2) the automatic bypass\nMichael's --quick skips may not be limited to suppressing \"we cannot\nread this blob object\" class, but also need to suppress checks for\nsome form of tree integrity violation).\n\nThanks.\n"},{"id":"264459","messageId":"xmqqwpywiybv.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"xmqq1th4kdeq.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-21T20:46:44Z","receivedAt":"2015-06-21T20:46:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Of course, that assumes that you can tell an object is a blob\n> without unpacking.  If a tree entry mentions an object to be a blob\n> by having 100644 as its mode, unless you unpack the object pointed\n> at by that tree entry to make sure it is a blob, you wouldn't be\n> able to detect a case where a non-blob object is stored with 100644\n> mode, which would be an error in the containing tree object that we\n> may want to detect.\n\nHeh, I was being stupid here.  Of course, you can tell an object is\na blob without fully inflating it (we should already be doing that\nin sha1_object_info(), inflating only the object header in loose\nobjects or inflating only the early parts of delta in packs to\nfollow the delta chain to learn the type of the object), and that\nwould certainly save us substantial zlib cost.\n\nSorry about the noise.\n"},{"id":"264487","messageId":"CAPc5daV2P_vR-wjuU-0W81F9_phawHaQM1HHpOqNTsNDyQ5NHQ@mail.gmail.com","threadId":"38144","inReplyTo":"e843f9f1defca543d3f2eb3143cf9fee8c72f695.1434720655.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v6 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-22T04:21:05Z","receivedAt":"2015-06-22T04:21:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Fri, Jun 19, 2015 at 6:35 AM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n>\n> @@ -227,6 +277,10 @@ static int report(struct fsck_options *options, struct object *object,\n>         if (msg_type == FSCK_IGNORE)\n>                 return 0;\n>\n> +       if (options->skiplist && object &&\n> +                       sha1_array_lookup(options->skiplist, object->sha1) >= 0)\n> +               return 0;\n> +\n>         if (msg_type == FSCK_FATAL)\n>                 msg_type = FSCK_ERROR;\n>         else if (msg_type == FSCK_INFO)\n\nI just double checked this patch because I wanted to make sure this\nwas applied in the\nreport() function (i.e. behave as if FSCK_IGNORE was specified for\nspecific objects on\nthe skip list), and I am happy to see that it indeed is the case.\n\nThat was because I briefly feared that skip could be done before going\nthrough the usual\nverification chain, which would have been very wrong (e.g. we may want\nnot to hear about\nmissing tagger in v2.6.11-tree tag, but nevertheless we would want to\ncheck all the tree\ncontents pointed at by that tag, as that tree may not be reachable by\nany other way).\n\nSo this one looks good.\n"},{"id":"264493","messageId":"5fd3e400f4466965f8949ecf2440fc41@www.dscho.org","threadId":"38144","inReplyTo":"CAPc5daV2P_vR-wjuU-0W81F9_phawHaQM1HHpOqNTsNDyQ5NHQ@mail.gmail.com","subject":"Re: [PATCH v6 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T08:49:12Z","receivedAt":"2015-06-22T08:49:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-22 06:21, Junio C Hamano wrote:\n> On Fri, Jun 19, 2015 at 6:35 AM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n>>\n>> @@ -227,6 +277,10 @@ static int report(struct fsck_options *options, struct object *object,\n>>         if (msg_type == FSCK_IGNORE)\n>>                 return 0;\n>>\n>> +       if (options->skiplist && object &&\n>> +                       sha1_array_lookup(options->skiplist, object->sha1) >= 0)\n>> +               return 0;\n>> +\n>>         if (msg_type == FSCK_FATAL)\n>>                 msg_type = FSCK_ERROR;\n>>         else if (msg_type == FSCK_INFO)\n> \n> I just double checked this patch because I wanted to make sure this\n> was applied in the\n> report() function (i.e. behave as if FSCK_IGNORE was specified for\n> specific objects on\n> the skip list), and I am happy to see that it indeed is the case.\n> \n> That was because I briefly feared that skip could be done before going\n> through the usual\n> verification chain, which would have been very wrong (e.g. we may want\n> not to hear about\n> missing tagger in v2.6.11-tree tag, but nevertheless we would want to\n> check all the tree\n> contents pointed at by that tag, as that tree may not be reachable by\n> any other way).\n\nTo be honest, an earlier iteration actually did have that test much earlier in the call chain, but I had changed it to the current location in v5.\n\nMy rationale was slightly different from yours: I wanted to affect the performance as little as possible. So looking up each and every object in the skip list (which I expect to be relatively small) seemed to be wasteful. And then it occurred to me that it would make much more sense to just make the skip-list functionality equivalent to the \"ignore\" message type.\n\nIt just occurred to me, however, that one thing is possibly surprising with either version of the skip list functionality: if a certain object is corrupt on disk, it cannot be skipped via the skip-list, as the object is *still* unpacked (which would fail in the case of a corrupt object).\n\nI will document that in the man page.\n\nCiao,\nDscho\n"},{"id":"264510","messageId":"80dac4a49123c6e4ddc905c74d63d44e@www.dscho.org","threadId":"38144","inReplyTo":"xmqq1th4kdeq.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 17/19] fsck: Introduce `git fsck --quick`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T13:01:50Z","receivedAt":"2015-06-22T13:01:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-21 22:35, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> On 2015-06-21 19:15, Junio C Hamano wrote:\n>> Michael Haggerty <mhagger@alum.mit.edu> writes:\n>>> That's brilliant.\n>>>\n>>> Just to make sure I am reading you correctly, you mean the current\n>>> overall structure:\n>>>\n>>> [...]\n>>\n>> The way I read Michael's mail, he actually meant something different:\n>> if all of the blob-related errors/warnings are switched to \"ignore\",\n>> simply skip unpacking the blobs.\n> \n> That is how I read his mail, too.\n> \n> But because IIRC we do not check anything special with blob other\n> than we can read it correctly, my description of \"overall structure\"\n> stayed at a very high conceptual level.  The unpacking may happen at\n> a much higher level in the code, i.e. it comes way before this part\n> of the logic flow:\n> \n>         if (\"is bad_blob ignored?\")\n> \t\t;\n> \telse if (! \"is the blob loadable and well-formed?\") {\n> \n> in which case \"is bad blobs ignored?\" check may have to happen\n> before we unpack the object.\n> \n> And I do not suggest introducing yet another BAD_BLOB error class; I\n> would have guessed that you already have an error class for objects\n> that are not stored correctly (be it truncated loose object, checksum\n> mismatch in the packed base object, or corrupt delta in pack).\n\nSadly, there is no BAD_BLOB class. The reason is that we actually perform no test on blobs, as you pointed out, except for the implicit one: read it as a blob object.\n\nAnd reading them even only partially would still imply a lot of I/O, taking away much of the performance improvement I wanted to achieve here.\n\nFurther, please note that the `--quick` option *solely* impacts `git fsck`, not `git receive-pack`, because we actually really skipped everything except the connectivity test.\n\nTo allow this discussion to be resolved without further ado, I therefore renamed the `--quick` option to `--connectivity-only`, as even I realize that there is not much of a check left if not even author or committer lines are tested.\n\nCiao,\nDscho\n"},{"id":"264543","messageId":"d58343ff03fbc41b8d1346419ce2c389@www.dscho.org","threadId":"38144","inReplyTo":"xmqqzj3v7b58.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v6 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:24:35Z","receivedAt":"2015-06-22T15:24:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-19 21:26, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> +static inline int substrcmp(const char *string, int len, const char *match)\n>> +{\n>> +\tint match_len = strlen(match);\n>> +\tif (match_len != len)\n>> +\t\treturn -1;\n>> +\treturn memcmp(string, match, len);\n>> +}\n> \n> What this does looks suspiciously like starts_with(), but its name\n> \"substrcmp()\" does not give any hint that this is about the beginnig\n> part of \"string\"; if anything, it gives a wrong hint that it may be\n> any substring.  prefixcmp() might be a better name but that was the\n> old name for !starts_with() so we cannot use it here.  It is a\n> mouthful, but starts_with_counted() may be.\n> \n> But the whole thing may be moot.\n> \n> If we take the \"why not upcase the end-user string upfront\"\n> suggestion from the previous review, fsck_set_msg_types() would have\n> an upcased copy of the end-user string that it can muck with; it can\n> turn \"badfoo=error,poorbar=...\" into \"BADFOO=error,POORBAR=...\"\n> that is stored in its own writable memory (possibly a strbuf), and\n> at that point it can afford to NUL-terminate BADFOO=error after\n> finding where one specification ends with strcspn() before calling\n> fsck_set_msg_type(), which in turn calls parse_msg_type().\n> \n> So all parse_msg_type() needs to do is just !strcmp().\n\nTurns out that the diffstat says it saves 10 lines. So I changed it according to your suggestion. Part of v7.\n\nCiao,\nDscho\n"},{"id":"264544","messageId":"cover.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434720655.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:24:45Z","receivedAt":"2015-06-22T15:24:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"At the moment, the git-fsck's integrity checks are targeted toward the\nend user, i.e. the error messages are really just messages, intended for\nhuman consumption.\n\nUnder certain circumstances, some of those errors should be allowed to\nbe turned into mere warnings, though, because the cost of fixing the\nissues might well be larger than the cost of carrying those flawed\nobjects. For example, when an already-public repository contains a\ncommit object with two authors for years, it does not make sense to\nforce the maintainer to rewrite the history, affecting all contributors\nnegatively by forcing them to update.\n\nThis branch introduces an internal fsck API to be able to turn some of\nthe errors into warnings, and to make it easier to call the fsck\nmachinery from elsewhere in general.\n\nI am proud to report that this work has been sponsored by GitHub.\n\nChanges since v6:\n\n- camelCased message IDs\n\n- multiple author checking now as suggested by Junio\n\n- renamed `--quick` to `--connectivity-only`, better commit message\n\n- `fsck.skipList` is now handled correctly (and not mistaken for a message\n  type setting)\n\n- `fsck.skipList` can handle user paths now\n\n- index-pack configures the walk function in a more logical place now\n\n- simplified code by avoiding working on partial strings (i.e. removed\n  `substrcmp()`). This saves 10 lines. To accomodate parsing config\n  variables directly, we now work on lowercased message IDs; unfortunately\n  this means that we cannot use them in append_msg_id() because that\n  function wants to append camelCased message IDs.\n\nInterdiff below diffstat.\n\nJohannes Schindelin (19):\n  fsck: Introduce fsck options\n  fsck: Introduce identifiers for fsck messages\n  fsck: Provide a function to parse fsck message IDs\n  fsck: Offer a function to demote fsck errors to warnings\n  fsck (receive-pack): Allow demoting errors to warnings\n  fsck: Report the ID of the error/warning\n  fsck: Make fsck_ident() warn-friendly\n  fsck: Make fsck_commit() warn-friendly\n  fsck: Handle multiple authors in commits specially\n  fsck: Make fsck_tag() warn-friendly\n  fsck: Add a simple test for receive.fsck.<msg-id>\n  fsck: Disallow demoting grave fsck errors to warnings\n  fsck: Optionally ignore specific fsck issues completely\n  fsck: Allow upgrading fsck warnings to errors\n  fsck: Document the new receive.fsck.<msg-id> options\n  fsck: Support demoting errors to warnings\n  fsck: Introduce `git fsck --connectivity-only`\n  fsck: git receive-pack: support excluding objects from fsck'ing\n  fsck: support ignoring objects in `git fsck` via fsck.skiplist\n\n Documentation/config.txt        |  41 +++\n Documentation/git-fsck.txt      |   7 +-\n builtin/fsck.c                  |  78 ++++--\n builtin/index-pack.c            |  13 +-\n builtin/receive-pack.c          |  28 +-\n builtin/unpack-objects.c        |  16 +-\n fsck.c                          | 554 +++++++++++++++++++++++++++++++---------\n fsck.h                          |  30 ++-\n t/t1450-fsck.sh                 |  37 ++-\n t/t5302-pack-index.sh           |   2 +-\n t/t5504-fetch-receive-strict.sh |  51 ++++\n 11 files changed, 692 insertions(+), 165 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5aba63a..69dda93 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1252,11 +1252,11 @@ filter.<driver>.smudge::\n \n fsck.<msg-id>::\n \tAllows overriding the message type (error, warn or ignore) of a\n-\tspecific message ID such as `missingemail`.\n+\tspecific message ID such as `missingEmail`.\n +\n For convenience, fsck prefixes the error/warning with the message ID,\n-e.g.  \"missingemail: invalid author/committer line - missing email\" means\n-that setting `fsck.missingemail = ignore` will hide that issue.\n+e.g.  \"missingEmail: invalid author/committer line - missing email\" means\n+that setting `fsck.missingEmail = ignore` will hide that issue.\n +\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n@@ -1267,6 +1267,7 @@ fsck.skipList::\n \tbe ignored. This feature is useful when an established project\n \tshould be accepted despite early commits containing errors that\n \tcan be safely ignored such as invalid committer email addresses.\n+\tNote: corrupt objects cannot be skipped with this setting.\n \n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n@@ -2228,9 +2229,9 @@ receive.fsck.<msg-id>::\n \tto warnings and vice versa by configuring the `receive.fsck.<msg-id>`\n \tsetting where the `<msg-id>` is the fsck message ID and the value\n \tis one of `error`, `warn` or `ignore`. For convenience, fsck prefixes\n-\tthe error/warning with the message ID, e.g. \"missingemail: invalid\n+\tthe error/warning with the message ID, e.g. \"missingEmail: invalid\n \tauthor/committer line - missing email\" means that setting\n-\t`receive.fsck.missingemail = ignore` will hide that issue.\n+\t`receive.fsck.missingEmail = ignore` will hide that issue.\n +\n This feature is intended to support working with legacy repositories\n which would not pass pushing when `receive.fsckObjects = true`, allowing\n@@ -2243,6 +2244,7 @@ receive.fsck.skipList::\n \tbe ignored. This feature is useful when an established project\n \tshould be accepted despite early commits containing errors that\n \tcan be safely ignored such as invalid committer email addresses.\n+\tNote: corrupt objects cannot be skipped with this setting.\n \n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex b98fb43..84ee92e 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -10,8 +10,8 @@ SYNOPSIS\n --------\n [verse]\n 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n-\t [--[no-]full] [--quick] [--strict] [--verbose] [--lost-found]\n-\t [--[no-]dangling] [--[no-]progress] [<object>*]\n+\t [--[no-]full] [--strict] [--verbose] [--lost-found]\n+\t [--[no-]dangling] [--[no-]progress] [--connectivity-only] [<object>*]\n \n DESCRIPTION\n -----------\n@@ -60,10 +60,10 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \tobject pools.  This is now default; you can turn it off\n \twith --no-full.\n \n---quick::\n+--connectivity-only::\n \tCheck only the connectivity of tags, commits and tree objects. By\n \tavoiding to unpack blobs, this speeds up the operation, at the\n-\texpense of missing corrupt objects.\n+\texpense of missing corrupt objects or other problematic issues.\n \n --strict::\n \tEnable more strict checking, namely to catch a file mode\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex ce538ac..7e3df20 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -23,7 +23,7 @@ static int show_tags;\n static int show_unreachable;\n static int include_reflogs = 1;\n static int check_full = 1;\n-static int quick;\n+static int connectivity_only;\n static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n@@ -49,21 +49,24 @@ static int show_dangling = 1;\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n-\tif (skip_prefix(var, \"fsck.\", &var)) {\n-\t\tfsck_set_msg_type(&fsck_obj_options, var, -1, value, -1);\n-\t\treturn 0;\n-\t}\n-\n \tif (strcmp(var, \"fsck.skiplist\") == 0) {\n-\t\tconst char *path = is_absolute_path(value) ?\n-\t\t\tvalue : git_path(\"%s\", value);\n+\t\tconst char *path;\n \t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (git_config_pathname(&path, var, value))\n+\t\t\treturn 1;\n \t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfree((char *) path);\n \t\tfsck_set_msg_types(&fsck_obj_options, sb.buf);\n \t\tstrbuf_release(&sb);\n \t\treturn 0;\n \t}\n \n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tfsck_set_msg_type(&fsck_obj_options, var, value);\n+\t\treturn 0;\n+\t}\n+\n \treturn git_default_config(var, value, cb);\n }\n \n@@ -192,7 +195,7 @@ static void check_reachable_object(struct object *obj)\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (has_sha1_pack(obj->sha1))\n \t\t\treturn; /* it is in pack - forget about it */\n-\t\tif (quick && has_sha1_file(obj->sha1))\n+\t\tif (connectivity_only && has_sha1_file(obj->sha1))\n \t\t\treturn;\n \t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n \t\terrors_found |= ERROR_REACHABLE;\n@@ -636,7 +639,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"cache\", &keep_cache_objects, N_(\"make index objects head nodes\")),\n \tOPT_BOOL(0, \"reflogs\", &include_reflogs, N_(\"make reflogs head nodes (default)\")),\n \tOPT_BOOL(0, \"full\", &check_full, N_(\"also consider packs and alternate objects\")),\n-\tOPT_BOOL(0, \"quick\", &quick, N_(\"check only connectivity\")),\n+\tOPT_BOOL(0, \"connectivity-only\", &connectivity_only, N_(\"check only connectivity\")),\n \tOPT_BOOL(0, \"strict\", &check_strict, N_(\"enable more strict checking\")),\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n@@ -673,7 +676,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tif (!quick)\n+\tif (!connectivity_only)\n \t\tfsck_object_dir(get_object_directory());\n \n \tprepare_alt_odb();\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 98e14fe..f0d283b 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -841,7 +841,6 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (do_fsck_object &&\n \t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"Error in object\"));\n-\t\t\tfsck_options.walk = mark_link;\n \t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n \n@@ -1616,6 +1615,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\tusage(index_pack_usage);\n \n \tcheck_replace_refs = 0;\n+\tfsck_options.walk = mark_link;\n \n \treset_pack_idx_option(&opts);\n \tgit_config(git_index_pack_config, &opts);\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 80574f9..3fbed23 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -118,10 +118,13 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t}\n \n \tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n-\t\tconst char *path = is_absolute_path(value) ?\n-\t\t\tvalue : git_path(\"%s\", value);\n+\t\tconst char *path;\n+\n+\t\tif (git_config_pathname(&path, var, value))\n+\t\t\treturn 1;\n \t\tstrbuf_addf(&fsck_msg_types, \"%cskiplist=%s\",\n \t\t\tfsck_msg_types.len ? ',' : '=', path);\n+\t\tfree((char *) path);\n \t\treturn 0;\n \t}\n \ndiff --git a/fsck.c b/fsck.c\nindex f80b508..a677b50 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -71,37 +71,42 @@ enum fsck_msg_id {\n #undef MSG_ID\n \n #define STR(x) #x\n-#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n+#define MSG_ID(id, msg_type) { STR(id), NULL, FSCK_##msg_type },\n static struct {\n \tconst char *id_string;\n+\tconst char *lowercased;\n \tint msg_type;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n-\t{ NULL, -1 }\n+\t{ NULL, NULL, -1 }\n };\n #undef MSG_ID\n \n-static int parse_msg_id(const char *text, int len)\n+static int parse_msg_id(const char *text)\n {\n-\tint i, j;\n-\n-\tif (len < 0)\n-\t\tlen = strlen(text);\n-\n-\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n-\t\tconst char *key = msg_id_info[i].id_string;\n-\t\t/* match id_string case-insensitively, without underscores. */\n-\t\tfor (j = 0; j < len; j++) {\n-\t\t\tchar c = *(key++);\n-\t\t\tif (c == '_')\n-\t\t\t\tc = *(key++);\n-\t\t\tif (toupper(text[j]) != c)\n-\t\t\t\tbreak;\n+\tint i;\n+\n+\tif (!msg_id_info[0].lowercased) {\n+\t\t/* convert id_string to lower case, without underscores. */\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n+\t\t\tconst char *p = msg_id_info[i].id_string;\n+\t\t\tint len = strlen(p);\n+\t\t\tchar *q = xmalloc(len);\n+\n+\t\t\tmsg_id_info[i].lowercased = q;\n+\t\t\twhile (*p)\n+\t\t\t\tif (*p == '_')\n+\t\t\t\t\tp++;\n+\t\t\t\telse\n+\t\t\t\t\t*(q)++ = tolower(*(p)++);\n+\t\t\t*q = '\\0';\n \t\t}\n-\t\tif (j == len && !*key)\n-\t\t\treturn i;\n \t}\n \n+\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\tif (!strcmp(text, msg_id_info[i].lowercased))\n+\t\t\treturn i;\n+\n \treturn -1;\n }\n \n@@ -160,51 +165,37 @@ static void init_skiplist(struct fsck_options *options, const char *path)\n \t\tskiplist.sorted = 1;\n }\n \n-static inline int substrcmp(const char *string, int len, const char *match)\n-{\n-\tint match_len = strlen(match);\n-\tif (match_len != len)\n-\t\treturn -1;\n-\treturn memcmp(string, match, len);\n-}\n-\n-static int parse_msg_type(const char *str, int len)\n+static int parse_msg_type(const char *str)\n {\n-\tif (len < 0)\n-\t\tlen = strlen(str);\n-\n-\tif (!substrcmp(str, len, \"error\"))\n+\tif (!strcmp(str, \"error\"))\n \t\treturn FSCK_ERROR;\n-\telse if (!substrcmp(str, len, \"warn\"))\n+\telse if (!strcmp(str, \"warn\"))\n \t\treturn FSCK_WARN;\n-\telse if (!substrcmp(str, len, \"ignore\"))\n+\telse if (!strcmp(str, \"ignore\"))\n \t\treturn FSCK_IGNORE;\n \telse\n-\t\tdie(\"Unknown fsck message type: '%.*s'\",\n-\t\t\t\tlen, str);\n+\t\tdie(\"Unknown fsck message type: '%s'\", str);\n }\n \n int is_valid_msg_type(const char *msg_id, const char *msg_type)\n {\n-\tif (parse_msg_id(msg_id, -1) < 0)\n+\tif (parse_msg_id(msg_id) < 0)\n \t\treturn 0;\n-\tparse_msg_type(msg_type, -1);\n+\tparse_msg_type(msg_type);\n \treturn 1;\n }\n \n void fsck_set_msg_type(struct fsck_options *options,\n-\t\tconst char *msg_id, int msg_id_len,\n-\t\tconst char *msg_type, int msg_type_len)\n+\t\tconst char *msg_id, const char *msg_type)\n {\n-\tint id = parse_msg_id(msg_id, msg_id_len), type;\n+\tint id = parse_msg_id(msg_id), type;\n \n \tif (id < 0)\n-\t\tdie(\"Unhandled message id: %.*s\", msg_id_len, msg_id);\n-\ttype = parse_msg_type(msg_type, msg_type_len);\n+\t\tdie(\"Unhandled message id: %s\", msg_id);\n+\ttype = parse_msg_type(msg_type);\n \n \tif (type != FSCK_ERROR && msg_id_info[id].msg_type == FSCK_FATAL)\n-\t\tdie(\"Cannot demote %.*s to %.*s\", msg_id_len, msg_id,\n-\t\t\t\tmsg_type_len, msg_type);\n+\t\tdie(\"Cannot demote %s to %s\", msg_id, msg_type);\n \n \tif (!options->msg_type) {\n \t\tint i;\n@@ -219,37 +210,39 @@ void fsck_set_msg_type(struct fsck_options *options,\n \n void fsck_set_msg_types(struct fsck_options *options, const char *values)\n {\n-\twhile (*values) {\n-\t\tint len = strcspn(values, \" ,|\"), equal;\n+\tchar *buf = xstrdup(values), *to_free = buf;\n+\tint done = 0;\n \n+\twhile (!done) {\n+\t\tint len = strcspn(buf, \" ,|\"), equal;\n+\n+\t\tdone = !buf[len];\n \t\tif (!len) {\n-\t\t\tvalues++;\n+\t\t\tbuf++;\n \t\t\tcontinue;\n \t\t}\n+\t\tbuf[len] = '\\0';\n \n-\t\tfor (equal = 0; equal < len; equal++)\n-\t\t\tif (values[equal] == '=' || values[equal] == ':')\n-\t\t\t\tbreak;\n-\n-\t\tif (!substrcmp(values, equal, \"skiplist\")) {\n-\t\t\tchar *path = xstrndup(values + equal + 1,\n-\t\t\t\tlen - equal - 1);\n+\t\tfor (equal = 0; equal < len &&\n+\t\t\t\tbuf[equal] != '=' && buf[equal] != ':'; equal++)\n+\t\t\tbuf[equal] = tolower(buf[equal]);\n+\t\tbuf[equal] = '\\0';\n \n+\t\tif (!strcmp(buf, \"skiplist\")) {\n \t\t\tif (equal == len)\n \t\t\t\tdie(\"skiplist requires a path\");\n-\t\t\tinit_skiplist(options, path);\n-\t\t\tfree(path);\n-\t\t\tvalues += len;\n+\t\t\tinit_skiplist(options, buf + equal + 1);\n+\t\t\tbuf += len + 1;\n \t\t\tcontinue;\n \t\t}\n \n \t\tif (equal == len)\n-\t\t\tdie(\"Missing '=': '%.*s'\", len, values);\n+\t\t\tdie(\"Missing '=': '%s'\", buf);\n \n-\t\tfsck_set_msg_type(options, values, equal,\n-\t\t\t\tvalues + equal + 1, len - equal - 1);\n-\t\tvalues += len;\n+\t\tfsck_set_msg_type(options, buf, buf + equal + 1);\n+\t\tbuf += len + 1;\n \t}\n+\tfree(to_free);\n }\n \n static void append_msg_id(struct strbuf *sb, const char *msg_id)\n@@ -261,6 +254,10 @@ static void append_msg_id(struct strbuf *sb, const char *msg_id)\n \t\t\tbreak;\n \t\tif (c != '_')\n \t\t\tstrbuf_addch(sb, tolower(c));\n+\t\telse {\n+\t\t\tassert(*msg_id);\n+\t\t\tstrbuf_addch(sb, *(msg_id)++);\n+\t\t}\n \t}\n \n \tstrbuf_addstr(sb, \": \");\n@@ -600,7 +597,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n-\tunsigned parent_count, parent_line_count = 0;\n+\tunsigned parent_count, parent_line_count = 0, author_count;\n \tint err;\n \n \tif (require_end_of_header(buffer, size, &commit->object, options))\n@@ -640,19 +637,19 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\t\t\treturn err;\n \t\t}\n \t}\n-\tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, options);\n-\tif (err)\n-\t\treturn err;\n+\tauthor_count = 0;\n \twhile (skip_prefix(buffer, \"author \", &buffer)) {\n-\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n-\t\tif (err)\n-\t\t\treturn err;\n+\t\tauthor_count++;\n \t\terr = fsck_ident(&buffer, &commit->object, options);\n \t\tif (err)\n \t\t\treturn err;\n \t}\n+\tif (author_count < 1)\n+\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n+\telse if (author_count > 1)\n+\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n+\tif (err)\n+\t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\ndiff --git a/fsck.h b/fsck.h\nindex cab9c65..dded84b 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -8,8 +8,7 @@\n struct fsck_options;\n \n void fsck_set_msg_type(struct fsck_options *options,\n-\t\tconst char *msg_id, int msg_id_len,\n-\t\tconst char *msg_type, int msg_type_len);\n+\t\tconst char *msg_id, const char *msg_type);\n void fsck_set_msg_types(struct fsck_options *options, const char *values);\n int is_valid_msg_type(const char *msg_id, const char *msg_type);\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 2863a8a..956673b 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: badtagname: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: missingtaggerentry: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: badTagName: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missingTaggerEntry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n@@ -295,7 +295,7 @@ test_expect_success 'force fsck to ignore double author' '\n \tgit update-ref refs/heads/bogus \"$new\" &&\n \ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n \ttest_must_fail git fsck &&\n-\tgit -c fsck.multipleauthors=ignore fsck\n+\tgit -c fsck.multipleAuthors=ignore fsck\n '\n \n _bz='\\0'\n@@ -431,11 +431,11 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n \ttest_must_fail git -C missing fsck\n '\n \n-test_expect_success 'fsck --quick' '\n-\trm -rf quick &&\n-\tgit init quick &&\n+test_expect_success 'fsck --connectivity-only' '\n+\trm -rf connectivity-only &&\n+\tgit init connectivity-only &&\n \t(\n-\t\tcd quick &&\n+\t\tcd connectivity-only &&\n \t\ttouch empty &&\n \t\tgit add empty &&\n \t\ttest_commit empty &&\n@@ -443,13 +443,13 @@ test_expect_success 'fsck --quick' '\n \t\trm -f $empty &&\n \t\techo invalid >$empty &&\n \t\ttest_must_fail git fsck --strict &&\n-\t\tgit fsck --strict --quick &&\n+\t\tgit fsck --strict --connectivity-only &&\n \t\ttree=$(git rev-parse HEAD:) &&\n \t\tsuffix=${tree#??} &&\n \t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n \t\trm -f $tree &&\n \t\techo invalid >$tree &&\n-\t\ttest_must_fail git fsck --strict --quick\n+\t\ttest_must_fail git fsck --strict --connectivity-only\n \t)\n '\n \ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 1ada54c..6a1f89e 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,19 +123,19 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n-test_expect_success 'push with receive.fsck.skiplist' '\n+test_expect_success 'push with receive.fsck.skipList' '\n \tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n \trm -rf dst &&\n \tgit init dst &&\n-\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\tgit --git-dir=dst/.git config receive.fsckObjects true &&\n \ttest_must_fail git push --porcelain dst bogus &&\n-\tgit --git-dir=dst/.git config receive.fsck.skiplist SKIP &&\n+\tgit --git-dir=dst/.git config receive.fsck.skipList SKIP &&\n \techo $commit >dst/.git/SKIP &&\n \tgit push --porcelain dst bogus\n '\n \n-test_expect_success 'push with receive.fsck.missingemail=warn' '\n+test_expect_success 'push with receive.fsck.missingEmail=warn' '\n \tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n \trm -rf dst &&\n@@ -143,20 +143,20 @@ test_expect_success 'push with receive.fsck.missingemail=warn' '\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n \ttest_must_fail git push --porcelain dst bogus &&\n \tgit --git-dir=dst/.git config \\\n-\t\treceive.fsck.missingemail warn &&\n+\t\treceive.fsck.missingEmail warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missingemail\" act &&\n+\tgrep \"missingEmail\" act &&\n \tgit --git-dir=dst/.git branch -D bogus &&\n \tgit  --git-dir=dst/.git config --add \\\n-\t\treceive.fsck.missingemail ignore &&\n+\t\treceive.fsck.missingEmail ignore &&\n \tgit  --git-dir=dst/.git config --add \\\n-\t\treceive.fsck.baddate warn &&\n+\t\treceive.fsck.badDate warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\ttest_must_fail grep \"missingemail\" act\n+\ttest_must_fail grep \"missingEmail\" act\n '\n \n test_expect_success \\\n-\t'receive.fsck.unterminatedheader=warn triggers error' '\n+\t'receive.fsck.unterminatedHeader=warn triggers error' '\n \trm -rf dst &&\n \tgit init dst &&\n \tgit --git-dir=dst/.git config receive.fsckobjects true &&\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264545","messageId":"81f4c085129282088e4f9de12c62796989816c70.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 01/19] fsck: Introduce fsck options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:25:00Z","receivedAt":"2015-06-22T15:25:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Just like the diff machinery, we are about to introduce more settings,\ntherefore it makes sense to carry them around as a (pointer to a) struct\ncontaining all of them.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c           |  20 +++++--\n builtin/index-pack.c     |   9 +--\n builtin/unpack-objects.c |  11 ++--\n fsck.c                   | 150 +++++++++++++++++++++++------------------------\n fsck.h                   |  17 +++++-\n 5 files changed, 114 insertions(+), 93 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2679793..981dca5 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -25,6 +25,8 @@ static int include_reflogs = 1;\n static int check_full = 1;\n static int check_strict;\n static int keep_cache_objects;\n+static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n+static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;\n static struct object_id head_oid;\n static const char *head_points_at;\n static int errors_found;\n@@ -76,7 +78,7 @@ static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n \n static struct object_array pending;\n \n-static int mark_object(struct object *obj, int type, void *data)\n+static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct object *parent = data;\n \n@@ -119,7 +121,7 @@ static int mark_object(struct object *obj, int type, void *data)\n \n static void mark_object_reachable(struct object *obj)\n {\n-\tmark_object(obj, OBJ_ANY, NULL);\n+\tmark_object(obj, OBJ_ANY, NULL, NULL);\n }\n \n static int traverse_one_object(struct object *obj)\n@@ -132,7 +134,7 @@ static int traverse_one_object(struct object *obj)\n \t\tif (parse_tree(tree) < 0)\n \t\t\treturn 1; /* error already displayed */\n \t}\n-\tresult = fsck_walk(obj, mark_object, obj);\n+\tresult = fsck_walk(obj, obj, &fsck_walk_options);\n \tif (tree)\n \t\tfree_tree_buffer(tree);\n \treturn result;\n@@ -158,7 +160,7 @@ static int traverse_reachable(void)\n \treturn !!result;\n }\n \n-static int mark_used(struct object *obj, int type, void *data)\n+static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn 1;\n@@ -296,9 +298,9 @@ static int fsck_obj(struct object *obj)\n \t\tfprintf(stderr, \"Checking %s %s\\n\",\n \t\t\ttypename(obj->type), sha1_to_hex(obj->sha1));\n \n-\tif (fsck_walk(obj, mark_used, NULL))\n+\tif (fsck_walk(obj, NULL, &fsck_obj_options))\n \t\tobjerror(obj, \"broken links\");\n-\tif (fsck_object(obj, NULL, 0, check_strict, fsck_error_func))\n+\tif (fsck_object(obj, NULL, 0, &fsck_obj_options))\n \t\treturn -1;\n \n \tif (obj->type == OBJ_TREE) {\n@@ -638,6 +640,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);\n \n+\tfsck_walk_options.walk = mark_object;\n+\tfsck_obj_options.walk = mark_used;\n+\tfsck_obj_options.error_func = fsck_error_func;\n+\tif (check_strict)\n+\t\tfsck_obj_options.strict = 1;\n+\n \tif (show_progress == -1)\n \t\tshow_progress = isatty(2);\n \tif (verbose)\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 48fa472..f8b0c64 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -75,6 +75,7 @@ static int nr_threads;\n static int from_stdin;\n static int strict;\n static int do_fsck_object;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n static int verbose;\n static int show_stat;\n static int check_self_contained_and_connected;\n@@ -192,7 +193,7 @@ static void cleanup_thread(void)\n #endif\n \n \n-static int mark_link(struct object *obj, int type, void *data)\n+static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -838,10 +839,9 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,\n \t\t\tif (!obj)\n \t\t\t\tdie(_(\"invalid %s\"), typename(type));\n \t\t\tif (do_fsck_object &&\n-\t\t\t    fsck_object(obj, buf, size, 1,\n-\t\t\t\t    fsck_error_function))\n+\t\t\t    fsck_object(obj, buf, size, &fsck_options))\n \t\t\t\tdie(_(\"Error in object\"));\n-\t\t\tif (fsck_walk(obj, mark_link, NULL))\n+\t\t\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\t\t\tdie(_(\"Not all child objects of %s are reachable\"), sha1_to_hex(obj->sha1));\n \n \t\t\tif (obj->type == OBJ_TREE) {\n@@ -1615,6 +1615,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\tusage(index_pack_usage);\n \n \tcheck_replace_refs = 0;\n+\tfsck_options.walk = mark_link;\n \n \treset_pack_idx_option(&opts);\n \tgit_config(git_index_pack_config, &opts);\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex ac66672..6d17040 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -20,6 +20,7 @@ static unsigned char buffer[4096];\n static unsigned int offset, len;\n static off_t consumed_bytes;\n static git_SHA_CTX ctx;\n+static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;\n \n /*\n  * When running under --strict mode, objects whose reachability are\n@@ -178,7 +179,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)\n  * that have reachability requirements and calls this function.\n  * Verify its reachability and validity recursively and write it out.\n  */\n-static int check_object(struct object *obj, int type, void *data)\n+static int check_object(struct object *obj, int type, void *data, struct fsck_options *options)\n {\n \tstruct obj_buffer *obj_buf;\n \n@@ -203,10 +204,10 @@ static int check_object(struct object *obj, int type, void *data)\n \tobj_buf = lookup_object_buffer(obj);\n \tif (!obj_buf)\n \t\tdie(\"Whoops! Cannot find object '%s'\", sha1_to_hex(obj->sha1));\n-\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1,\n-\t\t\tfsck_error_function))\n+\tif (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))\n \t\tdie(\"Error in object\");\n-\tif (fsck_walk(obj, check_object, NULL))\n+\tfsck_options.walk = check_object;\n+\tif (fsck_walk(obj, NULL, &fsck_options))\n \t\tdie(\"Error on reachable objects of %s\", sha1_to_hex(obj->sha1));\n \twrite_cached_object(obj, obj_buf);\n \treturn 0;\n@@ -217,7 +218,7 @@ static void write_rest(void)\n \tunsigned i;\n \tfor (i = 0; i < nr_objects; i++) {\n \t\tif (obj_list[i].obj)\n-\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL);\n+\t\t\tcheck_object(obj_list[i].obj, OBJ_ANY, NULL, NULL);\n \t}\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex 10bcb65..d83b811 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,7 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n-static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\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@@ -25,9 +25,9 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \t\tif (S_ISGITLINK(entry.mode))\n \t\t\tcontinue;\n \t\tif (S_ISDIR(entry.mode))\n-\t\t\tresult = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data);\n+\t\t\tresult = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);\n \t\telse if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))\n-\t\t\tresult = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);\n+\t\t\tresult = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);\n \t\telse {\n \t\t\tresult = error(\"in tree %s: entry %s has bad mode %.6o\",\n \t\t\t\t\tsha1_to_hex(tree->object.sha1), entry.path, entry.mode);\n@@ -40,7 +40,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)\n \treturn res;\n }\n \n-static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data)\n+static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)\n {\n \tstruct commit_list *parents;\n \tint res;\n@@ -49,14 +49,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \tif (parse_commit(commit))\n \t\treturn -1;\n \n-\tresult = walk((struct object *)commit->tree, OBJ_TREE, data);\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 \twhile (parents) {\n-\t\tresult = walk((struct object *)parents->item, OBJ_COMMIT, data);\n+\t\tresult = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);\n \t\tif (result < 0)\n \t\t\treturn result;\n \t\tif (!res)\n@@ -66,14 +66,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da\n \treturn res;\n }\n \n-static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data)\n+static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)\n {\n \tif (parse_tag(tag))\n \t\treturn -1;\n-\treturn walk(tag->tagged, OBJ_ANY, data);\n+\treturn options->walk(tag->tagged, OBJ_ANY, data, options);\n }\n \n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options)\n {\n \tif (!obj)\n \t\treturn -1;\n@@ -81,11 +81,11 @@ int fsck_walk(struct object *obj, fsck_walk_func walk, void *data)\n \tcase OBJ_BLOB:\n \t\treturn 0;\n \tcase OBJ_TREE:\n-\t\treturn fsck_walk_tree((struct tree *)obj, walk, data);\n+\t\treturn fsck_walk_tree((struct tree *)obj, data, options);\n \tcase OBJ_COMMIT:\n-\t\treturn fsck_walk_commit((struct commit *)obj, walk, data);\n+\t\treturn fsck_walk_commit((struct commit *)obj, data, options);\n \tcase OBJ_TAG:\n-\t\treturn fsck_walk_tag((struct tag *)obj, walk, data);\n+\t\treturn fsck_walk_tag((struct tag *)obj, data, options);\n \tdefault:\n \t\terror(\"Unknown object type for %s\", sha1_to_hex(obj->sha1));\n \t\treturn -1;\n@@ -138,7 +138,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con\n \treturn c1 < c2 ? 0 : TREE_UNORDERED;\n }\n \n-static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n+static int fsck_tree(struct tree *item, struct fsck_options *options)\n {\n \tint retval;\n \tint has_null_sha1 = 0;\n@@ -194,7 +194,7 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \t\t * bits..\n \t\t */\n \t\tcase S_IFREG | 0664:\n-\t\t\tif (!strict)\n+\t\t\tif (!options->strict)\n \t\t\t\tbreak;\n \t\tdefault:\n \t\t\thas_bad_modes = 1;\n@@ -219,30 +219,30 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n \treturn retval;\n }\n \n static int require_end_of_header(const void *data, unsigned long size,\n-\tstruct object *obj, fsck_error error_func)\n+\tstruct object *obj, struct fsck_options *options)\n {\n \tconst char *buffer = (const char *)data;\n \tunsigned long i;\n@@ -250,7 +250,7 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn error_func(obj, FSCK_ERROR,\n+\t\t\treturn options->error_func(obj, FSCK_ERROR,\n \t\t\t\t\"unterminated header: NUL at offset %d\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n@@ -258,36 +258,36 @@ static int require_end_of_header(const void *data, unsigned long size,\n \t\t}\n \t}\n \n-\treturn error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n }\n \n-static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)\n+static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,30 +295,30 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n \n static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n \tunsigned parent_count, parent_line_count = 0;\n \tint err;\n \n-\tif (require_end_of_header(buffer, size, &commit->object, error_func))\n+\tif (require_end_of_header(buffer, size, &commit->object, options))\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,39 +328,39 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n-\terr = fsck_ident(&buffer, &commit->object, error_func);\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n \n static int fsck_commit(struct commit *commit, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tconst char *buffer = data ?  data : get_commit_buffer(commit, &size);\n-\tint ret = fsck_commit_buffer(commit, buffer, size, error_func);\n+\tint ret = fsck_commit_buffer(commit, buffer, size, options);\n \tif (!data)\n \t\tunuse_commit_buffer(commit, buffer);\n \treturn ret;\n }\n \n static int fsck_tag_buffer(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tunsigned char sha1[20];\n \tint ret = 0;\n@@ -376,65 +376,65 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n \t\t}\n \t}\n \n-\tif (require_end_of_header(buffer, size, &tag->object, error_func))\n+\tif (require_end_of_header(buffer, size, &tag->object, options))\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\terror_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n \telse\n-\t\tret = fsck_ident(&buffer, &tag->object, error_func);\n+\t\tret = fsck_ident(&buffer, &tag->object, options);\n \n done:\n \tstrbuf_release(&sb);\n@@ -443,34 +443,34 @@ done:\n }\n \n static int fsck_tag(struct tag *tag, const char *data,\n-\tunsigned long size, fsck_error error_func)\n+\tunsigned long size, struct fsck_options *options)\n {\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n \n-\treturn fsck_tag_buffer(tag, data, size, error_func);\n+\treturn fsck_tag_buffer(tag, data, size, options);\n }\n \n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func)\n+\tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n \tif (obj->type == OBJ_TREE)\n-\t\treturn fsck_tree((struct tree *) obj, strict, error_func);\n+\t\treturn fsck_tree((struct tree *) obj, options);\n \tif (obj->type == OBJ_COMMIT)\n \t\treturn fsck_commit((struct commit *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \tif (obj->type == OBJ_TAG)\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n-\t\t\tsize, error_func);\n+\t\t\tsize, options);\n \n-\treturn error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \ndiff --git a/fsck.h b/fsck.h\nindex d1e6387..07d0ab2 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -4,6 +4,8 @@\n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n \n+struct fsck_options;\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -12,7 +14,7 @@\n  *     <0\terror signaled and abort\n  *     >0\terror signaled and do not abort\n  */\n-typedef int (*fsck_walk_func)(struct object *obj, int type, void *data);\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 *err, ...);\n@@ -20,6 +22,15 @@ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...);\n __attribute__((format (printf, 3, 4)))\n int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n \n+struct fsck_options {\n+\tfsck_walk_func walk;\n+\tfsck_error error_func;\n+\tunsigned strict:1;\n+};\n+\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+\n /* descend in all linked child objects\n  * the return value is:\n  *    -1\terror in processing the object\n@@ -27,9 +38,9 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n  *    >0\treturn value of the first signaled error >0 (in the case of no other errors)\n  *    0\t\teverything OK\n  */\n-int fsck_walk(struct object *obj, fsck_walk_func walk, void *data);\n+int fsck_walk(struct object *obj, void *data, struct fsck_options *options);\n /* If NULL is passed for data, we assume the object is local and read it. */\n int fsck_object(struct object *obj, void *data, unsigned long size,\n-\tint strict, fsck_error error_func);\n+\tstruct fsck_options *options);\n \n #endif\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264546","messageId":"6bf5621b72ecb3aaf3e069efc8c7db6fbc22ada4.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 02/19] fsck: Introduce identifiers for fsck messages","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:25:09Z","receivedAt":"2015-06-22T15:25:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Instead of specifying whether a message by the fsck machinery constitutes\nan error or a warning, let's specify an identifier relating to the\nconcrete problem that was encountered. This is necessary for upcoming\nsupport to be able to demote certain errors to warnings.\n\nIn the process, simplify the requirements on the calling code: instead of\nhaving to handle full-blown varargs in every callback, we now send a\nstring buffer ready to be used by the callback.\n\nWe could use a simple enum for the message IDs here, but we want to\nguarantee that the enum values are associated with the appropriate\nmessage types (i.e. error or warning?). Besides, we want to introduce a\nparser in the next commit that maps the string representation to the\nenum value, hence we use the slightly ugly preprocessor construct that\nis extensible for use with said parser.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/fsck.c |  26 +++-----\n fsck.c         | 201 +++++++++++++++++++++++++++++++++++++++++----------------\n fsck.h         |   5 +-\n 3 files changed, 154 insertions(+), 78 deletions(-)\n\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 981dca5..fff38fe 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,33 +46,23 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n-static void objreport(struct object *obj, const char *severity,\n-                      const char *err, va_list params)\n+static void objreport(struct object *obj, const char *msg_type,\n+\t\t\tconst char *err)\n {\n-\tfprintf(stderr, \"%s in %s %s: \",\n-\t        severity, typename(obj->type), sha1_to_hex(obj->sha1));\n-\tvfprintf(stderr, err, params);\n-\tfputs(\"\\n\", stderr);\n+\tfprintf(stderr, \"%s in %s %s: %s\\n\",\n+\t\tmsg_type, typename(obj->type), sha1_to_hex(obj->sha1), err);\n }\n \n-__attribute__((format (printf, 2, 3)))\n-static int objerror(struct object *obj, const char *err, ...)\n+static int objerror(struct object *obj, const char *err)\n {\n-\tva_list params;\n-\tva_start(params, err);\n \terrors_found |= ERROR_OBJECT;\n-\tobjreport(obj, \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, \"error\", err);\n \treturn -1;\n }\n \n-__attribute__((format (printf, 3, 4)))\n-static int fsck_error_func(struct object *obj, int type, const char *err, ...)\n+static int fsck_error_func(struct object *obj, int type, const char *message)\n {\n-\tva_list params;\n-\tva_start(params, err);\n-\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", err, params);\n-\tva_end(params);\n+\tobjreport(obj, (type == FSCK_WARN) ? \"warning\" : \"error\", message);\n \treturn (type == FSCK_WARN) ? 0 : 1;\n }\n \ndiff --git a/fsck.c b/fsck.c\nindex d83b811..ab24618 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,6 +9,98 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FOREACH_MSG_ID(FUNC) \\\n+\t/* errors */ \\\n+\tFUNC(BAD_DATE, ERROR) \\\n+\tFUNC(BAD_DATE_OVERFLOW, ERROR) \\\n+\tFUNC(BAD_EMAIL, ERROR) \\\n+\tFUNC(BAD_NAME, ERROR) \\\n+\tFUNC(BAD_OBJECT_SHA1, ERROR) \\\n+\tFUNC(BAD_PARENT_SHA1, ERROR) \\\n+\tFUNC(BAD_TAG_OBJECT, ERROR) \\\n+\tFUNC(BAD_TIMEZONE, ERROR) \\\n+\tFUNC(BAD_TREE, ERROR) \\\n+\tFUNC(BAD_TREE_SHA1, ERROR) \\\n+\tFUNC(BAD_TYPE, ERROR) \\\n+\tFUNC(DUPLICATE_ENTRIES, ERROR) \\\n+\tFUNC(MISSING_AUTHOR, ERROR) \\\n+\tFUNC(MISSING_COMMITTER, ERROR) \\\n+\tFUNC(MISSING_EMAIL, ERROR) \\\n+\tFUNC(MISSING_GRAFT, ERROR) \\\n+\tFUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_OBJECT, ERROR) \\\n+\tFUNC(MISSING_PARENT, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \\\n+\tFUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \\\n+\tFUNC(MISSING_TAG, ERROR) \\\n+\tFUNC(MISSING_TAG_ENTRY, ERROR) \\\n+\tFUNC(MISSING_TAG_OBJECT, ERROR) \\\n+\tFUNC(MISSING_TREE, ERROR) \\\n+\tFUNC(MISSING_TYPE, ERROR) \\\n+\tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(NUL_IN_HEADER, ERROR) \\\n+\tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n+\tFUNC(TREE_NOT_SORTED, ERROR) \\\n+\tFUNC(UNKNOWN_TYPE, ERROR) \\\n+\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n+\tFUNC(ZERO_PADDED_DATE, ERROR) \\\n+\t/* warnings */ \\\n+\tFUNC(BAD_FILEMODE, WARN) \\\n+\tFUNC(BAD_TAG_NAME, WARN) \\\n+\tFUNC(EMPTY_NAME, WARN) \\\n+\tFUNC(FULL_PATHNAME, WARN) \\\n+\tFUNC(HAS_DOT, WARN) \\\n+\tFUNC(HAS_DOTDOT, WARN) \\\n+\tFUNC(HAS_DOTGIT, WARN) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n+\tFUNC(NULL_SHA1, WARN) \\\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\n+#define MSG_ID(id, msg_type) FSCK_MSG_##id,\n+enum fsck_msg_id {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\tFSCK_MSG_MAX\n+};\n+#undef MSG_ID\n+\n+#define MSG_ID(id, msg_type) { FSCK_##msg_type },\n+static struct {\n+\tint msg_type;\n+} msg_id_info[FSCK_MSG_MAX + 1] = {\n+\tFOREACH_MSG_ID(MSG_ID)\n+\t{ -1 }\n+};\n+#undef MSG_ID\n+\n+static int fsck_msg_type(enum fsck_msg_id msg_id,\n+\tstruct fsck_options *options)\n+{\n+\tint msg_type;\n+\n+\tmsg_type = msg_id_info[msg_id].msg_type;\n+\tif (options->strict && msg_type == FSCK_WARN)\n+\t\tmsg_type = FSCK_ERROR;\n+\n+\treturn msg_type;\n+}\n+\n+__attribute__((format (printf, 4, 5)))\n+static int report(struct fsck_options *options, struct object *object,\n+\tenum fsck_msg_id id, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint msg_type = fsck_msg_type(id, options), result;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(&sb, fmt, ap);\n+\tresult = options->error_func(object, msg_type, sb.buf);\n+\tstrbuf_release(&sb);\n+\tva_end(ap);\n+\n+\treturn result;\n+}\n+\n static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)\n {\n \tstruct tree_desc desc;\n@@ -219,25 +311,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)\n \n \tretval = 0;\n \tif (has_null_sha1)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains entries pointing to null sha1\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_NULL_SHA1, \"contains entries pointing to null sha1\");\n \tif (has_full_path)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains full pathnames\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, \"contains full pathnames\");\n \tif (has_empty_name)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains empty pathname\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, \"contains empty pathname\");\n \tif (has_dot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOT, \"contains '.'\");\n \tif (has_dotdot)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '..'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, \"contains '..'\");\n \tif (has_dotgit)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains '.git'\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, \"contains '.git'\");\n \tif (has_zero_pad)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains zero-padded file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, \"contains zero-padded file modes\");\n \tif (has_bad_modes)\n-\t\tretval += options->error_func(&item->object, FSCK_WARN, \"contains bad file modes\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, \"contains bad file modes\");\n \tif (has_dup_entries)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"contains duplicate file entries\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, \"contains duplicate file entries\");\n \tif (not_properly_sorted)\n-\t\tretval += options->error_func(&item->object, FSCK_ERROR, \"not properly sorted\");\n+\t\tretval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, \"not properly sorted\");\n \treturn retval;\n }\n \n@@ -250,15 +342,17 @@ static int require_end_of_header(const void *data, unsigned long size,\n \tfor (i = 0; i < size; i++) {\n \t\tswitch (buffer[i]) {\n \t\tcase '\\0':\n-\t\t\treturn options->error_func(obj, FSCK_ERROR,\n-\t\t\t\t\"unterminated header: NUL at offset %d\", i);\n+\t\t\treturn report(options, obj,\n+\t\t\t\tFSCK_MSG_NUL_IN_HEADER,\n+\t\t\t\t\"unterminated header: NUL at offset %ld\", i);\n \t\tcase '\\n':\n \t\t\tif (i + 1 < size && buffer[i + 1] == '\\n')\n \t\t\t\treturn 0;\n \t\t}\n \t}\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unterminated header\");\n+\treturn report(options, obj,\n+\t\tFSCK_MSG_UNTERMINATED_HEADER, \"unterminated header\");\n }\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n@@ -266,28 +360,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \tchar *end;\n \n \tif (**ident == '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident == '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad name\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n \tif (**ident != '<')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n \tif ((*ident)[-1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before email\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n \t(*ident)++;\n \t*ident += strcspn(*ident, \"<>\\n\");\n \tif (**ident != '>')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad email\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n \t(*ident)++;\n \tif (**ident != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - missing space before date\");\n+\t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n \t(*ident)++;\n \tif (**ident == '0' && (*ident)[1] != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - zero-padded date\");\n+\t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n \tif (date_overflows(strtoul(*ident, &end, 10)))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - date causes integer overflow\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n \tif (end == *ident || *end != ' ')\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad date\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n \t*ident = end + 1;\n \tif ((**ident != '+' && **ident != '-') ||\n \t    !isdigit((*ident)[1]) ||\n@@ -295,7 +389,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option\n \t    !isdigit((*ident)[3]) ||\n \t    !isdigit((*ident)[4]) ||\n \t    ((*ident)[5] != '\\n'))\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"invalid author/committer line - bad time zone\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n \t(*ident) += 6;\n \treturn 0;\n }\n@@ -312,13 +406,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\treturn -1;\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'tree' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n \tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'tree' line format - bad sha1\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n \t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid 'parent' line format - bad sha1\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -328,23 +422,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n \t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"graft objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n \t} else {\n \t\tif (parent_count != parent_line_count)\n-\t\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"parent objects missing\");\n+\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'author' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"invalid format - expected 'committer' line\");\n+\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, \"invalid format - expected 'committer' line\");\n \terr = fsck_ident(&buffer, &commit->object, options);\n \tif (err)\n \t\treturn err;\n \tif (!commit->tree)\n-\t\treturn options->error_func(&commit->object, FSCK_ERROR, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n+\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE, \"could not load commit's tree %s\", sha1_to_hex(tree_sha1));\n \n \treturn 0;\n }\n@@ -376,11 +470,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tbuffer = to_free =\n \t\t\tread_sha1_file(tag->object.sha1, &type, &size);\n \t\tif (!buffer)\n-\t\t\treturn options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\treturn report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_MISSING_TAG_OBJECT,\n \t\t\t\t\"cannot read tag object\");\n \n \t\tif (type != OBJ_TAG) {\n-\t\t\tret = options->error_func(&tag->object, FSCK_ERROR,\n+\t\t\tret = report(options, &tag->object,\n+\t\t\t\tFSCK_MSG_TAG_OBJECT_NOT_TAG,\n \t\t\t\t\"expected tag got %s\",\n \t\t\t    typename(type));\n \t\t\tgoto done;\n@@ -391,48 +487,49 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \n \tif (!skip_prefix(buffer, \"object \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'object' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, \"invalid format - expected 'object' line\");\n \t\tgoto done;\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'object' line format - bad sha1\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n \t\tgoto done;\n \t}\n \tbuffer += 41;\n \n \tif (!skip_prefix(buffer, \"type \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, \"invalid format - expected 'type' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tif (type_from_string_gently(buffer, eol - buffer, 1) < 0)\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid 'type' value\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, \"invalid 'type' value\");\n \tif (ret)\n \t\tgoto done;\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tag \", &buffer)) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - expected 'tag' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, \"invalid format - expected 'tag' line\");\n \t\tgoto done;\n \t}\n \teol = strchr(buffer, '\\n');\n \tif (!eol) {\n-\t\tret = options->error_func(&tag->object, FSCK_ERROR, \"invalid format - unexpected end after 'type' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, \"invalid format - unexpected end after 'type' line\");\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n \tif (check_refname_format(sb.buf, 0))\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid 'tag' name: %.*s\",\n+\t\treport(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,\n+\t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n \tbuffer = eol + 1;\n \n \tif (!skip_prefix(buffer, \"tagger \", &buffer))\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\toptions->error_func(&tag->object, FSCK_WARN, \"invalid format - expected 'tagger' line\");\n+\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \n@@ -448,7 +545,7 @@ static int fsck_tag(struct tag *tag, const char *data,\n \tstruct object *tagged = tag->tagged;\n \n \tif (!tagged)\n-\t\treturn options->error_func(&tag->object, FSCK_ERROR, \"could not load tagged object\");\n+\t\treturn report(options, &tag->object, FSCK_MSG_BAD_TAG_OBJECT, \"could not load tagged object\");\n \n \treturn fsck_tag_buffer(tag, data, size, options);\n }\n@@ -457,7 +554,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \tstruct fsck_options *options)\n {\n \tif (!obj)\n-\t\treturn options->error_func(obj, FSCK_ERROR, \"no valid object to fsck\");\n+\t\treturn report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, \"no valid object to fsck\");\n \n \tif (obj->type == OBJ_BLOB)\n \t\treturn 0;\n@@ -470,22 +567,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \t\treturn fsck_tag((struct tag *) obj, (const char *) data,\n \t\t\tsize, options);\n \n-\treturn options->error_func(obj, FSCK_ERROR, \"unknown type '%d' (internal fsck error)\",\n+\treturn report(options, obj, FSCK_MSG_UNKNOWN_TYPE, \"unknown type '%d' (internal fsck error)\",\n \t\t\t  obj->type);\n }\n \n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...)\n+int fsck_error_function(struct object *obj, int msg_type, const char *message)\n {\n-\tva_list ap;\n-\tstruct strbuf sb = STRBUF_INIT;\n-\n-\tstrbuf_addf(&sb, \"object %s:\", sha1_to_hex(obj->sha1));\n-\n-\tva_start(ap, fmt);\n-\tstrbuf_vaddf(&sb, fmt, ap);\n-\tva_end(ap);\n-\n-\terror(\"%s\", sb.buf);\n-\tstrbuf_release(&sb);\n+\terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex 07d0ab2..f6f268a 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -17,10 +17,9 @@ struct fsck_options;\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 *err, ...);\n+typedef int (*fsck_error)(struct object *obj, int type, const char *message);\n \n-__attribute__((format (printf, 3, 4)))\n-int fsck_error_function(struct object *obj, int type, const char *fmt, ...);\n+int fsck_error_function(struct object *obj, int type, const char *message);\n \n struct fsck_options {\n \tfsck_walk_func walk;\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264547","messageId":"bd90483f53f8158cf9e76cea85db407156b8f885.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 03/19] fsck: Provide a function to parse fsck message IDs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:25:14Z","receivedAt":"2015-06-22T15:25:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"These functions will be used in the next commits to allow the user to\nask fsck to handle specific problems differently, e.g. demoting certain\nerrors to warnings. The upcoming `fsck_set_msg_types()` function has to\nhandle partial strings because we would like to be able to parse, say,\n'missingemail=warn,missingtaggerentry=warn' command line parameters\n(which will be passed by receive-pack to index-pack and unpack-objects).\n\nTo make the parsing robust, we generate strings from the enum keys, and\nusing these keys, we match up strings without dashes case-insensitively\nto the corresponding enum values.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 36 ++++++++++++++++++++++++++++++++++--\n 1 file changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex ab24618..1a3f7ce 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -63,15 +63,47 @@ enum fsck_msg_id {\n };\n #undef MSG_ID\n \n-#define MSG_ID(id, msg_type) { FSCK_##msg_type },\n+#define STR(x) #x\n+#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n static struct {\n+\tconst char *id_string;\n \tint msg_type;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n-\t{ -1 }\n+\t{ NULL, -1 }\n };\n #undef MSG_ID\n \n+static int parse_msg_id(const char *text)\n+{\n+\tstatic char **lowercased;\n+\tint i;\n+\n+\tif (!lowercased) {\n+\t\t/* convert id_string to lower case, without underscores. */\n+\t\tlowercased = xmalloc(FSCK_MSG_MAX * sizeof(*lowercased));\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n+\t\t\tconst char *p = msg_id_info[i].id_string;\n+\t\t\tint len = strlen(p);\n+\t\t\tchar *q = xmalloc(len);\n+\n+\t\t\tlowercased[i] = q;\n+\t\t\twhile (*p)\n+\t\t\t\tif (*p == '_')\n+\t\t\t\t\tp++;\n+\t\t\t\telse\n+\t\t\t\t\t*(q)++ = tolower(*(p)++);\n+\t\t\t*q = '\\0';\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\tif (!strcmp(text, lowercased[i]))\n+\t\t\treturn i;\n+\n+\treturn -1;\n+}\n+\n static int fsck_msg_type(enum fsck_msg_id msg_id,\n \tstruct fsck_options *options)\n {\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264548","messageId":"08ab723892b4412431b88df8764f506f0abbb0a6.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:25:25Z","receivedAt":"2015-06-22T15:25:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"There are legacy repositories out there whose older commits and tags\nhave issues that prevent pushing them when 'receive.fsckObjects' is set.\nOne real-life example is a commit object that has been hand-crafted to\nlist two authors.\n\nOften, it is not possible to fix those issues without disrupting the\nwork with said repositories, yet it is still desirable to perform checks\nby setting `receive.fsckObjects = true`. This commit is the first step\nto allow demoting specific fsck issues to mere warnings.\n\nThe `fsck_set_msg_types()` function added by this commit parses a list\nof settings in the form:\n\n\tmissingemail=warn,badname=warn,...\n\nUnfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by\ngit fsck so far, but other call paths (e.g. git index-pack --strict)\nerror out *always* no matter what type was specified. Therefore, we need\nto take extra care to set all message types to FSCK_ERROR by default in\nthose cases.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------\n fsck.h |  9 +++++--\n 2 files changed, 85 insertions(+), 12 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 1a3f7ce..e81a342 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -64,30 +64,29 @@ enum fsck_msg_id {\n #undef MSG_ID\n \n #define STR(x) #x\n-#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n+#define MSG_ID(id, msg_type) { STR(id), NULL, FSCK_##msg_type },\n static struct {\n \tconst char *id_string;\n+\tconst char *lowercased;\n \tint msg_type;\n } msg_id_info[FSCK_MSG_MAX + 1] = {\n \tFOREACH_MSG_ID(MSG_ID)\n-\t{ NULL, -1 }\n+\t{ NULL, NULL, -1 }\n };\n #undef MSG_ID\n \n static int parse_msg_id(const char *text)\n {\n-\tstatic char **lowercased;\n \tint i;\n \n-\tif (!lowercased) {\n+\tif (!msg_id_info[0].lowercased) {\n \t\t/* convert id_string to lower case, without underscores. */\n-\t\tlowercased = xmalloc(FSCK_MSG_MAX * sizeof(*lowercased));\n \t\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n \t\t\tconst char *p = msg_id_info[i].id_string;\n \t\t\tint len = strlen(p);\n \t\t\tchar *q = xmalloc(len);\n \n-\t\t\tlowercased[i] = q;\n+\t\t\tmsg_id_info[i].lowercased = q;\n \t\t\twhile (*p)\n \t\t\t\tif (*p == '_')\n \t\t\t\t\tp++;\n@@ -98,7 +97,7 @@ static int parse_msg_id(const char *text)\n \t}\n \n \tfor (i = 0; i < FSCK_MSG_MAX; i++)\n-\t\tif (!strcmp(text, lowercased[i]))\n+\t\tif (!strcmp(text, msg_id_info[i].lowercased))\n \t\t\treturn i;\n \n \treturn -1;\n@@ -109,13 +108,78 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,\n {\n \tint msg_type;\n \n-\tmsg_type = msg_id_info[msg_id].msg_type;\n-\tif (options->strict && msg_type == FSCK_WARN)\n-\t\tmsg_type = FSCK_ERROR;\n+\tassert(msg_id >= 0 && msg_id < FSCK_MSG_MAX);\n+\n+\tif (options->msg_type)\n+\t\tmsg_type = options->msg_type[msg_id];\n+\telse {\n+\t\tmsg_type = msg_id_info[msg_id].msg_type;\n+\t\tif (options->strict && msg_type == FSCK_WARN)\n+\t\t\tmsg_type = FSCK_ERROR;\n+\t}\n \n \treturn msg_type;\n }\n \n+static int parse_msg_type(const char *str)\n+{\n+\tif (!strcmp(str, \"error\"))\n+\t\treturn FSCK_ERROR;\n+\telse if (!strcmp(str, \"warn\"))\n+\t\treturn FSCK_WARN;\n+\telse\n+\t\tdie(\"Unknown fsck message type: '%s'\", str);\n+}\n+\n+void fsck_set_msg_type(struct fsck_options *options,\n+\t\tconst char *msg_id, const char *msg_type)\n+{\n+\tint id = parse_msg_id(msg_id), type;\n+\n+\tif (id < 0)\n+\t\tdie(\"Unhandled message id: %s\", msg_id);\n+\ttype = parse_msg_type(msg_type);\n+\n+\tif (!options->msg_type) {\n+\t\tint i;\n+\t\tint *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n+\t\tfor (i = 0; i < FSCK_MSG_MAX; i++)\n+\t\t\tmsg_type[i] = fsck_msg_type(i, options);\n+\t\toptions->msg_type = msg_type;\n+\t}\n+\n+\toptions->msg_type[id] = type;\n+}\n+\n+void fsck_set_msg_types(struct fsck_options *options, const char *values)\n+{\n+\tchar *buf = xstrdup(values), *to_free = buf;\n+\tint done = 0;\n+\n+\twhile (!done) {\n+\t\tint len = strcspn(buf, \" ,|\"), equal;\n+\n+\t\tdone = !buf[len];\n+\t\tif (!len) {\n+\t\t\tbuf++;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tbuf[len] = '\\0';\n+\n+\t\tfor (equal = 0; equal < len &&\n+\t\t\t\tbuf[equal] != '=' && buf[equal] != ':'; equal++)\n+\t\t\tbuf[equal] = tolower(buf[equal]);\n+\t\tbuf[equal] = '\\0';\n+\n+\t\tif (equal == len)\n+\t\t\tdie(\"Missing '=': '%s'\", buf);\n+\n+\t\tfsck_set_msg_type(options, buf, buf + equal + 1);\n+\t\tbuf += len + 1;\n+\t}\n+\tfree(to_free);\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -605,6 +669,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size,\n \n int fsck_error_function(struct object *obj, int msg_type, const char *message)\n {\n+\tif (msg_type == FSCK_WARN) {\n+\t\twarning(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n+\t\treturn 0;\n+\t}\n \terror(\"object %s: %s\", sha1_to_hex(obj->sha1), message);\n \treturn 1;\n }\ndiff --git a/fsck.h b/fsck.h\nindex f6f268a..af3c84e 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -6,6 +6,10 @@\n \n struct fsck_options;\n \n+void fsck_set_msg_type(struct fsck_options *options,\n+\t\tconst char *msg_id, const char *msg_type);\n+void fsck_set_msg_types(struct fsck_options *options, const char *values);\n+\n /*\n  * callback function for fsck_walk\n  * type is the expected type of the object or OBJ_ANY\n@@ -25,10 +29,11 @@ struct fsck_options {\n \tfsck_walk_func walk;\n \tfsck_error error_func;\n \tunsigned strict:1;\n+\tint *msg_type;\n };\n \n-#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 }\n-#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 }\n+#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\n+#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL }\n \n /* descend in all linked child objects\n  * the return value is:\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264549","messageId":"639965f17eef7a0fc05af7ee4e2b064a8d1737ce.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 05/19] fsck (receive-pack): Allow demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:25:31Z","receivedAt":"2015-06-22T15:25:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For example, missing emails in commit and tag objects can be demoted to\nmere warnings with\n\n\tgit config receive.fsck.missingemail=warn\n\nThe value is actually a comma-separated list.\n\nIn case that the same key is listed in multiple receive.fsck.<msg-id>\nlines in the config, the latter configuration wins (this can happen for\nexample when both $HOME/.gitconfig and .git/config contain message type\nsettings).\n\nAs git receive-pack does not actually perform the checks, it hands off\nthe setting to index-pack or unpack-objects in the form of an optional\nargument to the --strict option.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n builtin/index-pack.c     |  4 ++++\n builtin/receive-pack.c   | 17 +++++++++++++++--\n builtin/unpack-objects.c |  5 +++++\n fsck.c                   |  8 ++++++++\n fsck.h                   |  1 +\n 5 files changed, 33 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex f8b0c64..f0d283b 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1633,6 +1633,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \t\t\t} else if (!strcmp(arg, \"--strict\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tdo_fsck_object = 1;\n+\t\t\t} else if (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tdo_fsck_object = 1;\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n \t\t\t} else if (!strcmp(arg, \"--check-self-contained-and-connected\")) {\n \t\t\t\tstrict = 1;\n \t\t\t\tcheck_self_contained_and_connected = 1;\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 94d0571..3afe8f8 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -19,6 +19,7 @@\n #include \"tag.h\"\n #include \"gpg-interface.h\"\n #include \"sigchain.h\"\n+#include \"fsck.h\"\n \n static const char receive_pack_usage[] = \"git receive-pack <git-dir>\";\n \n@@ -36,6 +37,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED;\n static enum deny_action deny_delete_current = DENY_UNCONFIGURED;\n static int receive_fsck_objects = -1;\n static int transfer_fsck_objects = -1;\n+static struct strbuf fsck_msg_types = STRBUF_INIT;\n static int receive_unpack_limit = -1;\n static int transfer_unpack_limit = -1;\n static int advertise_atomic_push = 1;\n@@ -115,6 +117,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n+\t\tif (is_valid_msg_type(var, value))\n+\t\t\tstrbuf_addf(&fsck_msg_types, \"%c%s=%s\",\n+\t\t\t\tfsck_msg_types.len ? ',' : '=', var, value);\n+\t\telse\n+\t\t\twarning(\"Skipping unknown msg id '%s'\", var);\n+\t\treturn 0;\n+\t}\n+\n \tif (strcmp(var, \"receive.fsckobjects\") == 0) {\n \t\treceive_fsck_objects = git_config_bool(var, value);\n \t\treturn 0;\n@@ -1490,7 +1501,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\tif (quiet)\n \t\t\targv_array_push(&child.args, \"-q\");\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n+\t\t\t\tfsck_msg_types.buf);\n \t\tchild.no_stdout = 1;\n \t\tchild.err = err_fd;\n \t\tchild.git_cmd = 1;\n@@ -1508,7 +1520,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)\n \t\targv_array_pushl(&child.args, \"index-pack\",\n \t\t\t\t \"--stdin\", hdr_arg, keep_arg, NULL);\n \t\tif (fsck_objects)\n-\t\t\targv_array_push(&child.args, \"--strict\");\n+\t\t\targv_array_pushf(&child.args, \"--strict%s\",\n+\t\t\t\tfsck_msg_types.buf);\n \t\tif (fix_thin)\n \t\t\targv_array_push(&child.args, \"--fix-thin\");\n \t\tchild.out = -1;\ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 6d17040..7cc086f 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrict = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (skip_prefix(arg, \"--strict=\", &arg)) {\n+\t\t\t\tstrict = 1;\n+\t\t\t\tfsck_set_msg_types(&fsck_options, arg);\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (starts_with(arg, \"--pack_header=\")) {\n \t\t\t\tstruct pack_header *hdr;\n \t\t\t\tchar *c;\ndiff --git a/fsck.c b/fsck.c\nindex e81a342..02af3ed 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -131,6 +131,14 @@ static int parse_msg_type(const char *str)\n \t\tdie(\"Unknown fsck message type: '%s'\", str);\n }\n \n+int is_valid_msg_type(const char *msg_id, const char *msg_type)\n+{\n+\tif (parse_msg_id(msg_id) < 0)\n+\t\treturn 0;\n+\tparse_msg_type(msg_type);\n+\treturn 1;\n+}\n+\n void fsck_set_msg_type(struct fsck_options *options,\n \t\tconst char *msg_id, const char *msg_type)\n {\ndiff --git a/fsck.h b/fsck.h\nindex af3c84e..3ef92a3 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -9,6 +9,7 @@ struct fsck_options;\n void fsck_set_msg_type(struct fsck_options *options,\n \t\tconst char *msg_id, const char *msg_type);\n void fsck_set_msg_types(struct fsck_options *options, const char *values);\n+int is_valid_msg_type(const char *msg_id, const char *msg_type);\n \n /*\n  * callback function for fsck_walk\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264552","messageId":"ba9759ec82c03e2d60e1e71f1d6f8033d3c17c03.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 06/19] fsck: Report the ID of the error/warning","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:25:52Z","receivedAt":"2015-06-22T15:25:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some legacy code has objects with non-fatal fsck issues; To enable the\nuser to ignore those issues, let's print out the ID (e.g. when\nencountering \"missingEmail\", the user might want to call `git config\n--add receive.fsck.missingEmail=warn`).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c          | 20 ++++++++++++++++++++\n t/t1450-fsck.sh |  4 ++--\n 2 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 02af3ed..0ec02b2 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -188,6 +188,24 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n \tfree(to_free);\n }\n \n+static void append_msg_id(struct strbuf *sb, const char *msg_id)\n+{\n+\tfor (;;) {\n+\t\tchar c = *(msg_id)++;\n+\n+\t\tif (!c)\n+\t\t\tbreak;\n+\t\tif (c != '_')\n+\t\t\tstrbuf_addch(sb, tolower(c));\n+\t\telse {\n+\t\t\tassert(*msg_id);\n+\t\t\tstrbuf_addch(sb, *(msg_id)++);\n+\t\t}\n+\t}\n+\n+\tstrbuf_addstr(sb, \": \");\n+}\n+\n __attribute__((format (printf, 4, 5)))\n static int report(struct fsck_options *options, struct object *object,\n \tenum fsck_msg_id id, const char *fmt, ...)\n@@ -196,6 +214,8 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tappend_msg_id(&sb, msg_id_info[id].id_string);\n+\n \tva_start(ap, fmt);\n \tstrbuf_vaddf(&sb, fmt, ap);\n \tresult = options->error_func(object, msg_type, sb.buf);\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex cfb32b6..7c5b3d5 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '\n \tgit fsck --tags 2>out &&\n \n \tcat >expect <<-EOF &&\n-\twarning in tag $tag: invalid '\\''tag'\\'' name: wrong name format\n-\twarning in tag $tag: invalid format - expected '\\''tagger'\\'' line\n+\twarning in tag $tag: badTagName: invalid '\\''tag'\\'' name: wrong name format\n+\twarning in tag $tag: missingTaggerEntry: invalid format - expected '\\''tagger'\\'' line\n \tEOF\n \ttest_cmp expect out\n '\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264550","messageId":"f3733d12589870e1fc9a8f3fad7cb85d9a901cd1.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 07/19] fsck: Make fsck_ident() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:26:03Z","receivedAt":"2015-06-22T15:26:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_ident() identifies a problem with the ident, it should still\nadvance the pointer to the next line so that fsck can continue in the\ncase of a mere warning.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 49 +++++++++++++++++++++++++++----------------------\n 1 file changed, 27 insertions(+), 22 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 0ec02b2..d0a7282 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -481,40 +481,45 @@ static int require_end_of_header(const void *data, unsigned long size,\n \n static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)\n {\n+\tconst char *p = *ident;\n \tchar *end;\n \n-\tif (**ident == '<')\n+\t*ident = strchrnul(*ident, '\\n');\n+\tif (**ident == '\\n')\n+\t\t(*ident)++;\n+\n+\tif (*p == '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident == '>')\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p == '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_NAME, \"invalid author/committer line - bad name\");\n-\tif (**ident != '<')\n+\tif (*p != '<')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_EMAIL, \"invalid author/committer line - missing email\");\n-\tif ((*ident)[-1] != ' ')\n+\tif (p[-1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, \"invalid author/committer line - missing space before email\");\n-\t(*ident)++;\n-\t*ident += strcspn(*ident, \"<>\\n\");\n-\tif (**ident != '>')\n+\tp++;\n+\tp += strcspn(p, \"<>\\n\");\n+\tif (*p != '>')\n \t\treturn report(options, obj, FSCK_MSG_BAD_EMAIL, \"invalid author/committer line - bad email\");\n-\t(*ident)++;\n-\tif (**ident != ' ')\n+\tp++;\n+\tif (*p != ' ')\n \t\treturn report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, \"invalid author/committer line - missing space before date\");\n-\t(*ident)++;\n-\tif (**ident == '0' && (*ident)[1] != ' ')\n+\tp++;\n+\tif (*p == '0' && p[1] != ' ')\n \t\treturn report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, \"invalid author/committer line - zero-padded date\");\n-\tif (date_overflows(strtoul(*ident, &end, 10)))\n+\tif (date_overflows(strtoul(p, &end, 10)))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, \"invalid author/committer line - date causes integer overflow\");\n-\tif (end == *ident || *end != ' ')\n+\tif ((end == p || *end != ' '))\n \t\treturn report(options, obj, FSCK_MSG_BAD_DATE, \"invalid author/committer line - bad date\");\n-\t*ident = end + 1;\n-\tif ((**ident != '+' && **ident != '-') ||\n-\t    !isdigit((*ident)[1]) ||\n-\t    !isdigit((*ident)[2]) ||\n-\t    !isdigit((*ident)[3]) ||\n-\t    !isdigit((*ident)[4]) ||\n-\t    ((*ident)[5] != '\\n'))\n+\tp = end + 1;\n+\tif ((*p != '+' && *p != '-') ||\n+\t    !isdigit(p[1]) ||\n+\t    !isdigit(p[2]) ||\n+\t    !isdigit(p[3]) ||\n+\t    !isdigit(p[4]) ||\n+\t    (p[5] != '\\n'))\n \t\treturn report(options, obj, FSCK_MSG_BAD_TIMEZONE, \"invalid author/committer line - bad time zone\");\n-\t(*ident) += 6;\n+\tp += 6;\n \treturn 0;\n }\n \n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264551","messageId":"4fb19d2bf037c528de9c98068d083d49575876e5.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 08/19] fsck: Make fsck_commit() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:26:11Z","receivedAt":"2015-06-22T15:26:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_commit() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nNote that some problems are too problematic to simply ignore. For\nexample, when the header lines are mixed up, we punt after encountering\nan incorrect line. Therefore, demoting certain warnings to errors can\nhide other problems. Example: demoting the missingauthor error to\na warning would hide a problematic committer line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 28 ++++++++++++++++++++--------\n 1 file changed, 20 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex d0a7282..ef3bf68 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -536,12 +536,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \n \tif (!skip_prefix(buffer, \"tree \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_TREE, \"invalid format - expected 'tree' line\");\n-\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n')\n-\t\treturn report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\tif (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\\n') {\n+\t\terr = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, \"invalid 'tree' line format - bad sha1\");\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n \tbuffer += 41;\n \twhile (skip_prefix(buffer, \"parent \", &buffer)) {\n-\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, \"invalid 'parent' line format - bad sha1\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t\tbuffer += 41;\n \t\tparent_line_count++;\n \t}\n@@ -550,11 +556,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \tif (graft) {\n \t\tif (graft->nr_parent == -1 && !parent_count)\n \t\t\t; /* shallow commit */\n-\t\telse if (graft->nr_parent != parent_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\telse if (graft->nr_parent != parent_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, \"graft objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t} else {\n-\t\tif (parent_count != parent_line_count)\n-\t\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\tif (parent_count != parent_line_count) {\n+\t\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, \"parent objects missing\");\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n \t}\n \tif (!skip_prefix(buffer, \"author \", &buffer))\n \t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264553","messageId":"e82ab8cfa698c4a5b5d0be5df2ae59760b9f046d.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 09/19] fsck: Handle multiple authors in commits specially","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:26:23Z","receivedAt":"2015-06-22T15:26:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This problem has been detected in the wild, and is the primary reason\nto introduce an option to demote certain fsck errors to warnings. Let's\noffer to ignore this particular problem specifically.\n\nTechnically, we could handle such repositories by setting\nreceive.fsck.<msg-id> to missingCommitter=warn, but that could hide\nmissing tree objects in the same commit because we cannot continue\nverifying any commit object after encountering a missing committer line,\nwhile we can continue in the case of multiple author lines.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 17 +++++++++++++----\n 1 file changed, 13 insertions(+), 4 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex ef3bf68..daa07ad 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -38,6 +38,7 @@\n \tFUNC(MISSING_TREE, ERROR) \\\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n+\tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n \tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n \tFUNC(TREE_NOT_SORTED, ERROR) \\\n@@ -528,7 +529,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n {\n \tunsigned char tree_sha1[20], sha1[20];\n \tstruct commit_graft *graft;\n-\tunsigned parent_count, parent_line_count = 0;\n+\tunsigned parent_count, parent_line_count = 0, author_count;\n \tint err;\n \n \tif (require_end_of_header(buffer, size, &commit->object, options))\n@@ -568,9 +569,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,\n \t\t\t\treturn err;\n \t\t}\n \t}\n-\tif (!skip_prefix(buffer, \"author \", &buffer))\n-\t\treturn report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n-\terr = fsck_ident(&buffer, &commit->object, options);\n+\tauthor_count = 0;\n+\twhile (skip_prefix(buffer, \"author \", &buffer)) {\n+\t\tauthor_count++;\n+\t\terr = fsck_ident(&buffer, &commit->object, options);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\tif (author_count < 1)\n+\t\terr = report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, \"invalid format - expected 'author' line\");\n+\telse if (author_count > 1)\n+\t\terr = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, \"invalid format - multiple 'author' lines\");\n \tif (err)\n \t\treturn err;\n \tif (!skip_prefix(buffer, \"committer \", &buffer))\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264554","messageId":"dd82008f47d4f3b534a5c914c8f72accdbb6fd37.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 10/19] fsck: Make fsck_tag() warn-friendly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:26:30Z","receivedAt":"2015-06-22T15:26:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When fsck_tag() identifies a problem with the commit, it should try\nto make it possible to continue checking the commit object, in case the\nuser wants to demote the detected errors to mere warnings.\n\nJust like fsck_commit(), there are certain problems that could hide other\nissues with the same tag object. For example, if the 'type' line is not\nencountered in the correct position, the 'tag' line – if there is any –\nwould not be handled at all.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex daa07ad..3f76f99 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -642,7 +642,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t}\n \tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n') {\n \t\tret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, \"invalid 'object' line format - bad sha1\");\n-\t\tgoto done;\n+\t\tif (ret)\n+\t\t\tgoto done;\n \t}\n \tbuffer += 41;\n \n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264556","messageId":"50f06eaadaca96dd87f7dccc1b62e8abb0032dd9.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 11/19] fsck: Add a simple test for receive.fsck.<msg-id>","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:26:36Z","receivedAt":"2015-06-22T15:26:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n t/t5504-fetch-receive-strict.sh | 21 +++++++++++++++++++++\n 1 file changed, 21 insertions(+)\n\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 69ee13c..36024fc 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -115,4 +115,25 @@ test_expect_success 'push with transfer.fsckobjects' '\n \ttest_cmp exp act\n '\n \n+cat >bogus-commit <<\\EOF\n+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+author Bugs Bunny 1234567890 +0000\n+committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n+\n+This commit object intentionally broken\n+EOF\n+\n+test_expect_success 'push with receive.fsck.missingEmail=warn' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.missingEmail warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\tgrep \"missingEmail\" act\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264555","messageId":"d228bf8cfd6e8fdf148128d9ef5d611164d8ab11.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 12/19] fsck: Disallow demoting grave fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:26:42Z","receivedAt":"2015-06-22T15:26:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Some kinds of errors are intrinsically unrecoverable (e.g. errors while\nuncompressing objects). It does not make sense to allow demoting them to\nmere warnings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 13 +++++++++++--\n t/t5504-fetch-receive-strict.sh | 11 +++++++++++\n 2 files changed, 22 insertions(+), 2 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 3f76f99..85535b1 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -9,7 +9,12 @@\n #include \"refs.h\"\n #include \"utf8.h\"\n \n+#define FSCK_FATAL -1\n+\n #define FOREACH_MSG_ID(FUNC) \\\n+\t/* fatal errors */ \\\n+\tFUNC(NUL_IN_HEADER, FATAL) \\\n+\tFUNC(UNTERMINATED_HEADER, FATAL) \\\n \t/* errors */ \\\n \tFUNC(BAD_DATE, ERROR) \\\n \tFUNC(BAD_DATE_OVERFLOW, ERROR) \\\n@@ -39,11 +44,9 @@\n \tFUNC(MISSING_TYPE, ERROR) \\\n \tFUNC(MISSING_TYPE_ENTRY, ERROR) \\\n \tFUNC(MULTIPLE_AUTHORS, ERROR) \\\n-\tFUNC(NUL_IN_HEADER, ERROR) \\\n \tFUNC(TAG_OBJECT_NOT_TAG, ERROR) \\\n \tFUNC(TREE_NOT_SORTED, ERROR) \\\n \tFUNC(UNKNOWN_TYPE, ERROR) \\\n-\tFUNC(UNTERMINATED_HEADER, ERROR) \\\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE, WARN) \\\n@@ -149,6 +152,9 @@ void fsck_set_msg_type(struct fsck_options *options,\n \t\tdie(\"Unhandled message id: %s\", msg_id);\n \ttype = parse_msg_type(msg_type);\n \n+\tif (type != FSCK_ERROR && msg_id_info[id].msg_type == FSCK_FATAL)\n+\t\tdie(\"Cannot demote %s to %s\", msg_id, msg_type);\n+\n \tif (!options->msg_type) {\n \t\tint i;\n \t\tint *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);\n@@ -215,6 +221,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tif (msg_type == FSCK_FATAL)\n+\t\tmsg_type = FSCK_ERROR;\n+\n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n \tva_start(ap, fmt);\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex 36024fc..f5d6d0d 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -136,4 +136,15 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' '\n \tgrep \"missingEmail\" act\n '\n \n+test_expect_success \\\n+\t'receive.fsck.unterminatedHeader=warn triggers error' '\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckobjects true &&\n+\tgit --git-dir=dst/.git config \\\n+\t\treceive.fsck.unterminatedheader warn &&\n+\ttest_must_fail git push --porcelain dst HEAD >act 2>&1 &&\n+\tgrep \"Cannot demote unterminatedheader\" act\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264557","messageId":"4ae672be6b7ca7727cadc3b94c5f731b7df7abaf.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 13/19] fsck: Optionally ignore specific fsck issues completely","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:26:48Z","receivedAt":"2015-06-22T15:26:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An fsck issue in a legacy repository might be so common that one would\nlike not to bother the user with mentioning it at all. With this change,\nthat is possible by setting the respective message type to \"ignore\".\n\nThis change \"abuses\" the missingEmail=warn test to verify that \"ignore\"\nis also accepted and works correctly. And while at it, it makes sure\nthat multiple options work, too (they are passed to unpack-objects or\nindex-pack as a comma-separated list via the --strict=... command-line\noption).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                          | 5 +++++\n fsck.h                          | 1 +\n t/t5504-fetch-receive-strict.sh | 9 ++++++++-\n 3 files changed, 14 insertions(+), 1 deletion(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex 85535b1..cbfff1f 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -131,6 +131,8 @@ static int parse_msg_type(const char *str)\n \t\treturn FSCK_ERROR;\n \telse if (!strcmp(str, \"warn\"))\n \t\treturn FSCK_WARN;\n+\telse if (!strcmp(str, \"ignore\"))\n+\t\treturn FSCK_IGNORE;\n \telse\n \t\tdie(\"Unknown fsck message type: '%s'\", str);\n }\n@@ -221,6 +223,9 @@ static int report(struct fsck_options *options, struct object *object,\n \tstruct strbuf sb = STRBUF_INIT;\n \tint msg_type = fsck_msg_type(id, options), result;\n \n+\tif (msg_type == FSCK_IGNORE)\n+\t\treturn 0;\n+\n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n \ndiff --git a/fsck.h b/fsck.h\nindex 3ef92a3..1dab276 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -3,6 +3,7 @@\n \n #define FSCK_ERROR 1\n #define FSCK_WARN 2\n+#define FSCK_IGNORE 3\n \n struct fsck_options;\n \ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex f5d6d0d..af373ba 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -133,7 +133,14 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' '\n \tgit --git-dir=dst/.git config \\\n \t\treceive.fsck.missingEmail warn &&\n \tgit push --porcelain dst bogus >act 2>&1 &&\n-\tgrep \"missingEmail\" act\n+\tgrep \"missingEmail\" act &&\n+\tgit --git-dir=dst/.git branch -D bogus &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.missingEmail ignore &&\n+\tgit  --git-dir=dst/.git config --add \\\n+\t\treceive.fsck.badDate warn &&\n+\tgit push --porcelain dst bogus >act 2>&1 &&\n+\ttest_must_fail grep \"missingEmail\" act\n '\n \n test_expect_success \\\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264558","messageId":"3bbee0090b9ed0f6fc893facd815fdfaf0fa1619.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 14/19] fsck: Allow upgrading fsck warnings to errors","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:26:54Z","receivedAt":"2015-06-22T15:26:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'invalid tag name' and 'missing tagger entry' warnings can now be\nupgraded to errors by specifying `invalidTagName` and\n`missingTaggerEntry` in the receive.fsck.<msg-id> config setting.\n\nIncidentally, the missing tagger warning is now really shown as a warning\n(as opposed to being reported with the \"error:\" prefix, as it used to be\nthe case before this commit).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n fsck.c                | 24 +++++++++++++++++-------\n t/t5302-pack-index.sh |  2 +-\n 2 files changed, 18 insertions(+), 8 deletions(-)\n\ndiff --git a/fsck.c b/fsck.c\nindex cbfff1f..f6fc384 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -10,6 +10,7 @@\n #include \"utf8.h\"\n \n #define FSCK_FATAL -1\n+#define FSCK_INFO -2\n \n #define FOREACH_MSG_ID(FUNC) \\\n \t/* fatal errors */ \\\n@@ -50,15 +51,16 @@\n \tFUNC(ZERO_PADDED_DATE, ERROR) \\\n \t/* warnings */ \\\n \tFUNC(BAD_FILEMODE, WARN) \\\n-\tFUNC(BAD_TAG_NAME, WARN) \\\n \tFUNC(EMPTY_NAME, WARN) \\\n \tFUNC(FULL_PATHNAME, WARN) \\\n \tFUNC(HAS_DOT, WARN) \\\n \tFUNC(HAS_DOTDOT, WARN) \\\n \tFUNC(HAS_DOTGIT, WARN) \\\n-\tFUNC(MISSING_TAGGER_ENTRY, WARN) \\\n \tFUNC(NULL_SHA1, WARN) \\\n-\tFUNC(ZERO_PADDED_FILEMODE, WARN)\n+\tFUNC(ZERO_PADDED_FILEMODE, WARN) \\\n+\t/* infos (reported as warnings, but ignored by default) */ \\\n+\tFUNC(BAD_TAG_NAME, INFO) \\\n+\tFUNC(MISSING_TAGGER_ENTRY, INFO)\n \n #define MSG_ID(id, msg_type) FSCK_MSG_##id,\n enum fsck_msg_id {\n@@ -228,6 +230,8 @@ static int report(struct fsck_options *options, struct object *object,\n \n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n+\telse if (msg_type == FSCK_INFO)\n+\t\tmsg_type = FSCK_WARN;\n \n \tappend_msg_id(&sb, msg_id_info[id].id_string);\n \n@@ -686,15 +690,21 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,\n \t\tgoto done;\n \t}\n \tstrbuf_addf(&sb, \"refs/tags/%.*s\", (int)(eol - buffer), buffer);\n-\tif (check_refname_format(sb.buf, 0))\n-\t\treport(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,\n+\tif (check_refname_format(sb.buf, 0)) {\n+\t\tret = report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,\n \t\t\t   \"invalid 'tag' name: %.*s\",\n \t\t\t   (int)(eol - buffer), buffer);\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \tbuffer = eol + 1;\n \n-\tif (!skip_prefix(buffer, \"tagger \", &buffer))\n+\tif (!skip_prefix(buffer, \"tagger \", &buffer)) {\n \t\t/* early tags do not contain 'tagger' lines; warn only */\n-\t\treport(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, \"invalid format - expected 'tagger' line\");\n+\t\tif (ret)\n+\t\t\tgoto done;\n+\t}\n \telse\n \t\tret = fsck_ident(&buffer, &tag->object, options);\n \ndiff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh\nindex 61bc8da..3dc5ec4 100755\n--- a/t/t5302-pack-index.sh\n+++ b/t/t5302-pack-index.sh\n@@ -259,7 +259,7 @@ EOF\n     thirtyeight=${tag#??} &&\n     rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&\n     git index-pack --strict tag-test-${pack1}.pack 2>err &&\n-    grep \"^error:.* expected .tagger. line\" err\n+    grep \"^warning:.* expected .tagger. line\" err\n '\n \n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264559","messageId":"9d82c4ab5c93d70b80e333833959f543ccc68ba0.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 15/19] fsck: Document the new receive.fsck.<msg-id> options","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:27:00Z","receivedAt":"2015-06-22T15:27:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 3e37b93..4e5fbea 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2205,6 +2205,20 @@ receive.fsckObjects::\n \tDefaults to false. If not set, the value of `transfer.fsckObjects`\n \tis used instead.\n \n+receive.fsck.<msg-id>::\n+\tWhen `receive.fsckObjects` is set to true, errors can be switched\n+\tto warnings and vice versa by configuring the `receive.fsck.<msg-id>`\n+\tsetting where the `<msg-id>` is the fsck message ID and the value\n+\tis one of `error`, `warn` or `ignore`. For convenience, fsck prefixes\n+\tthe error/warning with the message ID, e.g. \"missingEmail: invalid\n+\tauthor/committer line - missing email\" means that setting\n+\t`receive.fsck.missingEmail = ignore` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which would not pass pushing when `receive.fsckObjects = true`, allowing\n+the host to accept repositories with certain known issues but still catch\n+other issues.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264560","messageId":"2a2659daa5bd676d68cd9b82fe630dbadcf9e360.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 16/19] fsck: Support demoting errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:27:06Z","receivedAt":"2015-06-22T15:27:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already have support in `git receive-pack` to deal with some legacy\nrepositories which have non-fatal issues.\n\nLet's make `git fsck` itself useful with such repositories, too, by\nallowing users to ignore known issues, or at least demote those issues\nto mere warnings.\n\nExample: `git -c fsck.missingEmail=ignore fsck` would hide\nproblems with missing emails in author, committer and tagger lines.\n\nIn the same spirit that `git receive-pack`'s usage of the fsck machinery\ndiffers from `git fsck`'s – some of the non-fatal warnings in `git fsck`\nare fatal with `git receive-pack` when receive.fsckObjects = true, for\nexample – we strictly separate the fsck.<msg-id> from the\nreceive.fsck.<msg-id> settings.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt | 11 +++++++++++\n builtin/fsck.c           | 12 ++++++++++++\n t/t1450-fsck.sh          | 11 +++++++++++\n 3 files changed, 34 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 4e5fbea..bfccd2b 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1250,6 +1250,17 @@ filter.<driver>.smudge::\n \tobject to a worktree file upon checkout.  See\n \tlinkgit:gitattributes[5] for details.\n \n+fsck.<msg-id>::\n+\tAllows overriding the message type (error, warn or ignore) of a\n+\tspecific message ID such as `missingEmail`.\n++\n+For convenience, fsck prefixes the error/warning with the message ID,\n+e.g.  \"missingEmail: invalid author/committer line - missing email\" means\n+that setting `fsck.missingEmail = ignore` will hide that issue.\n++\n+This feature is intended to support working with legacy repositories\n+which cannot be repaired without disruptive changes.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex fff38fe..adaa802 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -46,6 +46,16 @@ static int show_dangling = 1;\n #define DIRENT_SORT_HINT(de) ((de)->d_ino)\n #endif\n \n+static int fsck_config(const char *var, const char *value, void *cb)\n+{\n+\tif (skip_prefix(var, \"fsck.\", &var)) {\n+\t\tfsck_set_msg_type(&fsck_obj_options, var, value);\n+\t\treturn 0;\n+\t}\n+\n+\treturn git_default_config(var, value, cb);\n+}\n+\n static void objreport(struct object *obj, const char *msg_type,\n \t\t\tconst char *err)\n {\n@@ -646,6 +656,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \t\tinclude_reflogs = 0;\n \t}\n \n+\tgit_config(fsck_config, NULL);\n+\n \tfsck_head_link();\n \tfsck_object_dir(get_object_directory());\n \ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 7c5b3d5..1727129 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -287,6 +287,17 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '\n \tgrep -q \"error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff\" out\n '\n \n+test_expect_success 'force fsck to ignore double author' '\n+\tgit cat-file commit HEAD >basis &&\n+\tsed \"s/^author .*/&,&/\" <basis | tr , \\\\n >multiple-authors &&\n+\tnew=$(git hash-object -t commit -w --stdin <multiple-authors) &&\n+\ttest_when_finished \"remove_object $new\" &&\n+\tgit update-ref refs/heads/bogus \"$new\" &&\n+\ttest_when_finished \"git update-ref -d refs/heads/bogus\" &&\n+\ttest_must_fail git fsck &&\n+\tgit -c fsck.multipleAuthors=ignore fsck\n+'\n+\n _bz='\\0'\n _bz5=\"$_bz$_bz$_bz$_bz$_bz\"\n _bz20=\"$_bz5$_bz5$_bz5$_bz5\"\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264561","messageId":"cb83b55237198db7cde2d4fcf3765e76137af07e.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 17/19] fsck: Introduce `git fsck --connectivity-only`","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:27:12Z","receivedAt":"2015-06-22T15:27:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This option avoids unpacking each and all blob objects, and just\nverifies the connectivity. In particular with large repositories, this\nspeeds up the operation, at the expense of missing corrupt blobs,\nignoring unreachable objects and other fsck issues, if any.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-fsck.txt |  7 ++++++-\n builtin/fsck.c             |  7 ++++++-\n t/t1450-fsck.sh            | 22 ++++++++++++++++++++++\n 3 files changed, 34 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt\nindex 25c431d..84ee92e 100644\n--- a/Documentation/git-fsck.txt\n+++ b/Documentation/git-fsck.txt\n@@ -11,7 +11,7 @@ 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] [<object>*]\n+\t [--[no-]dangling] [--[no-]progress] [--connectivity-only] [<object>*]\n \n DESCRIPTION\n -----------\n@@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs\n \tobject pools.  This is now default; you can turn it off\n \twith --no-full.\n \n+--connectivity-only::\n+\tCheck only the connectivity of tags, commits and tree objects. By\n+\tavoiding to unpack blobs, this speeds up the operation, at the\n+\texpense of missing corrupt objects or other problematic issues.\n+\n --strict::\n \tEnable more strict checking, namely to catch a file mode\n \trecorded with g+w bit set, which was created by older\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex adaa802..2d14298 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -23,6 +23,7 @@ static int show_tags;\n static int show_unreachable;\n static int include_reflogs = 1;\n static int check_full = 1;\n+static int connectivity_only;\n static int check_strict;\n static int keep_cache_objects;\n static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;\n@@ -181,6 +182,8 @@ static void check_reachable_object(struct object *obj)\n \tif (!(obj->flags & HAS_OBJ)) {\n \t\tif (has_sha1_pack(obj->sha1))\n \t\t\treturn; /* it is in pack - forget about it */\n+\t\tif (connectivity_only && has_sha1_file(obj->sha1))\n+\t\t\treturn;\n \t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n \t\terrors_found |= ERROR_REACHABLE;\n \t\treturn;\n@@ -623,6 +626,7 @@ static struct option fsck_opts[] = {\n \tOPT_BOOL(0, \"cache\", &keep_cache_objects, N_(\"make index objects head nodes\")),\n \tOPT_BOOL(0, \"reflogs\", &include_reflogs, N_(\"make reflogs head nodes (default)\")),\n \tOPT_BOOL(0, \"full\", &check_full, N_(\"also consider packs and alternate objects\")),\n+\tOPT_BOOL(0, \"connectivity-only\", &connectivity_only, N_(\"check only connectivity\")),\n \tOPT_BOOL(0, \"strict\", &check_strict, N_(\"enable more strict checking\")),\n \tOPT_BOOL(0, \"lost-found\", &write_lost_and_found,\n \t\t\t\tN_(\"write dangling objects in .git/lost-found\")),\n@@ -659,7 +663,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)\n \tgit_config(fsck_config, NULL);\n \n \tfsck_head_link();\n-\tfsck_object_dir(get_object_directory());\n+\tif (!connectivity_only)\n+\t\tfsck_object_dir(get_object_directory());\n \n \tprepare_alt_odb();\n \tfor (alt = alt_odb_list; alt; alt = alt->next) {\ndiff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh\nindex 1727129..956673b 100755\n--- a/t/t1450-fsck.sh\n+++ b/t/t1450-fsck.sh\n@@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' '\n \ttest_must_fail git -C missing fsck\n '\n \n+test_expect_success 'fsck --connectivity-only' '\n+\trm -rf connectivity-only &&\n+\tgit init connectivity-only &&\n+\t(\n+\t\tcd connectivity-only &&\n+\t\ttouch empty &&\n+\t\tgit add empty &&\n+\t\ttest_commit empty &&\n+\t\tempty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 &&\n+\t\trm -f $empty &&\n+\t\techo invalid >$empty &&\n+\t\ttest_must_fail git fsck --strict &&\n+\t\tgit fsck --strict --connectivity-only &&\n+\t\ttree=$(git rev-parse HEAD:) &&\n+\t\tsuffix=${tree#??} &&\n+\t\ttree=.git/objects/${tree%$suffix}/$suffix &&\n+\t\trm -f $tree &&\n+\t\techo invalid >$tree &&\n+\t\ttest_must_fail git fsck --strict --connectivity-only\n+\t)\n+'\n+\n test_done\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264563","messageId":"0f26e80844745a9005eb4073289ea56dd452bbe2.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 18/19] fsck: git receive-pack: support excluding objects from fsck'ing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:27:18Z","receivedAt":"2015-06-22T15:27:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The optional new config option `receive.fsck.skipList` specifies the path\nto a file listing the names, i.e. SHA-1s, one per line, of objects that\nare to be ignored by `git receive-pack` when `receive.fsckObjects = true`.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nThe intended use case is for server administrators to inspect objects\nthat are reported by `git push` as being too problematic to enter the\nrepository, and to add the objects' SHA-1 to a (preferably sorted) file\nwhen the objects are legitimate, i.e. when it is determined that those\nproblematic objects should be allowed to enter the server.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt        |  8 +++++++\n builtin/receive-pack.c          | 11 +++++++++\n fsck.c                          | 50 +++++++++++++++++++++++++++++++++++++++++\n fsck.h                          |  1 +\n t/t5504-fetch-receive-strict.sh | 12 ++++++++++\n 5 files changed, 82 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex bfccd2b..ed7f37f 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -2230,6 +2230,14 @@ which would not pass pushing when `receive.fsckObjects = true`, allowing\n the host to accept repositories with certain known issues but still catch\n other issues.\n \n+receive.fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\tNote: corrupt objects cannot be skipped with this setting.\n+\n receive.unpackLimit::\n \tIf the number of objects received in a push is below this\n \tlimit then the objects will be unpacked into loose object\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 3afe8f8..3fbed23 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -117,6 +117,17 @@ static int receive_pack_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (strcmp(var, \"receive.fsck.skiplist\") == 0) {\n+\t\tconst char *path;\n+\n+\t\tif (git_config_pathname(&path, var, value))\n+\t\t\treturn 1;\n+\t\tstrbuf_addf(&fsck_msg_types, \"%cskiplist=%s\",\n+\t\t\tfsck_msg_types.len ? ',' : '=', path);\n+\t\tfree((char *) path);\n+\t\treturn 0;\n+\t}\n+\n \tif (skip_prefix(var, \"receive.fsck.\", &var)) {\n \t\tif (is_valid_msg_type(var, value))\n \t\t\tstrbuf_addf(&fsck_msg_types, \"%c%s=%s\",\ndiff --git a/fsck.c b/fsck.c\nindex f6fc384..a677b50 100644\n--- a/fsck.c\n+++ b/fsck.c\n@@ -8,6 +8,7 @@\n #include \"fsck.h\"\n #include \"refs.h\"\n #include \"utf8.h\"\n+#include \"sha1-array.h\"\n \n #define FSCK_FATAL -1\n #define FSCK_INFO -2\n@@ -127,6 +128,43 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,\n \treturn msg_type;\n }\n \n+static void init_skiplist(struct fsck_options *options, const char *path)\n+{\n+\tstatic struct sha1_array skiplist = SHA1_ARRAY_INIT;\n+\tint sorted, fd;\n+\tchar buffer[41];\n+\tunsigned char sha1[20];\n+\n+\tif (options->skiplist)\n+\t\tsorted = options->skiplist->sorted;\n+\telse {\n+\t\tsorted = 1;\n+\t\toptions->skiplist = &skiplist;\n+\t}\n+\n+\tfd = open(path, O_RDONLY);\n+\tif (fd < 0)\n+\t\tdie(\"Could not open skip list: %s\", path);\n+\tfor (;;) {\n+\t\tint result = read_in_full(fd, buffer, sizeof(buffer));\n+\t\tif (result < 0)\n+\t\t\tdie_errno(\"Could not read '%s'\", path);\n+\t\tif (!result)\n+\t\t\tbreak;\n+\t\tif (get_sha1_hex(buffer, sha1) || buffer[40] != '\\n')\n+\t\t\tdie(\"Invalid SHA-1: %s\", buffer);\n+\t\tsha1_array_append(&skiplist, sha1);\n+\t\tif (sorted && skiplist.nr > 1 &&\n+\t\t\t\thashcmp(skiplist.sha1[skiplist.nr - 2],\n+\t\t\t\t\tsha1) > 0)\n+\t\t\tsorted = 0;\n+\t}\n+\tclose(fd);\n+\n+\tif (sorted)\n+\t\tskiplist.sorted = 1;\n+}\n+\n static int parse_msg_type(const char *str)\n {\n \tif (!strcmp(str, \"error\"))\n@@ -190,6 +228,14 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)\n \t\t\tbuf[equal] = tolower(buf[equal]);\n \t\tbuf[equal] = '\\0';\n \n+\t\tif (!strcmp(buf, \"skiplist\")) {\n+\t\t\tif (equal == len)\n+\t\t\t\tdie(\"skiplist requires a path\");\n+\t\t\tinit_skiplist(options, buf + equal + 1);\n+\t\t\tbuf += len + 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif (equal == len)\n \t\t\tdie(\"Missing '=': '%s'\", buf);\n \n@@ -228,6 +274,10 @@ static int report(struct fsck_options *options, struct object *object,\n \tif (msg_type == FSCK_IGNORE)\n \t\treturn 0;\n \n+\tif (options->skiplist && object &&\n+\t\t\tsha1_array_lookup(options->skiplist, object->sha1) >= 0)\n+\t\treturn 0;\n+\n \tif (msg_type == FSCK_FATAL)\n \t\tmsg_type = FSCK_ERROR;\n \telse if (msg_type == FSCK_INFO)\ndiff --git a/fsck.h b/fsck.h\nindex 1dab276..dded84b 100644\n--- a/fsck.h\n+++ b/fsck.h\n@@ -32,6 +32,7 @@ struct fsck_options {\n \tfsck_error error_func;\n \tunsigned strict:1;\n \tint *msg_type;\n+\tstruct sha1_array *skiplist;\n };\n \n #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL }\ndiff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh\nindex af373ba..6a1f89e 100755\n--- a/t/t5504-fetch-receive-strict.sh\n+++ b/t/t5504-fetch-receive-strict.sh\n@@ -123,6 +123,18 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000\n This commit object intentionally broken\n EOF\n \n+test_expect_success 'push with receive.fsck.skipList' '\n+\tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n+\tgit push . $commit:refs/heads/bogus &&\n+\trm -rf dst &&\n+\tgit init dst &&\n+\tgit --git-dir=dst/.git config receive.fsckObjects true &&\n+\ttest_must_fail git push --porcelain dst bogus &&\n+\tgit --git-dir=dst/.git config receive.fsck.skipList SKIP &&\n+\techo $commit >dst/.git/SKIP &&\n+\tgit push --porcelain dst bogus\n+'\n+\n test_expect_success 'push with receive.fsck.missingEmail=warn' '\n \tcommit=\"$(git hash-object -t commit -w --stdin <bogus-commit)\" &&\n \tgit push . $commit:refs/heads/bogus &&\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264562","messageId":"48da2ac1fac1e4e68142c174846ba5977f3e0788.1434986506.git.johannes.schindelin@gmx.de","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"[PATCH v7 19/19] fsck: support ignoring objects in `git fsck` via fsck.skiplist","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T15:27:23Z","receivedAt":"2015-06-22T15:27:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Identical to support in `git receive-pack for the config option\n`receive.fsck.skiplist`, we now support ignoring given objects in\n`git fsck` via `fsck.skiplist` altogether.\n\nThis is extremely handy in case of legacy repositories where it would\ncause more pain to change incorrect objects than to live with them\n(e.g. a duplicate 'author' line in an early commit object).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/config.txt |  8 ++++++++\n builtin/fsck.c           | 13 +++++++++++++\n 2 files changed, 21 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ed7f37f..69dda93 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1261,6 +1261,14 @@ that setting `fsck.missingEmail = ignore` will hide that issue.\n This feature is intended to support working with legacy repositories\n which cannot be repaired without disruptive changes.\n \n+fsck.skipList::\n+\tThe path to a sorted list of object names (i.e. one SHA-1 per\n+\tline) that are known to be broken in a non-fatal way and should\n+\tbe ignored. This feature is useful when an established project\n+\tshould be accepted despite early commits containing errors that\n+\tcan be safely ignored such as invalid committer email addresses.\n+\tNote: corrupt objects cannot be skipped with this setting.\n+\n gc.aggressiveDepth::\n \tThe depth parameter used in the delta compression\n \talgorithm used by 'git gc --aggressive'.  This defaults\ndiff --git a/builtin/fsck.c b/builtin/fsck.c\nindex 2d14298..7e3df20 100644\n--- a/builtin/fsck.c\n+++ b/builtin/fsck.c\n@@ -49,6 +49,19 @@ static int show_dangling = 1;\n \n static int fsck_config(const char *var, const char *value, void *cb)\n {\n+\tif (strcmp(var, \"fsck.skiplist\") == 0) {\n+\t\tconst char *path;\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\n+\t\tif (git_config_pathname(&path, var, value))\n+\t\t\treturn 1;\n+\t\tstrbuf_addf(&sb, \"skiplist=%s\", path);\n+\t\tfree((char *) path);\n+\t\tfsck_set_msg_types(&fsck_obj_options, sb.buf);\n+\t\tstrbuf_release(&sb);\n+\t\treturn 0;\n+\t}\n+\n \tif (skip_prefix(var, \"fsck.\", &var)) {\n \t\tfsck_set_msg_type(&fsck_obj_options, var, value);\n \t\treturn 0;\n-- \n2.3.1.windows.1.9.g8c01ab4\n"},{"id":"264572","messageId":"xmqq616fiqz7.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"08ab723892b4412431b88df8764f506f0abbb0a6.1434986506.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v7 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-22T17:37:48Z","receivedAt":"2015-06-22T17:37:48Z","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> diff --git a/fsck.c b/fsck.c\n> index 1a3f7ce..e81a342 100644\n> --- a/fsck.c\n> +++ b/fsck.c\n> @@ -64,30 +64,29 @@ enum fsck_msg_id {\n>  #undef MSG_ID\n>  \n>  #define STR(x) #x\n> -#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n> +#define MSG_ID(id, msg_type) { STR(id), NULL, FSCK_##msg_type },\n>  static struct {\n>  \tconst char *id_string;\n> +\tconst char *lowercased;\n>  \tint msg_type;\n>  } msg_id_info[FSCK_MSG_MAX + 1] = {\n>  \tFOREACH_MSG_ID(MSG_ID)\n> -\t{ NULL, -1 }\n> +\t{ NULL, NULL, -1 }\n>  };\n>  #undef MSG_ID\n>  \n>  static int parse_msg_id(const char *text)\n>  {\n> -\tstatic char **lowercased;\n>  \tint i;\n>  \n> -\tif (!lowercased) {\n> +\tif (!msg_id_info[0].lowercased) {\n>  \t\t/* convert id_string to lower case, without underscores. */\n> -\t\tlowercased = xmalloc(FSCK_MSG_MAX * sizeof(*lowercased));\n>  \t\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n>  \t\t\tconst char *p = msg_id_info[i].id_string;\n>  \t\t\tint len = strlen(p);\n>  \t\t\tchar *q = xmalloc(len);\n>  \n> -\t\t\tlowercased[i] = q;\n> +\t\t\tmsg_id_info[i].lowercased = q;\n>  \t\t\twhile (*p)\n>  \t\t\t\tif (*p == '_')\n>  \t\t\t\t\tp++;\n> @@ -98,7 +97,7 @@ static int parse_msg_id(const char *text)\n>  \t}\n>  \n>  \tfor (i = 0; i < FSCK_MSG_MAX; i++)\n> -\t\tif (!strcmp(text, lowercased[i]))\n> +\t\tif (!strcmp(text, msg_id_info[i].lowercased))\n>  \t\t\treturn i;\n>  \n>  \treturn -1;\n\nHeh, this was the first thing that came to my mind when I saw 03/19\nthat lazily prepares downcased version (which is good) but do so in\na separately allocated buffer (which is improved by this change) ;-)\n\nIOW, I think all of the above should have been part of 03/19, not\n\"oops I belatedly realized that this way is better\" fixup here.\n\nThe end result looks good, so let's keep reading.\n\n> +void fsck_set_msg_types(struct fsck_options *options, const char *values)\n> +{\n> +\tchar *buf = xstrdup(values), *to_free = buf;\n> +\tint done = 0;\n> +\n> +\twhile (!done) {\n> +\t\tint len = strcspn(buf, \" ,|\"), equal;\n> +\n> +\t\tdone = !buf[len];\n> +\t\tif (!len) {\n> +\t\t\tbuf++;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tbuf[len] = '\\0';\n> +\n> +\t\tfor (equal = 0; equal < len &&\n> +\t\t\t\tbuf[equal] != '=' && buf[equal] != ':'; equal++)\n\nStyle.  I'd format this more like so:\n\n\t\tfor (equal = 0;\n                     equal < len && buf[equal] != '=' && buf[equal] != ':';\n\t\t     equal++)\n\n> +\t\t\tbuf[equal] = tolower(buf[equal]);\n> +\t\tbuf[equal] = '\\0';\n> +\n> +\t\tif (equal == len)\n> +\t\t\tdie(\"Missing '=': '%s'\", buf);\n> +\n> +\t\tfsck_set_msg_type(options, buf, buf + equal + 1);\n> +\t\tbuf += len + 1;\n> +\t}\n> +\tfree(to_free);\n> +}\n\nOverall, the change is good (and it was good in v6, too), and I\nthink it has become simpler to follow the logic with the upfront\ndowncasing.\n\nThanks.\n"},{"id":"264573","messageId":"xmqqlhfbhb9u.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"cover.1434986506.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v7 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-22T18:02:21Z","receivedAt":"2015-06-22T18:02:21Z","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> Changes since v6:\n> \n> - camelCased message IDs\n>\n> - multiple author checking now as suggested by Junio\n>\n> - renamed `--quick` to `--connectivity-only`, better commit message\n>\n> - `fsck.skipList` is now handled correctly (and not mistaken for a message\n>   type setting)\n>\n> - `fsck.skipList` can handle user paths now\n>\n> - index-pack configures the walk function in a more logical place now\n>\n> - simplified code by avoiding working on partial strings (i.e. removed\n>   `substrcmp()`). This saves 10 lines. To accomodate parsing config\n>   variables directly, we now work on lowercased message IDs; unfortunately\n>   this means that we cannot use them in append_msg_id() because that\n>   function wants to append camelCased message IDs.\n>\n> Interdiff below diffstat.\n\nExcept for minor nits I sent separate messages, this round looks\nvery nicely done (I however admit that I haven't read the skiplist\nparsing code carefully at all, expecting that you wouldn't screw up\nwith something simple like that ;-))\n\nThanks, will replace what is queued.  Let's start thinking about\nmoving it down to 'next' (meaning: we _could_ still accept a reroll,\nbut I think we are in a good shape and minor incremental refinements\nwould suffice), cooking it for the remainder of the cycle and having\nit graduate to 'master' at the beginning of the next cycle.\n\nThanks.\n"},{"id":"264574","messageId":"xmqqegl3hb6m.fsf@gitster.dls.corp.google.com","threadId":"38144","inReplyTo":"4ae672be6b7ca7727cadc3b94c5f731b7df7abaf.1434986506.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v7 13/19] fsck: Optionally ignore specific fsck issues completely","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-22T18:04:17Z","receivedAt":"2015-06-22T18:04:17Z","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> +\tgit --git-dir=dst/.git branch -D bogus &&\n> +\tgit  --git-dir=dst/.git config --add \\\n> +\t\treceive.fsck.missingEmail ignore &&\n> +\tgit  --git-dir=dst/.git config --add \\\n> +\t\treceive.fsck.badDate warn &&\n\nFunny double-SP (will locally fix).\n\nThere are a few other minor style nits (not in this patch but in\nother patches in the series) like\n\n\ts/free((char *) var)/free((char *)var)/;\n\nthat I locally added SQUASH, and I may also have tweaked some log\nmessages.  Please check what you will see in 'pu' when I push the\nday's integration out later.\n\nThanks.\n"},{"id":"264585","messageId":"4037772e17652ba93638434294c9512c@www.dscho.org","threadId":"38144","inReplyTo":"xmqq616fiqz7.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v7 04/19] fsck: Offer a function to demote fsck errors to warnings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T21:00:18Z","receivedAt":"2015-06-22T21:00:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-22 19:37, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> diff --git a/fsck.c b/fsck.c\n>> index 1a3f7ce..e81a342 100644\n>> --- a/fsck.c\n>> +++ b/fsck.c\n>> @@ -64,30 +64,29 @@ enum fsck_msg_id {\n>>  #undef MSG_ID\n>>\n>>  #define STR(x) #x\n>> -#define MSG_ID(id, msg_type) { STR(id), FSCK_##msg_type },\n>> +#define MSG_ID(id, msg_type) { STR(id), NULL, FSCK_##msg_type },\n>>  static struct {\n>>  \tconst char *id_string;\n>> +\tconst char *lowercased;\n>>  \tint msg_type;\n>>  } msg_id_info[FSCK_MSG_MAX + 1] = {\n>>  \tFOREACH_MSG_ID(MSG_ID)\n>> -\t{ NULL, -1 }\n>> +\t{ NULL, NULL, -1 }\n>>  };\n>>  #undef MSG_ID\n>>\n>>  static int parse_msg_id(const char *text)\n>>  {\n>> -\tstatic char **lowercased;\n>>  \tint i;\n>>\n>> -\tif (!lowercased) {\n>> +\tif (!msg_id_info[0].lowercased) {\n>>  \t\t/* convert id_string to lower case, without underscores. */\n>> -\t\tlowercased = xmalloc(FSCK_MSG_MAX * sizeof(*lowercased));\n>>  \t\tfor (i = 0; i < FSCK_MSG_MAX; i++) {\n>>  \t\t\tconst char *p = msg_id_info[i].id_string;\n>>  \t\t\tint len = strlen(p);\n>>  \t\t\tchar *q = xmalloc(len);\n>>\n>> -\t\t\tlowercased[i] = q;\n>> +\t\t\tmsg_id_info[i].lowercased = q;\n>>  \t\t\twhile (*p)\n>>  \t\t\t\tif (*p == '_')\n>>  \t\t\t\t\tp++;\n>> @@ -98,7 +97,7 @@ static int parse_msg_id(const char *text)\n>>  \t}\n>>\n>>  \tfor (i = 0; i < FSCK_MSG_MAX; i++)\n>> -\t\tif (!strcmp(text, lowercased[i]))\n>> +\t\tif (!strcmp(text, msg_id_info[i].lowercased))\n>>  \t\t\treturn i;\n>>\n>>  \treturn -1;\n> \n> Heh, this was the first thing that came to my mind when I saw 03/19\n> that lazily prepares downcased version (which is good) but do so in\n> a separately allocated buffer (which is improved by this change) ;-)\n> \n> IOW, I think all of the above should have been part of 03/19, not\n> \"oops I belatedly realized that this way is better\" fixup here.\n\nGaaaah. Wrong commit fixed up. Sorry. Will be fixed in v8.\n\n>> +void fsck_set_msg_types(struct fsck_options *options, const char *values)\n>> +{\n>> +\tchar *buf = xstrdup(values), *to_free = buf;\n>> +\tint done = 0;\n>> +\n>> +\twhile (!done) {\n>> +\t\tint len = strcspn(buf, \" ,|\"), equal;\n>> +\n>> +\t\tdone = !buf[len];\n>> +\t\tif (!len) {\n>> +\t\t\tbuf++;\n>> +\t\t\tcontinue;\n>> +\t\t}\n>> +\t\tbuf[len] = '\\0';\n>> +\n>> +\t\tfor (equal = 0; equal < len &&\n>> +\t\t\t\tbuf[equal] != '=' && buf[equal] != ':'; equal++)\n> \n> Style.  I'd format this more like so:\n> \n> \t\tfor (equal = 0;\n>                      equal < len && buf[equal] != '=' && buf[equal] != ':';\n> \t\t     equal++)\n\nWill be fixed.\n\n>> +\t\t\tbuf[equal] = tolower(buf[equal]);\n>> +\t\tbuf[equal] = '\\0';\n>> +\n>> +\t\tif (equal == len)\n>> +\t\t\tdie(\"Missing '=': '%s'\", buf);\n>> +\n>> +\t\tfsck_set_msg_type(options, buf, buf + equal + 1);\n>> +\t\tbuf += len + 1;\n>> +\t}\n>> +\tfree(to_free);\n>> +}\n> \n> Overall, the change is good (and it was good in v6, too), and I\n> think it has become simpler to follow the logic with the upfront\n> downcasing.\n\nYep, I agree. I did not expect that, but it was worth the effort to compare the two versions.\n\nCiao,\nDscho\n"},{"id":"264590","messageId":"aaf356d9d155cea34238883f22ed9cc6@www.dscho.org","threadId":"38144","inReplyTo":"xmqqlhfbhb9u.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v7 00/19] Introduce an internal API to interact with the fsck machinery","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T21:07:17Z","receivedAt":"2015-06-22T21:07:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-22 20:02, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> Changes since v6:\n>>\n>> - camelCased message IDs\n>>\n>> - multiple author checking now as suggested by Junio\n>>\n>> - renamed `--quick` to `--connectivity-only`, better commit message\n>>\n>> - `fsck.skipList` is now handled correctly (and not mistaken for a message\n>>   type setting)\n>>\n>> - `fsck.skipList` can handle user paths now\n>>\n>> - index-pack configures the walk function in a more logical place now\n>>\n>> - simplified code by avoiding working on partial strings (i.e. removed\n>>   `substrcmp()`). This saves 10 lines. To accomodate parsing config\n>>   variables directly, we now work on lowercased message IDs; unfortunately\n>>   this means that we cannot use them in append_msg_id() because that\n>>   function wants to append camelCased message IDs.\n>>\n>> Interdiff below diffstat.\n> \n> Except for minor nits I sent separate messages, this round looks\n> very nicely done (I however admit that I haven't read the skiplist\n> parsing code carefully at all, expecting that you wouldn't screw up\n> with something simple like that ;-))\n> \n> Thanks, will replace what is queued.  Let's start thinking about\n> moving it down to 'next' (meaning: we _could_ still accept a reroll,\n> but I think we are in a good shape and minor incremental refinements\n> would suffice), cooking it for the remainder of the cycle and having\n> it graduate to 'master' at the beginning of the next cycle.\n\nLet me submit a v8 with the borked fixup fixed (i.e. part of 04/19 moved to 03/19, where it really belongs), the `for` style fix, the fixed double space and the cast style, too.\n\nCiao,\nDscho\n"},{"id":"264592","messageId":"572bb1fdc77e8eb2862558b86304d4f2@www.dscho.org","threadId":"38144","inReplyTo":"xmqqegl3hb6m.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v7 13/19] fsck: Optionally ignore specific fsck issues completely","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-06-22T21:11:00Z","receivedAt":"2015-06-22T21:11:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn 2015-06-22 20:04, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n>> +\tgit --git-dir=dst/.git branch -D bogus &&\n>> +\tgit  --git-dir=dst/.git config --add \\\n>> +\t\treceive.fsck.missingEmail ignore &&\n>> +\tgit  --git-dir=dst/.git config --add \\\n>> +\t\treceive.fsck.badDate warn &&\n> \n> Funny double-SP (will locally fix).\n> \n> There are a few other minor style nits (not in this patch but in\n> other patches in the series) like\n> \n> \ts/free((char *) var)/free((char *)var)/;\n> \n> that I locally added SQUASH,\n\nI fixed all of this locally, ready to push out v8, but...\n\n> and I may also have tweaked some log messages.  Please check what you will see in 'pu' when I push the day's integration out later.\n\n... I did not see that yet.\n\nFor the record, my current state is available at\n\n    https://github.com/dscho/git/compare/fsck-api~19...fsck-api\n\nI could wait for tomorrow to adjust my commit messages... or what do you want me to do?\n\nCiao,\nDscho\n"}]}