From 22410549fc71b3973a61666f82b7c76317da5df0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:25:00 +0200 Subject: fsck: introduce fsck options Just like the diff machinery, we are about to introduce more settings, therefore it makes sense to carry them around as a (pointer to a) struct containing all of them. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin/fsck.c b/builtin/fsck.c index 4e8e2ee..6f11fbc 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -25,6 +25,8 @@ static int include_reflogs = 1; static int check_full = 1; static int check_strict; static int keep_cache_objects; +static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT; +static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT; static struct object_id head_oid; static const char *head_points_at; static int errors_found; @@ -76,7 +78,7 @@ static int fsck_error_func(struct object *obj, int type, const char *err, ...) static struct object_array pending; -static int mark_object(struct object *obj, int type, void *data) +static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options) { struct object *parent = data; @@ -119,7 +121,7 @@ static int mark_object(struct object *obj, int type, void *data) static void mark_object_reachable(struct object *obj) { - mark_object(obj, OBJ_ANY, NULL); + mark_object(obj, OBJ_ANY, NULL, NULL); } static int traverse_one_object(struct object *obj) @@ -132,7 +134,7 @@ static int traverse_one_object(struct object *obj) if (parse_tree(tree) < 0) return 1; /* error already displayed */ } - result = fsck_walk(obj, mark_object, obj); + result = fsck_walk(obj, obj, &fsck_walk_options); if (tree) free_tree_buffer(tree); return result; @@ -158,7 +160,7 @@ static int traverse_reachable(void) return !!result; } -static int mark_used(struct object *obj, int type, void *data) +static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options) { if (!obj) return 1; @@ -296,9 +298,9 @@ static int fsck_obj(struct object *obj) fprintf(stderr, "Checking %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1)); - if (fsck_walk(obj, mark_used, NULL)) + if (fsck_walk(obj, NULL, &fsck_obj_options)) objerror(obj, "broken links"); - if (fsck_object(obj, NULL, 0, check_strict, fsck_error_func)) + if (fsck_object(obj, NULL, 0, &fsck_obj_options)) return -1; if (obj->type == OBJ_TREE) { @@ -632,6 +634,12 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); + fsck_walk_options.walk = mark_object; + fsck_obj_options.walk = mark_used; + fsck_obj_options.error_func = fsck_error_func; + if (check_strict) + fsck_obj_options.strict = 1; + if (show_progress == -1) show_progress = isatty(2); if (verbose) diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 2ecfbae..627eb3f 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -75,6 +75,7 @@ static int nr_threads; static int from_stdin; static int strict; static int do_fsck_object; +static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; static int verbose; static int show_stat; static int check_self_contained_and_connected; @@ -192,7 +193,7 @@ static void cleanup_thread(void) #endif -static int mark_link(struct object *obj, int type, void *data) +static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options) { if (!obj) return -1; @@ -838,10 +839,9 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (!obj) die(_("invalid %s"), typename(type)); if (do_fsck_object && - fsck_object(obj, buf, size, 1, - fsck_error_function)) + fsck_object(obj, buf, size, &fsck_options)) die(_("Error in object")); - if (fsck_walk(obj, mark_link, NULL)) + if (fsck_walk(obj, NULL, &fsck_options)) die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1)); if (obj->type == OBJ_TREE) { @@ -1615,6 +1615,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) usage(index_pack_usage); check_replace_refs = 0; + fsck_options.walk = mark_link; reset_pack_idx_option(&opts); git_config(git_index_pack_config, &opts); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index ac66672..6d17040 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -20,6 +20,7 @@ static unsigned char buffer[4096]; static unsigned int offset, len; static off_t consumed_bytes; static git_SHA_CTX ctx; +static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; /* * When running under --strict mode, objects whose reachability are @@ -178,7 +179,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf) * that have reachability requirements and calls this function. * Verify its reachability and validity recursively and write it out. */ -static int check_object(struct object *obj, int type, void *data) +static int check_object(struct object *obj, int type, void *data, struct fsck_options *options) { struct obj_buffer *obj_buf; @@ -203,10 +204,10 @@ static int check_object(struct object *obj, int type, void *data) obj_buf = lookup_object_buffer(obj); if (!obj_buf) die("Whoops! Cannot find object '%s'", sha1_to_hex(obj->sha1)); - if (fsck_object(obj, obj_buf->buffer, obj_buf->size, 1, - fsck_error_function)) + if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options)) die("Error in object"); - if (fsck_walk(obj, check_object, NULL)) + fsck_options.walk = check_object; + if (fsck_walk(obj, NULL, &fsck_options)) die("Error on reachable objects of %s", sha1_to_hex(obj->sha1)); write_cached_object(obj, obj_buf); return 0; @@ -217,7 +218,7 @@ static void write_rest(void) unsigned i; for (i = 0; i < nr_objects; i++) { if (obj_list[i].obj) - check_object(obj_list[i].obj, OBJ_ANY, NULL); + check_object(obj_list[i].obj, OBJ_ANY, NULL, NULL); } } diff --git a/fsck.c b/fsck.c index 10bcb65..d83b811 100644 --- a/fsck.c +++ b/fsck.c @@ -9,7 +9,7 @@ #include "refs.h" #include "utf8.h" -static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data) +static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options) { struct tree_desc desc; struct name_entry entry; @@ -25,9 +25,9 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data) if (S_ISGITLINK(entry.mode)) continue; if (S_ISDIR(entry.mode)) - result = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data); + result = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options); else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) - result = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data); + result = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options); else { result = error("in tree %s: entry %s has bad mode %.6o", sha1_to_hex(tree->object.sha1), entry.path, entry.mode); @@ -40,7 +40,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data) return res; } -static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data) +static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options) { struct commit_list *parents; int res; @@ -49,14 +49,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da if (parse_commit(commit)) return -1; - result = walk((struct object *)commit->tree, OBJ_TREE, data); + result = options->walk((struct object *)commit->tree, OBJ_TREE, data, options); if (result < 0) return result; res = result; parents = commit->parents; while (parents) { - result = walk((struct object *)parents->item, OBJ_COMMIT, data); + result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options); if (result < 0) return result; if (!res) @@ -66,14 +66,14 @@ static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *da return res; } -static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data) +static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options) { if (parse_tag(tag)) return -1; - return walk(tag->tagged, OBJ_ANY, data); + return options->walk(tag->tagged, OBJ_ANY, data, options); } -int fsck_walk(struct object *obj, fsck_walk_func walk, void *data) +int fsck_walk(struct object *obj, void *data, struct fsck_options *options) { if (!obj) return -1; @@ -81,11 +81,11 @@ int fsck_walk(struct object *obj, fsck_walk_func walk, void *data) case OBJ_BLOB: return 0; case OBJ_TREE: - return fsck_walk_tree((struct tree *)obj, walk, data); + return fsck_walk_tree((struct tree *)obj, data, options); case OBJ_COMMIT: - return fsck_walk_commit((struct commit *)obj, walk, data); + return fsck_walk_commit((struct commit *)obj, data, options); case OBJ_TAG: - return fsck_walk_tag((struct tag *)obj, walk, data); + return fsck_walk_tag((struct tag *)obj, data, options); default: error("Unknown object type for %s", sha1_to_hex(obj->sha1)); return -1; @@ -138,7 +138,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con return c1 < c2 ? 0 : TREE_UNORDERED; } -static int fsck_tree(struct tree *item, int strict, fsck_error error_func) +static int fsck_tree(struct tree *item, struct fsck_options *options) { int retval; int has_null_sha1 = 0; @@ -194,7 +194,7 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) * bits.. */ case S_IFREG | 0664: - if (!strict) + if (!options->strict) break; default: has_bad_modes = 1; @@ -219,30 +219,30 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) retval = 0; if (has_null_sha1) - retval += error_func(&item->object, FSCK_WARN, "contains entries pointing to null sha1"); + retval += options->error_func(&item->object, FSCK_WARN, "contains entries pointing to null sha1"); if (has_full_path) - retval += error_func(&item->object, FSCK_WARN, "contains full pathnames"); + retval += options->error_func(&item->object, FSCK_WARN, "contains full pathnames"); if (has_empty_name) - retval += error_func(&item->object, FSCK_WARN, "contains empty pathname"); + retval += options->error_func(&item->object, FSCK_WARN, "contains empty pathname"); if (has_dot) - retval += error_func(&item->object, FSCK_WARN, "contains '.'"); + retval += options->error_func(&item->object, FSCK_WARN, "contains '.'"); if (has_dotdot) - retval += error_func(&item->object, FSCK_WARN, "contains '..'"); + retval += options->error_func(&item->object, FSCK_WARN, "contains '..'"); if (has_dotgit) - retval += error_func(&item->object, FSCK_WARN, "contains '.git'"); + retval += options->error_func(&item->object, FSCK_WARN, "contains '.git'"); if (has_zero_pad) - retval += error_func(&item->object, FSCK_WARN, "contains zero-padded file modes"); + retval += options->error_func(&item->object, FSCK_WARN, "contains zero-padded file modes"); if (has_bad_modes) - retval += error_func(&item->object, FSCK_WARN, "contains bad file modes"); + retval += options->error_func(&item->object, FSCK_WARN, "contains bad file modes"); if (has_dup_entries) - retval += error_func(&item->object, FSCK_ERROR, "contains duplicate file entries"); + retval += options->error_func(&item->object, FSCK_ERROR, "contains duplicate file entries"); if (not_properly_sorted) - retval += error_func(&item->object, FSCK_ERROR, "not properly sorted"); + retval += options->error_func(&item->object, FSCK_ERROR, "not properly sorted"); return retval; } static int require_end_of_header(const void *data, unsigned long size, - struct object *obj, fsck_error error_func) + struct object *obj, struct fsck_options *options) { const char *buffer = (const char *)data; unsigned long i; @@ -250,7 +250,7 @@ static int require_end_of_header(const void *data, unsigned long size, for (i = 0; i < size; i++) { switch (buffer[i]) { case '\0': - return error_func(obj, FSCK_ERROR, + return options->error_func(obj, FSCK_ERROR, "unterminated header: NUL at offset %d", i); case '\n': if (i + 1 < size && buffer[i + 1] == '\n') @@ -258,36 +258,36 @@ static int require_end_of_header(const void *data, unsigned long size, } } - return error_func(obj, FSCK_ERROR, "unterminated header"); + return options->error_func(obj, FSCK_ERROR, "unterminated header"); } -static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func) +static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options) { char *end; if (**ident == '<') - return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email"); *ident += strcspn(*ident, "<>\n"); if (**ident == '>') - return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad name"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad name"); if (**ident != '<') - return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing email"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing email"); if ((*ident)[-1] != ' ') - return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email"); (*ident)++; *ident += strcspn(*ident, "<>\n"); if (**ident != '>') - return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad email"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad email"); (*ident)++; if (**ident != ' ') - return error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before date"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before date"); (*ident)++; if (**ident == '0' && (*ident)[1] != ' ') - return error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date"); if (date_overflows(strtoul(*ident, &end, 10))) - return error_func(obj, FSCK_ERROR, "invalid author/committer line - date causes integer overflow"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - date causes integer overflow"); if (end == *ident || *end != ' ') - return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date"); *ident = end + 1; if ((**ident != '+' && **ident != '-') || !isdigit((*ident)[1]) || @@ -295,30 +295,30 @@ static int fsck_ident(const char **ident, struct object *obj, fsck_error error_f !isdigit((*ident)[3]) || !isdigit((*ident)[4]) || ((*ident)[5] != '\n')) - return error_func(obj, FSCK_ERROR, "invalid author/committer line - bad time zone"); + return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad time zone"); (*ident) += 6; return 0; } static int fsck_commit_buffer(struct commit *commit, const char *buffer, - unsigned long size, fsck_error error_func) + unsigned long size, struct fsck_options *options) { unsigned char tree_sha1[20], sha1[20]; struct commit_graft *graft; unsigned parent_count, parent_line_count = 0; int err; - if (require_end_of_header(buffer, size, &commit->object, error_func)) + if (require_end_of_header(buffer, size, &commit->object, options)) return -1; if (!skip_prefix(buffer, "tree ", &buffer)) - return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line"); + return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line"); if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n') - return error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1"); + return options->error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1"); buffer += 41; while (skip_prefix(buffer, "parent ", &buffer)) { if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') - return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1"); + return options->error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1"); buffer += 41; parent_line_count++; } @@ -328,39 +328,39 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, if (graft->nr_parent == -1 && !parent_count) ; /* shallow commit */ else if (graft->nr_parent != parent_count) - return error_func(&commit->object, FSCK_ERROR, "graft objects missing"); + return options->error_func(&commit->object, FSCK_ERROR, "graft objects missing"); } else { if (parent_count != parent_line_count) - return error_func(&commit->object, FSCK_ERROR, "parent objects missing"); + return options->error_func(&commit->object, FSCK_ERROR, "parent objects missing"); } if (!skip_prefix(buffer, "author ", &buffer)) - return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line"); - err = fsck_ident(&buffer, &commit->object, error_func); + return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line"); + err = fsck_ident(&buffer, &commit->object, options); if (err) return err; if (!skip_prefix(buffer, "committer ", &buffer)) - return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line"); - err = fsck_ident(&buffer, &commit->object, error_func); + return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line"); + err = fsck_ident(&buffer, &commit->object, options); if (err) return err; if (!commit->tree) - return error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1)); + return options->error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1)); return 0; } static int fsck_commit(struct commit *commit, const char *data, - unsigned long size, fsck_error error_func) + unsigned long size, struct fsck_options *options) { const char *buffer = data ? data : get_commit_buffer(commit, &size); - int ret = fsck_commit_buffer(commit, buffer, size, error_func); + int ret = fsck_commit_buffer(commit, buffer, size, options); if (!data) unuse_commit_buffer(commit, buffer); return ret; } static int fsck_tag_buffer(struct tag *tag, const char *data, - unsigned long size, fsck_error error_func) + unsigned long size, struct fsck_options *options) { unsigned char sha1[20]; int ret = 0; @@ -376,65 +376,65 @@ static int fsck_tag_buffer(struct tag *tag, const char *data, buffer = to_free = read_sha1_file(tag->object.sha1, &type, &size); if (!buffer) - return error_func(&tag->object, FSCK_ERROR, + return options->error_func(&tag->object, FSCK_ERROR, "cannot read tag object"); if (type != OBJ_TAG) { - ret = error_func(&tag->object, FSCK_ERROR, + ret = options->error_func(&tag->object, FSCK_ERROR, "expected tag got %s", typename(type)); goto done; } } - if (require_end_of_header(buffer, size, &tag->object, error_func)) + if (require_end_of_header(buffer, size, &tag->object, options)) goto done; if (!skip_prefix(buffer, "object ", &buffer)) { - ret = error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'object' line"); + ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'object' line"); goto done; } if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') { - ret = error_func(&tag->object, FSCK_ERROR, "invalid 'object' line format - bad sha1"); + ret = options->error_func(&tag->object, FSCK_ERROR, "invalid 'object' line format - bad sha1"); goto done; } buffer += 41; if (!skip_prefix(buffer, "type ", &buffer)) { - ret = error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'type' line"); + ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'type' line"); goto done; } eol = strchr(buffer, '\n'); if (!eol) { - ret = error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line"); + ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line"); goto done; } if (type_from_string_gently(buffer, eol - buffer, 1) < 0) - ret = error_func(&tag->object, FSCK_ERROR, "invalid 'type' value"); + ret = options->error_func(&tag->object, FSCK_ERROR, "invalid 'type' value"); if (ret) goto done; buffer = eol + 1; if (!skip_prefix(buffer, "tag ", &buffer)) { - ret = error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'tag' line"); + ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'tag' line"); goto done; } eol = strchr(buffer, '\n'); if (!eol) { - ret = error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line"); + ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line"); goto done; } strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer); if (check_refname_format(sb.buf, 0)) - error_func(&tag->object, FSCK_WARN, "invalid 'tag' name: %.*s", + options->error_func(&tag->object, FSCK_WARN, "invalid 'tag' name: %.*s", (int)(eol - buffer), buffer); buffer = eol + 1; if (!skip_prefix(buffer, "tagger ", &buffer)) /* early tags do not contain 'tagger' lines; warn only */ - error_func(&tag->object, FSCK_WARN, "invalid format - expected 'tagger' line"); + options->error_func(&tag->object, FSCK_WARN, "invalid format - expected 'tagger' line"); else - ret = fsck_ident(&buffer, &tag->object, error_func); + ret = fsck_ident(&buffer, &tag->object, options); done: strbuf_release(&sb); @@ -443,34 +443,34 @@ done: } static int fsck_tag(struct tag *tag, const char *data, - unsigned long size, fsck_error error_func) + unsigned long size, struct fsck_options *options) { struct object *tagged = tag->tagged; if (!tagged) - return error_func(&tag->object, FSCK_ERROR, "could not load tagged object"); + return options->error_func(&tag->object, FSCK_ERROR, "could not load tagged object"); - return fsck_tag_buffer(tag, data, size, error_func); + return fsck_tag_buffer(tag, data, size, options); } int fsck_object(struct object *obj, void *data, unsigned long size, - int strict, fsck_error error_func) + struct fsck_options *options) { if (!obj) - return error_func(obj, FSCK_ERROR, "no valid object to fsck"); + return options->error_func(obj, FSCK_ERROR, "no valid object to fsck"); if (obj->type == OBJ_BLOB) return 0; if (obj->type == OBJ_TREE) - return fsck_tree((struct tree *) obj, strict, error_func); + return fsck_tree((struct tree *) obj, options); if (obj->type == OBJ_COMMIT) return fsck_commit((struct commit *) obj, (const char *) data, - size, error_func); + size, options); if (obj->type == OBJ_TAG) return fsck_tag((struct tag *) obj, (const char *) data, - size, error_func); + size, options); - return error_func(obj, FSCK_ERROR, "unknown type '%d' (internal fsck error)", + return options->error_func(obj, FSCK_ERROR, "unknown type '%d' (internal fsck error)", obj->type); } diff --git a/fsck.h b/fsck.h index d1e6387..07d0ab2 100644 --- a/fsck.h +++ b/fsck.h @@ -4,6 +4,8 @@ #define FSCK_ERROR 1 #define FSCK_WARN 2 +struct fsck_options; + /* * callback function for fsck_walk * type is the expected type of the object or OBJ_ANY @@ -12,7 +14,7 @@ * <0 error signaled and abort * >0 error signaled and do not abort */ -typedef int (*fsck_walk_func)(struct object *obj, int type, void *data); +typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options); /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...); @@ -20,6 +22,15 @@ typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...); __attribute__((format (printf, 3, 4))) int fsck_error_function(struct object *obj, int type, const char *fmt, ...); +struct fsck_options { + fsck_walk_func walk; + fsck_error error_func; + unsigned strict:1; +}; + +#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 } +#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 } + /* descend in all linked child objects * the return value is: * -1 error in processing the object @@ -27,9 +38,9 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...); * >0 return value of the first signaled error >0 (in the case of no other errors) * 0 everything OK */ -int fsck_walk(struct object *obj, fsck_walk_func walk, void *data); +int fsck_walk(struct object *obj, void *data, struct fsck_options *options); /* If NULL is passed for data, we assume the object is local and read it. */ int fsck_object(struct object *obj, void *data, unsigned long size, - int strict, fsck_error error_func); + struct fsck_options *options); #endif -- cgit v0.10.2-6-g49f6 From c99ba492f1cc3e632ed12d46d7358effb6fa7f03 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:25:09 +0200 Subject: fsck: introduce identifiers for fsck messages Instead of specifying whether a message by the fsck machinery constitutes an error or a warning, let's specify an identifier relating to the concrete problem that was encountered. This is necessary for upcoming support to be able to demote certain errors to warnings. In the process, simplify the requirements on the calling code: instead of having to handle full-blown varargs in every callback, we now send a string buffer ready to be used by the callback. We could use a simple enum for the message IDs here, but we want to guarantee that the enum values are associated with the appropriate message types (i.e. error or warning?). Besides, we want to introduce a parser in the next commit that maps the string representation to the enum value, hence we use the slightly ugly preprocessor construct that is extensible for use with said parser. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin/fsck.c b/builtin/fsck.c index 6f11fbc..cea623d 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -46,33 +46,23 @@ static int show_dangling = 1; #define DIRENT_SORT_HINT(de) ((de)->d_ino) #endif -static void objreport(struct object *obj, const char *severity, - const char *err, va_list params) +static void objreport(struct object *obj, const char *msg_type, + const char *err) { - fprintf(stderr, "%s in %s %s: ", - severity, typename(obj->type), sha1_to_hex(obj->sha1)); - vfprintf(stderr, err, params); - fputs("\n", stderr); + fprintf(stderr, "%s in %s %s: %s\n", + msg_type, typename(obj->type), sha1_to_hex(obj->sha1), err); } -__attribute__((format (printf, 2, 3))) -static int objerror(struct object *obj, const char *err, ...) +static int objerror(struct object *obj, const char *err) { - va_list params; - va_start(params, err); errors_found |= ERROR_OBJECT; - objreport(obj, "error", err, params); - va_end(params); + objreport(obj, "error", err); return -1; } -__attribute__((format (printf, 3, 4))) -static int fsck_error_func(struct object *obj, int type, const char *err, ...) +static int fsck_error_func(struct object *obj, int type, const char *message) { - va_list params; - va_start(params, err); - objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params); - va_end(params); + objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message); return (type == FSCK_WARN) ? 0 : 1; } diff --git a/fsck.c b/fsck.c index d83b811..ab24618 100644 --- a/fsck.c +++ b/fsck.c @@ -9,6 +9,98 @@ #include "refs.h" #include "utf8.h" +#define FOREACH_MSG_ID(FUNC) \ + /* errors */ \ + FUNC(BAD_DATE, ERROR) \ + FUNC(BAD_DATE_OVERFLOW, ERROR) \ + FUNC(BAD_EMAIL, ERROR) \ + FUNC(BAD_NAME, ERROR) \ + FUNC(BAD_OBJECT_SHA1, ERROR) \ + FUNC(BAD_PARENT_SHA1, ERROR) \ + FUNC(BAD_TAG_OBJECT, ERROR) \ + FUNC(BAD_TIMEZONE, ERROR) \ + FUNC(BAD_TREE, ERROR) \ + FUNC(BAD_TREE_SHA1, ERROR) \ + FUNC(BAD_TYPE, ERROR) \ + FUNC(DUPLICATE_ENTRIES, ERROR) \ + FUNC(MISSING_AUTHOR, ERROR) \ + FUNC(MISSING_COMMITTER, ERROR) \ + FUNC(MISSING_EMAIL, ERROR) \ + FUNC(MISSING_GRAFT, ERROR) \ + FUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \ + FUNC(MISSING_OBJECT, ERROR) \ + FUNC(MISSING_PARENT, ERROR) \ + FUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \ + FUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \ + FUNC(MISSING_TAG, ERROR) \ + FUNC(MISSING_TAG_ENTRY, ERROR) \ + FUNC(MISSING_TAG_OBJECT, ERROR) \ + FUNC(MISSING_TREE, ERROR) \ + FUNC(MISSING_TYPE, ERROR) \ + FUNC(MISSING_TYPE_ENTRY, ERROR) \ + FUNC(NUL_IN_HEADER, ERROR) \ + FUNC(TAG_OBJECT_NOT_TAG, ERROR) \ + FUNC(TREE_NOT_SORTED, ERROR) \ + FUNC(UNKNOWN_TYPE, ERROR) \ + FUNC(UNTERMINATED_HEADER, ERROR) \ + FUNC(ZERO_PADDED_DATE, ERROR) \ + /* warnings */ \ + FUNC(BAD_FILEMODE, WARN) \ + FUNC(BAD_TAG_NAME, WARN) \ + FUNC(EMPTY_NAME, WARN) \ + FUNC(FULL_PATHNAME, WARN) \ + FUNC(HAS_DOT, WARN) \ + FUNC(HAS_DOTDOT, WARN) \ + FUNC(HAS_DOTGIT, WARN) \ + FUNC(MISSING_TAGGER_ENTRY, WARN) \ + FUNC(NULL_SHA1, WARN) \ + FUNC(ZERO_PADDED_FILEMODE, WARN) + +#define MSG_ID(id, msg_type) FSCK_MSG_##id, +enum fsck_msg_id { + FOREACH_MSG_ID(MSG_ID) + FSCK_MSG_MAX +}; +#undef MSG_ID + +#define MSG_ID(id, msg_type) { FSCK_##msg_type }, +static struct { + int msg_type; +} msg_id_info[FSCK_MSG_MAX + 1] = { + FOREACH_MSG_ID(MSG_ID) + { -1 } +}; +#undef MSG_ID + +static int fsck_msg_type(enum fsck_msg_id msg_id, + struct fsck_options *options) +{ + int msg_type; + + msg_type = msg_id_info[msg_id].msg_type; + if (options->strict && msg_type == FSCK_WARN) + msg_type = FSCK_ERROR; + + return msg_type; +} + +__attribute__((format (printf, 4, 5))) +static int report(struct fsck_options *options, struct object *object, + enum fsck_msg_id id, const char *fmt, ...) +{ + va_list ap; + struct strbuf sb = STRBUF_INIT; + int msg_type = fsck_msg_type(id, options), result; + + va_start(ap, fmt); + strbuf_vaddf(&sb, fmt, ap); + result = options->error_func(object, msg_type, sb.buf); + strbuf_release(&sb); + va_end(ap); + + return result; +} + static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options) { struct tree_desc desc; @@ -219,25 +311,25 @@ static int fsck_tree(struct tree *item, struct fsck_options *options) retval = 0; if (has_null_sha1) - retval += options->error_func(&item->object, FSCK_WARN, "contains entries pointing to null sha1"); + retval += report(options, &item->object, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1"); if (has_full_path) - retval += options->error_func(&item->object, FSCK_WARN, "contains full pathnames"); + retval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, "contains full pathnames"); if (has_empty_name) - retval += options->error_func(&item->object, FSCK_WARN, "contains empty pathname"); + retval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, "contains empty pathname"); if (has_dot) - retval += options->error_func(&item->object, FSCK_WARN, "contains '.'"); + retval += report(options, &item->object, FSCK_MSG_HAS_DOT, "contains '.'"); if (has_dotdot) - retval += options->error_func(&item->object, FSCK_WARN, "contains '..'"); + retval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, "contains '..'"); if (has_dotgit) - retval += options->error_func(&item->object, FSCK_WARN, "contains '.git'"); + retval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, "contains '.git'"); if (has_zero_pad) - retval += options->error_func(&item->object, FSCK_WARN, "contains zero-padded file modes"); + retval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes"); if (has_bad_modes) - retval += options->error_func(&item->object, FSCK_WARN, "contains bad file modes"); + retval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, "contains bad file modes"); if (has_dup_entries) - retval += options->error_func(&item->object, FSCK_ERROR, "contains duplicate file entries"); + retval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries"); if (not_properly_sorted) - retval += options->error_func(&item->object, FSCK_ERROR, "not properly sorted"); + retval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted"); return retval; } @@ -250,15 +342,17 @@ static int require_end_of_header(const void *data, unsigned long size, for (i = 0; i < size; i++) { switch (buffer[i]) { case '\0': - return options->error_func(obj, FSCK_ERROR, - "unterminated header: NUL at offset %d", i); + return report(options, obj, + FSCK_MSG_NUL_IN_HEADER, + "unterminated header: NUL at offset %ld", i); case '\n': if (i + 1 < size && buffer[i + 1] == '\n') return 0; } } - return options->error_func(obj, FSCK_ERROR, "unterminated header"); + return report(options, obj, + FSCK_MSG_UNTERMINATED_HEADER, "unterminated header"); } static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options) @@ -266,28 +360,28 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option char *end; if (**ident == '<') - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email"); + return report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email"); *ident += strcspn(*ident, "<>\n"); if (**ident == '>') - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad name"); + return report(options, obj, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name"); if (**ident != '<') - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing email"); + return report(options, obj, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email"); if ((*ident)[-1] != ' ') - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before email"); + return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email"); (*ident)++; *ident += strcspn(*ident, "<>\n"); if (**ident != '>') - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad email"); + return report(options, obj, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email"); (*ident)++; if (**ident != ' ') - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - missing space before date"); + return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date"); (*ident)++; if (**ident == '0' && (*ident)[1] != ' ') - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - zero-padded date"); + return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date"); if (date_overflows(strtoul(*ident, &end, 10))) - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - date causes integer overflow"); + return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow"); if (end == *ident || *end != ' ') - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad date"); + return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date"); *ident = end + 1; if ((**ident != '+' && **ident != '-') || !isdigit((*ident)[1]) || @@ -295,7 +389,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option !isdigit((*ident)[3]) || !isdigit((*ident)[4]) || ((*ident)[5] != '\n')) - return options->error_func(obj, FSCK_ERROR, "invalid author/committer line - bad time zone"); + return report(options, obj, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone"); (*ident) += 6; return 0; } @@ -312,13 +406,13 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, return -1; if (!skip_prefix(buffer, "tree ", &buffer)) - return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line"); + return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line"); if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n') - return options->error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1"); + return report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"); buffer += 41; while (skip_prefix(buffer, "parent ", &buffer)) { if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') - return options->error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1"); + return report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"); buffer += 41; parent_line_count++; } @@ -328,23 +422,23 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, if (graft->nr_parent == -1 && !parent_count) ; /* shallow commit */ else if (graft->nr_parent != parent_count) - return options->error_func(&commit->object, FSCK_ERROR, "graft objects missing"); + return report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, "graft objects missing"); } else { if (parent_count != parent_line_count) - return options->error_func(&commit->object, FSCK_ERROR, "parent objects missing"); + return report(options, &commit->object, FSCK_MSG_MISSING_PARENT, "parent objects missing"); } if (!skip_prefix(buffer, "author ", &buffer)) - return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line"); + return report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line"); err = fsck_ident(&buffer, &commit->object, options); if (err) return err; if (!skip_prefix(buffer, "committer ", &buffer)) - return options->error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line"); + return report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line"); err = fsck_ident(&buffer, &commit->object, options); if (err) return err; if (!commit->tree) - return options->error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1)); + return report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1)); return 0; } @@ -376,11 +470,13 @@ static int fsck_tag_buffer(struct tag *tag, const char *data, buffer = to_free = read_sha1_file(tag->object.sha1, &type, &size); if (!buffer) - return options->error_func(&tag->object, FSCK_ERROR, + return report(options, &tag->object, + FSCK_MSG_MISSING_TAG_OBJECT, "cannot read tag object"); if (type != OBJ_TAG) { - ret = options->error_func(&tag->object, FSCK_ERROR, + ret = report(options, &tag->object, + FSCK_MSG_TAG_OBJECT_NOT_TAG, "expected tag got %s", typename(type)); goto done; @@ -391,48 +487,49 @@ static int fsck_tag_buffer(struct tag *tag, const char *data, goto done; if (!skip_prefix(buffer, "object ", &buffer)) { - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'object' line"); + ret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line"); goto done; } if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') { - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid 'object' line format - bad sha1"); + ret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1"); goto done; } buffer += 41; if (!skip_prefix(buffer, "type ", &buffer)) { - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'type' line"); + ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line"); goto done; } eol = strchr(buffer, '\n'); if (!eol) { - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line"); + ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line"); goto done; } if (type_from_string_gently(buffer, eol - buffer, 1) < 0) - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid 'type' value"); + ret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, "invalid 'type' value"); if (ret) goto done; buffer = eol + 1; if (!skip_prefix(buffer, "tag ", &buffer)) { - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - expected 'tag' line"); + ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line"); goto done; } eol = strchr(buffer, '\n'); if (!eol) { - ret = options->error_func(&tag->object, FSCK_ERROR, "invalid format - unexpected end after 'type' line"); + ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line"); goto done; } strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer); if (check_refname_format(sb.buf, 0)) - options->error_func(&tag->object, FSCK_WARN, "invalid 'tag' name: %.*s", + report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME, + "invalid 'tag' name: %.*s", (int)(eol - buffer), buffer); buffer = eol + 1; if (!skip_prefix(buffer, "tagger ", &buffer)) /* early tags do not contain 'tagger' lines; warn only */ - options->error_func(&tag->object, FSCK_WARN, "invalid format - expected 'tagger' line"); + report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line"); else ret = fsck_ident(&buffer, &tag->object, options); @@ -448,7 +545,7 @@ static int fsck_tag(struct tag *tag, const char *data, struct object *tagged = tag->tagged; if (!tagged) - return options->error_func(&tag->object, FSCK_ERROR, "could not load tagged object"); + return report(options, &tag->object, FSCK_MSG_BAD_TAG_OBJECT, "could not load tagged object"); return fsck_tag_buffer(tag, data, size, options); } @@ -457,7 +554,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size, struct fsck_options *options) { if (!obj) - return options->error_func(obj, FSCK_ERROR, "no valid object to fsck"); + return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck"); if (obj->type == OBJ_BLOB) return 0; @@ -470,22 +567,12 @@ int fsck_object(struct object *obj, void *data, unsigned long size, return fsck_tag((struct tag *) obj, (const char *) data, size, options); - return options->error_func(obj, FSCK_ERROR, "unknown type '%d' (internal fsck error)", + return report(options, obj, FSCK_MSG_UNKNOWN_TYPE, "unknown type '%d' (internal fsck error)", obj->type); } -int fsck_error_function(struct object *obj, int type, const char *fmt, ...) +int fsck_error_function(struct object *obj, int msg_type, const char *message) { - va_list ap; - struct strbuf sb = STRBUF_INIT; - - strbuf_addf(&sb, "object %s:", sha1_to_hex(obj->sha1)); - - va_start(ap, fmt); - strbuf_vaddf(&sb, fmt, ap); - va_end(ap); - - error("%s", sb.buf); - strbuf_release(&sb); + error("object %s: %s", sha1_to_hex(obj->sha1), message); return 1; } diff --git a/fsck.h b/fsck.h index 07d0ab2..f6f268a 100644 --- a/fsck.h +++ b/fsck.h @@ -17,10 +17,9 @@ struct fsck_options; typedef int (*fsck_walk_func)(struct object *obj, int type, void *data, struct fsck_options *options); /* callback for fsck_object, type is FSCK_ERROR or FSCK_WARN */ -typedef int (*fsck_error)(struct object *obj, int type, const char *err, ...); +typedef int (*fsck_error)(struct object *obj, int type, const char *message); -__attribute__((format (printf, 3, 4))) -int fsck_error_function(struct object *obj, int type, const char *fmt, ...); +int fsck_error_function(struct object *obj, int type, const char *message); struct fsck_options { fsck_walk_func walk; -- cgit v0.10.2-6-g49f6 From f417eed8cde2e7def06091977f888c296cbeae00 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:25:14 +0200 Subject: fsck: provide a function to parse fsck message IDs These functions will be used in the next commits to allow the user to ask fsck to handle specific problems differently, e.g. demoting certain errors to warnings. The upcoming `fsck_set_msg_types()` function has to handle partial strings because we would like to be able to parse, say, 'missingemail=warn,missingtaggerentry=warn' command line parameters (which will be passed by receive-pack to index-pack and unpack-objects). To make the parsing robust, we generate strings from the enum keys, and using these keys, we match up strings without dashes case-insensitively to the corresponding enum values. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index ab24618..30c1a19 100644 --- a/fsck.c +++ b/fsck.c @@ -63,15 +63,46 @@ enum fsck_msg_id { }; #undef MSG_ID -#define MSG_ID(id, msg_type) { FSCK_##msg_type }, +#define STR(x) #x +#define MSG_ID(id, msg_type) { STR(id), NULL, FSCK_##msg_type }, static struct { + const char *id_string; + const char *downcased; int msg_type; } msg_id_info[FSCK_MSG_MAX + 1] = { FOREACH_MSG_ID(MSG_ID) - { -1 } + { NULL, NULL, -1 } }; #undef MSG_ID +static int parse_msg_id(const char *text) +{ + int i; + + if (!msg_id_info[0].downcased) { + /* convert id_string to lower case, without underscores. */ + for (i = 0; i < FSCK_MSG_MAX; i++) { + const char *p = msg_id_info[i].id_string; + int len = strlen(p); + char *q = xmalloc(len); + + msg_id_info[i].downcased = q; + while (*p) + if (*p == '_') + p++; + else + *(q)++ = tolower(*(p)++); + *q = '\0'; + } + } + + for (i = 0; i < FSCK_MSG_MAX; i++) + if (!strcmp(text, msg_id_info[i].downcased)) + return i; + + return -1; +} + static int fsck_msg_type(enum fsck_msg_id msg_id, struct fsck_options *options) { -- cgit v0.10.2-6-g49f6 From 0282f4dced029230024196e460b9d9f971f494dd Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:25:25 +0200 Subject: fsck: offer a function to demote fsck errors to warnings There are legacy repositories out there whose older commits and tags have issues that prevent pushing them when 'receive.fsckObjects' is set. One real-life example is a commit object that has been hand-crafted to list two authors. Often, it is not possible to fix those issues without disrupting the work with said repositories, yet it is still desirable to perform checks by setting `receive.fsckObjects = true`. This commit is the first step to allow demoting specific fsck issues to mere warnings. The `fsck_set_msg_types()` function added by this commit parses a list of settings in the form: missingemail=warn,badname=warn,... Unfortunately, the FSCK_WARN/FSCK_ERROR flag is only really heeded by git fsck so far, but other call paths (e.g. git index-pack --strict) error out *always* no matter what type was specified. Therefore, we need to take extra care to set all message types to FSCK_ERROR by default in those cases. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index 30c1a19..41208a4 100644 --- a/fsck.c +++ b/fsck.c @@ -108,13 +108,79 @@ static int fsck_msg_type(enum fsck_msg_id msg_id, { int msg_type; - msg_type = msg_id_info[msg_id].msg_type; - if (options->strict && msg_type == FSCK_WARN) - msg_type = FSCK_ERROR; + assert(msg_id >= 0 && msg_id < FSCK_MSG_MAX); + + if (options->msg_type) + msg_type = options->msg_type[msg_id]; + else { + msg_type = msg_id_info[msg_id].msg_type; + if (options->strict && msg_type == FSCK_WARN) + msg_type = FSCK_ERROR; + } return msg_type; } +static int parse_msg_type(const char *str) +{ + if (!strcmp(str, "error")) + return FSCK_ERROR; + else if (!strcmp(str, "warn")) + return FSCK_WARN; + else + die("Unknown fsck message type: '%s'", str); +} + +void fsck_set_msg_type(struct fsck_options *options, + const char *msg_id, const char *msg_type) +{ + int id = parse_msg_id(msg_id), type; + + if (id < 0) + die("Unhandled message id: %s", msg_id); + type = parse_msg_type(msg_type); + + if (!options->msg_type) { + int i; + int *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX); + for (i = 0; i < FSCK_MSG_MAX; i++) + msg_type[i] = fsck_msg_type(i, options); + options->msg_type = msg_type; + } + + options->msg_type[id] = type; +} + +void fsck_set_msg_types(struct fsck_options *options, const char *values) +{ + char *buf = xstrdup(values), *to_free = buf; + int done = 0; + + while (!done) { + int len = strcspn(buf, " ,|"), equal; + + done = !buf[len]; + if (!len) { + buf++; + continue; + } + buf[len] = '\0'; + + for (equal = 0; + equal < len && buf[equal] != '=' && buf[equal] != ':'; + equal++) + buf[equal] = tolower(buf[equal]); + buf[equal] = '\0'; + + if (equal == len) + die("Missing '=': '%s'", buf); + + fsck_set_msg_type(options, buf, buf + equal + 1); + buf += len + 1; + } + free(to_free); +} + __attribute__((format (printf, 4, 5))) static int report(struct fsck_options *options, struct object *object, enum fsck_msg_id id, const char *fmt, ...) @@ -604,6 +670,10 @@ int fsck_object(struct object *obj, void *data, unsigned long size, int fsck_error_function(struct object *obj, int msg_type, const char *message) { + if (msg_type == FSCK_WARN) { + warning("object %s: %s", sha1_to_hex(obj->sha1), message); + return 0; + } error("object %s: %s", sha1_to_hex(obj->sha1), message); return 1; } diff --git a/fsck.h b/fsck.h index f6f268a..af3c84e 100644 --- a/fsck.h +++ b/fsck.h @@ -6,6 +6,10 @@ struct fsck_options; +void fsck_set_msg_type(struct fsck_options *options, + const char *msg_id, const char *msg_type); +void fsck_set_msg_types(struct fsck_options *options, const char *values); + /* * callback function for fsck_walk * type is the expected type of the object or OBJ_ANY @@ -25,10 +29,11 @@ struct fsck_options { fsck_walk_func walk; fsck_error error_func; unsigned strict:1; + int *msg_type; }; -#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0 } -#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1 } +#define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL } +#define FSCK_OPTIONS_STRICT { NULL, fsck_error_function, 1, NULL } /* descend in all linked child objects * the return value is: -- cgit v0.10.2-6-g49f6 From 5d477a334a68698709f07ebda4999c10997ef6f7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:25:31 +0200 Subject: fsck (receive-pack): allow demoting errors to warnings For example, missing emails in commit and tag objects can be demoted to mere warnings with git config receive.fsck.missingemail=warn The value is actually a comma-separated list. In case that the same key is listed in multiple receive.fsck. lines in the config, the latter configuration wins (this can happen for example when both $HOME/.gitconfig and .git/config contain message type settings). As git receive-pack does not actually perform the checks, it hands off the setting to index-pack or unpack-objects in the form of an optional argument to the --strict option. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 627eb3f..67b4bef 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1633,6 +1633,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) } else if (!strcmp(arg, "--strict")) { strict = 1; do_fsck_object = 1; + } else if (skip_prefix(arg, "--strict=", &arg)) { + strict = 1; + do_fsck_object = 1; + fsck_set_msg_types(&fsck_options, arg); } else if (!strcmp(arg, "--check-self-contained-and-connected")) { strict = 1; check_self_contained_and_connected = 1; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 94d0571..3afe8f8 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -19,6 +19,7 @@ #include "tag.h" #include "gpg-interface.h" #include "sigchain.h" +#include "fsck.h" static const char receive_pack_usage[] = "git receive-pack "; @@ -36,6 +37,7 @@ static enum deny_action deny_current_branch = DENY_UNCONFIGURED; static enum deny_action deny_delete_current = DENY_UNCONFIGURED; static int receive_fsck_objects = -1; static int transfer_fsck_objects = -1; +static struct strbuf fsck_msg_types = STRBUF_INIT; static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; static int advertise_atomic_push = 1; @@ -115,6 +117,15 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (skip_prefix(var, "receive.fsck.", &var)) { + if (is_valid_msg_type(var, value)) + strbuf_addf(&fsck_msg_types, "%c%s=%s", + fsck_msg_types.len ? ',' : '=', var, value); + else + warning("Skipping unknown msg id '%s'", var); + return 0; + } + if (strcmp(var, "receive.fsckobjects") == 0) { receive_fsck_objects = git_config_bool(var, value); return 0; @@ -1490,7 +1501,8 @@ static const char *unpack(int err_fd, struct shallow_info *si) if (quiet) argv_array_push(&child.args, "-q"); if (fsck_objects) - argv_array_push(&child.args, "--strict"); + argv_array_pushf(&child.args, "--strict%s", + fsck_msg_types.buf); child.no_stdout = 1; child.err = err_fd; child.git_cmd = 1; @@ -1508,7 +1520,8 @@ static const char *unpack(int err_fd, struct shallow_info *si) argv_array_pushl(&child.args, "index-pack", "--stdin", hdr_arg, keep_arg, NULL); if (fsck_objects) - argv_array_push(&child.args, "--strict"); + argv_array_pushf(&child.args, "--strict%s", + fsck_msg_types.buf); if (fix_thin) argv_array_push(&child.args, "--fix-thin"); child.out = -1; diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 6d17040..7cc086f 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -530,6 +530,11 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) strict = 1; continue; } + if (skip_prefix(arg, "--strict=", &arg)) { + strict = 1; + fsck_set_msg_types(&fsck_options, arg); + continue; + } if (starts_with(arg, "--pack_header=")) { struct pack_header *hdr; char *c; diff --git a/fsck.c b/fsck.c index 41208a4..e6e8dc4 100644 --- a/fsck.c +++ b/fsck.c @@ -131,6 +131,14 @@ static int parse_msg_type(const char *str) die("Unknown fsck message type: '%s'", str); } +int is_valid_msg_type(const char *msg_id, const char *msg_type) +{ + if (parse_msg_id(msg_id) < 0) + return 0; + parse_msg_type(msg_type); + return 1; +} + void fsck_set_msg_type(struct fsck_options *options, const char *msg_id, const char *msg_type) { diff --git a/fsck.h b/fsck.h index af3c84e..3ef92a3 100644 --- a/fsck.h +++ b/fsck.h @@ -9,6 +9,7 @@ struct fsck_options; void fsck_set_msg_type(struct fsck_options *options, const char *msg_id, const char *msg_type); void fsck_set_msg_types(struct fsck_options *options, const char *values); +int is_valid_msg_type(const char *msg_id, const char *msg_type); /* * callback function for fsck_walk -- cgit v0.10.2-6-g49f6 From 71ab8fa840fcca4b4f80acba6a529e3cbc174b65 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:25:52 +0200 Subject: fsck: report the ID of the error/warning Some repositories written by legacy code have objects with non-fatal fsck issues. To allow the user to ignore those issues, let's print out the ID (e.g. when encountering "missingEmail", the user might want to call `git config --add receive.fsck.missingEmail=warn`). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index e6e8dc4..4748f1e 100644 --- a/fsck.c +++ b/fsck.c @@ -189,6 +189,24 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values) free(to_free); } +static void append_msg_id(struct strbuf *sb, const char *msg_id) +{ + for (;;) { + char c = *(msg_id)++; + + if (!c) + break; + if (c != '_') + strbuf_addch(sb, tolower(c)); + else { + assert(*msg_id); + strbuf_addch(sb, *(msg_id)++); + } + } + + strbuf_addstr(sb, ": "); +} + __attribute__((format (printf, 4, 5))) static int report(struct fsck_options *options, struct object *object, enum fsck_msg_id id, const char *fmt, ...) @@ -197,6 +215,8 @@ static int report(struct fsck_options *options, struct object *object, struct strbuf sb = STRBUF_INIT; int msg_type = fsck_msg_type(id, options), result; + append_msg_id(&sb, msg_id_info[id].id_string); + va_start(ap, fmt); strbuf_vaddf(&sb, fmt, ap); result = options->error_func(object, msg_type, sb.buf); diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index cfb32b6..7c5b3d5 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -231,8 +231,8 @@ test_expect_success 'tag with incorrect tag name & missing tagger' ' git fsck --tags 2>out && cat >expect <<-EOF && - warning in tag $tag: invalid '\''tag'\'' name: wrong name format - warning in tag $tag: invalid format - expected '\''tagger'\'' line + warning in tag $tag: badTagName: invalid '\''tag'\'' name: wrong name format + warning in tag $tag: missingTaggerEntry: invalid format - expected '\''tagger'\'' line EOF test_cmp expect out ' -- cgit v0.10.2-6-g49f6 From e6826e335addb557dc80eebe46031f896ac4424c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:26:03 +0200 Subject: fsck: make fsck_ident() warn-friendly When fsck_ident() identifies a problem with the ident, it should still advance the pointer to the next line so that fsck can continue in the case of a mere warning. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index 4748f1e..8a6ecf3 100644 --- a/fsck.c +++ b/fsck.c @@ -482,40 +482,45 @@ static int require_end_of_header(const void *data, unsigned long size, static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options) { + const char *p = *ident; char *end; - if (**ident == '<') + *ident = strchrnul(*ident, '\n'); + if (**ident == '\n') + (*ident)++; + + if (*p == '<') return report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email"); - *ident += strcspn(*ident, "<>\n"); - if (**ident == '>') + p += strcspn(p, "<>\n"); + if (*p == '>') return report(options, obj, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name"); - if (**ident != '<') + if (*p != '<') return report(options, obj, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email"); - if ((*ident)[-1] != ' ') + if (p[-1] != ' ') return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email"); - (*ident)++; - *ident += strcspn(*ident, "<>\n"); - if (**ident != '>') + p++; + p += strcspn(p, "<>\n"); + if (*p != '>') return report(options, obj, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email"); - (*ident)++; - if (**ident != ' ') + p++; + if (*p != ' ') return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date"); - (*ident)++; - if (**ident == '0' && (*ident)[1] != ' ') + p++; + if (*p == '0' && p[1] != ' ') return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date"); - if (date_overflows(strtoul(*ident, &end, 10))) + if (date_overflows(strtoul(p, &end, 10))) return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow"); - if (end == *ident || *end != ' ') + if ((end == p || *end != ' ')) return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date"); - *ident = end + 1; - if ((**ident != '+' && **ident != '-') || - !isdigit((*ident)[1]) || - !isdigit((*ident)[2]) || - !isdigit((*ident)[3]) || - !isdigit((*ident)[4]) || - ((*ident)[5] != '\n')) + p = end + 1; + if ((*p != '+' && *p != '-') || + !isdigit(p[1]) || + !isdigit(p[2]) || + !isdigit(p[3]) || + !isdigit(p[4]) || + (p[5] != '\n')) return report(options, obj, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone"); - (*ident) += 6; + p += 6; return 0; } -- cgit v0.10.2-6-g49f6 From b3584761eb7284263e702088d71cc6f094fbb926 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:26:11 +0200 Subject: fsck: make fsck_commit() warn-friendly When fsck_commit() identifies a problem with the commit, it should try to make it possible to continue checking the commit object, in case the user wants to demote the detected errors to mere warnings. Note that some problems are too problematic to simply ignore. For example, when the header lines are mixed up, we punt after encountering an incorrect line. Therefore, demoting certain warnings to errors can hide other problems. Example: demoting the missingauthor error to a warning would hide a problematic committer line. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index 8a6ecf3..d3d81fc 100644 --- a/fsck.c +++ b/fsck.c @@ -537,12 +537,18 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, if (!skip_prefix(buffer, "tree ", &buffer)) return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line"); - if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n') - return report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"); + if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n') { + err = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"); + if (err) + return err; + } buffer += 41; while (skip_prefix(buffer, "parent ", &buffer)) { - if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') - return report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"); + if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') { + err = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"); + if (err) + return err; + } buffer += 41; parent_line_count++; } @@ -551,11 +557,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, if (graft) { if (graft->nr_parent == -1 && !parent_count) ; /* shallow commit */ - else if (graft->nr_parent != parent_count) - return report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, "graft objects missing"); + else if (graft->nr_parent != parent_count) { + err = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, "graft objects missing"); + if (err) + return err; + } } else { - if (parent_count != parent_line_count) - return report(options, &commit->object, FSCK_MSG_MISSING_PARENT, "parent objects missing"); + if (parent_count != parent_line_count) { + err = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, "parent objects missing"); + if (err) + return err; + } } if (!skip_prefix(buffer, "author ", &buffer)) return report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line"); -- cgit v0.10.2-6-g49f6 From c9ad147f83f70b6add9066e16e1e44c8bc3d5c13 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:26:23 +0200 Subject: fsck: handle multiple authors in commits specially This problem has been detected in the wild, and is the primary reason to introduce an option to demote certain fsck errors to warnings. Let's offer to ignore this particular problem specifically. Technically, we could handle such repositories by setting receive.fsck. to missingCommitter=warn, but that could hide missing tree objects in the same commit because we cannot continue verifying any commit object after encountering a missing committer line, while we can continue in the case of multiple author lines. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index d3d81fc..8dc5867 100644 --- a/fsck.c +++ b/fsck.c @@ -38,6 +38,7 @@ FUNC(MISSING_TREE, ERROR) \ FUNC(MISSING_TYPE, ERROR) \ FUNC(MISSING_TYPE_ENTRY, ERROR) \ + FUNC(MULTIPLE_AUTHORS, ERROR) \ FUNC(NUL_IN_HEADER, ERROR) \ FUNC(TAG_OBJECT_NOT_TAG, ERROR) \ FUNC(TREE_NOT_SORTED, ERROR) \ @@ -529,7 +530,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, { unsigned char tree_sha1[20], sha1[20]; struct commit_graft *graft; - unsigned parent_count, parent_line_count = 0; + unsigned parent_count, parent_line_count = 0, author_count; int err; if (require_end_of_header(buffer, size, &commit->object, options)) @@ -569,9 +570,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer, return err; } } - if (!skip_prefix(buffer, "author ", &buffer)) - return report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line"); - err = fsck_ident(&buffer, &commit->object, options); + author_count = 0; + while (skip_prefix(buffer, "author ", &buffer)) { + author_count++; + err = fsck_ident(&buffer, &commit->object, options); + if (err) + return err; + } + if (author_count < 1) + err = report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line"); + else if (author_count > 1) + err = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines"); if (err) return err; if (!skip_prefix(buffer, "committer ", &buffer)) -- cgit v0.10.2-6-g49f6 From 7d7d5b0568e49d9d0dd3b4909572fadca8fc3b06 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:26:30 +0200 Subject: fsck: make fsck_tag() warn-friendly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When fsck_tag() identifies a problem with the commit, it should try to make it possible to continue checking the commit object, in case the user wants to demote the detected errors to mere warnings. Just like fsck_commit(), there are certain problems that could hide other issues with the same tag object. For example, if the 'type' line is not encountered in the correct position, the 'tag' line – if there is any – would not be handled at all. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index 8dc5867..6635e15 100644 --- a/fsck.c +++ b/fsck.c @@ -643,7 +643,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data, } if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') { ret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1"); - goto done; + if (ret) + goto done; } buffer += 41; -- cgit v0.10.2-6-g49f6 From 70a4ae73d8a6d8d0ce86d746a20bb243d83d28e3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:26:36 +0200 Subject: fsck: add a simple test for receive.fsck. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 69ee13c..36024fc 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -115,4 +115,25 @@ test_expect_success 'push with transfer.fsckobjects' ' test_cmp exp act ' +cat >bogus-commit <<\EOF +tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 +author Bugs Bunny 1234567890 +0000 +committer Bugs Bunny 1234567890 +0000 + +This commit object intentionally broken +EOF + +test_expect_success 'push with receive.fsck.missingEmail=warn' ' + commit="$(git hash-object -t commit -w --stdin act 2>&1 && + grep "missingEmail" act +' + test_done -- cgit v0.10.2-6-g49f6 From f50c44073051820cd368be4ca520ae43f34fdf8b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:26:42 +0200 Subject: fsck: disallow demoting grave fsck errors to warnings Some kinds of errors are intrinsically unrecoverable (e.g. errors while uncompressing objects). It does not make sense to allow demoting them to mere warnings. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index 6635e15..a4205aa 100644 --- a/fsck.c +++ b/fsck.c @@ -9,7 +9,12 @@ #include "refs.h" #include "utf8.h" +#define FSCK_FATAL -1 + #define FOREACH_MSG_ID(FUNC) \ + /* fatal errors */ \ + FUNC(NUL_IN_HEADER, FATAL) \ + FUNC(UNTERMINATED_HEADER, FATAL) \ /* errors */ \ FUNC(BAD_DATE, ERROR) \ FUNC(BAD_DATE_OVERFLOW, ERROR) \ @@ -39,11 +44,9 @@ FUNC(MISSING_TYPE, ERROR) \ FUNC(MISSING_TYPE_ENTRY, ERROR) \ FUNC(MULTIPLE_AUTHORS, ERROR) \ - FUNC(NUL_IN_HEADER, ERROR) \ FUNC(TAG_OBJECT_NOT_TAG, ERROR) \ FUNC(TREE_NOT_SORTED, ERROR) \ FUNC(UNKNOWN_TYPE, ERROR) \ - FUNC(UNTERMINATED_HEADER, ERROR) \ FUNC(ZERO_PADDED_DATE, ERROR) \ /* warnings */ \ FUNC(BAD_FILEMODE, WARN) \ @@ -149,6 +152,9 @@ void fsck_set_msg_type(struct fsck_options *options, die("Unhandled message id: %s", msg_id); type = parse_msg_type(msg_type); + if (type != FSCK_ERROR && msg_id_info[id].msg_type == FSCK_FATAL) + die("Cannot demote %s to %s", msg_id, msg_type); + if (!options->msg_type) { int i; int *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX); @@ -216,6 +222,9 @@ static int report(struct fsck_options *options, struct object *object, struct strbuf sb = STRBUF_INIT; int msg_type = fsck_msg_type(id, options), result; + if (msg_type == FSCK_FATAL) + msg_type = FSCK_ERROR; + append_msg_id(&sb, msg_id_info[id].id_string); va_start(ap, fmt); diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 36024fc..f5d6d0d 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -136,4 +136,15 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' ' grep "missingEmail" act ' +test_expect_success \ + 'receive.fsck.unterminatedHeader=warn triggers error' ' + rm -rf dst && + git init dst && + git --git-dir=dst/.git config receive.fsckobjects true && + git --git-dir=dst/.git config \ + receive.fsck.unterminatedheader warn && + test_must_fail git push --porcelain dst HEAD >act 2>&1 && + grep "Cannot demote unterminatedheader" act +' + test_done -- cgit v0.10.2-6-g49f6 From efaba7cc77fb22753675dc1e4f376b61797eca36 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:26:48 +0200 Subject: fsck: optionally ignore specific fsck issues completely An fsck issue in a legacy repository might be so common that one would like not to bother the user with mentioning it at all. With this change, that is possible by setting the respective message type to "ignore". This change "abuses" the missingEmail=warn test to verify that "ignore" is also accepted and works correctly. And while at it, it makes sure that multiple options work, too (they are passed to unpack-objects or index-pack as a comma-separated list via the --strict=... command-line option). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index a4205aa..680f35e 100644 --- a/fsck.c +++ b/fsck.c @@ -131,6 +131,8 @@ static int parse_msg_type(const char *str) return FSCK_ERROR; else if (!strcmp(str, "warn")) return FSCK_WARN; + else if (!strcmp(str, "ignore")) + return FSCK_IGNORE; else die("Unknown fsck message type: '%s'", str); } @@ -222,6 +224,9 @@ static int report(struct fsck_options *options, struct object *object, struct strbuf sb = STRBUF_INIT; int msg_type = fsck_msg_type(id, options), result; + if (msg_type == FSCK_IGNORE) + return 0; + if (msg_type == FSCK_FATAL) msg_type = FSCK_ERROR; diff --git a/fsck.h b/fsck.h index 3ef92a3..1dab276 100644 --- a/fsck.h +++ b/fsck.h @@ -3,6 +3,7 @@ #define FSCK_ERROR 1 #define FSCK_WARN 2 +#define FSCK_IGNORE 3 struct fsck_options; diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index f5d6d0d..720c121 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -133,7 +133,14 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' ' git --git-dir=dst/.git config \ receive.fsck.missingEmail warn && git push --porcelain dst bogus >act 2>&1 && - grep "missingEmail" act + grep "missingEmail" act && + git --git-dir=dst/.git branch -D bogus && + git --git-dir=dst/.git config --add \ + receive.fsck.missingEmail ignore && + git --git-dir=dst/.git config --add \ + receive.fsck.badDate warn && + git push --porcelain dst bogus >act 2>&1 && + test_must_fail grep "missingEmail" act ' test_expect_success \ -- cgit v0.10.2-6-g49f6 From f27d05b17041d6e5c8cedced682709b2f1007ecc Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:26:54 +0200 Subject: fsck: allow upgrading fsck warnings to errors The 'invalid tag name' and 'missing tagger entry' warnings can now be upgraded to errors by specifying `invalidTagName` and `missingTaggerEntry` in the receive.fsck. config setting. Incidentally, the missing tagger warning is now really shown as a warning (as opposed to being reported with the "error:" prefix, as it used to be the case before this commit). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/fsck.c b/fsck.c index 680f35e..7a614bb 100644 --- a/fsck.c +++ b/fsck.c @@ -10,6 +10,7 @@ #include "utf8.h" #define FSCK_FATAL -1 +#define FSCK_INFO -2 #define FOREACH_MSG_ID(FUNC) \ /* fatal errors */ \ @@ -50,15 +51,16 @@ FUNC(ZERO_PADDED_DATE, ERROR) \ /* warnings */ \ FUNC(BAD_FILEMODE, WARN) \ - FUNC(BAD_TAG_NAME, WARN) \ FUNC(EMPTY_NAME, WARN) \ FUNC(FULL_PATHNAME, WARN) \ FUNC(HAS_DOT, WARN) \ FUNC(HAS_DOTDOT, WARN) \ FUNC(HAS_DOTGIT, WARN) \ - FUNC(MISSING_TAGGER_ENTRY, WARN) \ FUNC(NULL_SHA1, WARN) \ - FUNC(ZERO_PADDED_FILEMODE, WARN) + FUNC(ZERO_PADDED_FILEMODE, WARN) \ + /* infos (reported as warnings, but ignored by default) */ \ + FUNC(BAD_TAG_NAME, INFO) \ + FUNC(MISSING_TAGGER_ENTRY, INFO) #define MSG_ID(id, msg_type) FSCK_MSG_##id, enum fsck_msg_id { @@ -229,6 +231,8 @@ static int report(struct fsck_options *options, struct object *object, if (msg_type == FSCK_FATAL) msg_type = FSCK_ERROR; + else if (msg_type == FSCK_INFO) + msg_type = FSCK_WARN; append_msg_id(&sb, msg_id_info[id].id_string); @@ -687,15 +691,21 @@ static int fsck_tag_buffer(struct tag *tag, const char *data, goto done; } strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer); - if (check_refname_format(sb.buf, 0)) - report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME, + if (check_refname_format(sb.buf, 0)) { + ret = report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME, "invalid 'tag' name: %.*s", (int)(eol - buffer), buffer); + if (ret) + goto done; + } buffer = eol + 1; - if (!skip_prefix(buffer, "tagger ", &buffer)) + if (!skip_prefix(buffer, "tagger ", &buffer)) { /* early tags do not contain 'tagger' lines; warn only */ - report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line"); + ret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line"); + if (ret) + goto done; + } else ret = fsck_ident(&buffer, &tag->object, options); diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index 61bc8da..3dc5ec4 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -259,7 +259,7 @@ EOF thirtyeight=${tag#??} && rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight && git index-pack --strict tag-test-${pack1}.pack 2>err && - grep "^error:.* expected .tagger. line" err + grep "^warning:.* expected .tagger. line" err ' test_done -- cgit v0.10.2-6-g49f6 From 4b55b9b4793b2b134fb439161d5407168cd88dcc Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:27:00 +0200 Subject: fsck: document the new receive.fsck. options Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 43bb53c..9393f4e 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2197,6 +2197,20 @@ receive.fsckObjects:: Defaults to false. If not set, the value of `transfer.fsckObjects` is used instead. +receive.fsck.:: + When `receive.fsckObjects` is set to true, errors can be switched + to warnings and vice versa by configuring the `receive.fsck.` + setting where the `` is the fsck message ID and the value + is one of `error`, `warn` or `ignore`. For convenience, fsck prefixes + the error/warning with the message ID, e.g. "missingEmail: invalid + author/committer line - missing email" means that setting + `receive.fsck.missingEmail = ignore` will hide that issue. ++ +This feature is intended to support working with legacy repositories +which would not pass pushing when `receive.fsckObjects = true`, allowing +the host to accept repositories with certain known issues but still catch +other issues. + receive.unpackLimit:: If the number of objects received in a push is below this limit then the objects will be unpacked into loose object -- cgit v0.10.2-6-g49f6 From 2becf00ff70b324999d0e3046d828a80884ab561 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:27:06 +0200 Subject: fsck: support demoting errors to warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have support in `git receive-pack` to deal with some legacy repositories which have non-fatal issues. Let's make `git fsck` itself useful with such repositories, too, by allowing users to ignore known issues, or at least demote those issues to mere warnings. Example: `git -c fsck.missingEmail=ignore fsck` would hide problems with missing emails in author, committer and tagger lines. In the same spirit that `git receive-pack`'s usage of the fsck machinery differs from `git fsck`'s – some of the non-fatal warnings in `git fsck` are fatal with `git receive-pack` when receive.fsckObjects = true, for example – we strictly separate the fsck. from the receive.fsck. settings. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 9393f4e..1c65767 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1242,6 +1242,17 @@ filter..smudge:: object to a worktree file upon checkout. See linkgit:gitattributes[5] for details. +fsck.:: + Allows overriding the message type (error, warn or ignore) of a + specific message ID such as `missingEmail`. ++ +For convenience, fsck prefixes the error/warning with the message ID, +e.g. "missingEmail: invalid author/committer line - missing email" means +that setting `fsck.missingEmail = ignore` will hide that issue. ++ +This feature is intended to support working with legacy repositories +which cannot be repaired without disruptive changes. + gc.aggressiveDepth:: The depth parameter used in the delta compression algorithm used by 'git gc --aggressive'. This defaults diff --git a/builtin/fsck.c b/builtin/fsck.c index cea623d..6dc2f94 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -46,6 +46,16 @@ static int show_dangling = 1; #define DIRENT_SORT_HINT(de) ((de)->d_ino) #endif +static int fsck_config(const char *var, const char *value, void *cb) +{ + if (skip_prefix(var, "fsck.", &var)) { + fsck_set_msg_type(&fsck_obj_options, var, value); + return 0; + } + + return git_default_config(var, value, cb); +} + static void objreport(struct object *obj, const char *msg_type, const char *err) { @@ -640,6 +650,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) include_reflogs = 0; } + git_config(fsck_config, NULL); + fsck_head_link(); fsck_object_dir(get_object_directory()); diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 7c5b3d5..1727129 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -287,6 +287,17 @@ test_expect_success 'rev-list --verify-objects with bad sha1' ' grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out ' +test_expect_success 'force fsck to ignore double author' ' + git cat-file commit HEAD >basis && + sed "s/^author .*/&,&/" multiple-authors && + new=$(git hash-object -t commit -w --stdin Date: Mon, 22 Jun 2015 17:27:12 +0200 Subject: fsck: introduce `git fsck --connectivity-only` This option avoids unpacking each and all blob objects, and just verifies the connectivity. In particular with large repositories, this speeds up the operation, at the expense of missing corrupt blobs, ignoring unreachable objects and other fsck issues, if any. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/git-fsck.txt b/Documentation/git-fsck.txt index 25c431d..84ee92e 100644 --- a/Documentation/git-fsck.txt +++ b/Documentation/git-fsck.txt @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs] [--[no-]full] [--strict] [--verbose] [--lost-found] - [--[no-]dangling] [--[no-]progress] [*] + [--[no-]dangling] [--[no-]progress] [--connectivity-only] [*] DESCRIPTION ----------- @@ -60,6 +60,11 @@ index file, all SHA-1 references in `refs` namespace, and all reflogs object pools. This is now default; you can turn it off with --no-full. +--connectivity-only:: + Check only the connectivity of tags, commits and tree objects. By + avoiding to unpack blobs, this speeds up the operation, at the + expense of missing corrupt objects or other problematic issues. + --strict:: Enable more strict checking, namely to catch a file mode recorded with g+w bit set, which was created by older diff --git a/builtin/fsck.c b/builtin/fsck.c index 6dc2f94..070909e 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -23,6 +23,7 @@ static int show_tags; static int show_unreachable; static int include_reflogs = 1; static int check_full = 1; +static int connectivity_only; static int check_strict; static int keep_cache_objects; static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT; @@ -181,6 +182,8 @@ static void check_reachable_object(struct object *obj) if (!(obj->flags & HAS_OBJ)) { if (has_sha1_pack(obj->sha1)) return; /* it is in pack - forget about it */ + if (connectivity_only && has_sha1_file(obj->sha1)) + return; printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1)); errors_found |= ERROR_REACHABLE; return; @@ -617,6 +620,7 @@ static struct option fsck_opts[] = { OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")), OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")), OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")), + OPT_BOOL(0, "connectivity-only", &connectivity_only, N_("check only connectivity")), OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")), OPT_BOOL(0, "lost-found", &write_lost_and_found, N_("write dangling objects in .git/lost-found")), @@ -653,7 +657,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) git_config(fsck_config, NULL); fsck_head_link(); - fsck_object_dir(get_object_directory()); + if (!connectivity_only) + fsck_object_dir(get_object_directory()); prepare_alt_odb(); for (alt = alt_odb_list; alt; alt = alt->next) { diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 1727129..956673b 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -431,4 +431,26 @@ test_expect_success 'fsck notices ref pointing to missing tag' ' test_must_fail git -C missing fsck ' +test_expect_success 'fsck --connectivity-only' ' + rm -rf connectivity-only && + git init connectivity-only && + ( + cd connectivity-only && + touch empty && + git add empty && + test_commit empty && + empty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 && + rm -f $empty && + echo invalid >$empty && + test_must_fail git fsck --strict && + git fsck --strict --connectivity-only && + tree=$(git rev-parse HEAD:) && + suffix=${tree#??} && + tree=.git/objects/${tree%$suffix}/$suffix && + rm -f $tree && + echo invalid >$tree && + test_must_fail git fsck --strict --connectivity-only + ) +' + test_done -- cgit v0.10.2-6-g49f6 From cd94c6f91ee92ffb0592696170fa84f426b839ab Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 22 Jun 2015 17:27:18 +0200 Subject: fsck: git receive-pack: support excluding objects from fsck'ing The optional new config option `receive.fsck.skipList` specifies the path to a file listing the names, i.e. SHA-1s, one per line, of objects that are to be ignored by `git receive-pack` when `receive.fsckObjects = true`. This is extremely handy in case of legacy repositories where it would cause more pain to change incorrect objects than to live with them (e.g. a duplicate 'author' line in an early commit object). The intended use case is for server administrators to inspect objects that are reported by `git push` as being too problematic to enter the repository, and to add the objects' SHA-1 to a (preferably sorted) file when the objects are legitimate, i.e. when it is determined that those problematic objects should be allowed to enter the server. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 1c65767..f6cbd6d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2222,6 +2222,14 @@ which would not pass pushing when `receive.fsckObjects = true`, allowing the host to accept repositories with certain known issues but still catch other issues. +receive.fsck.skipList:: + The path to a sorted list of object names (i.e. one SHA-1 per + line) that are known to be broken in a non-fatal way and should + be ignored. This feature is useful when an established project + should be accepted despite early commits containing errors that + can be safely ignored such as invalid committer email addresses. + Note: corrupt objects cannot be skipped with this setting. + receive.unpackLimit:: If the number of objects received in a push is below this limit then the objects will be unpacked into loose object diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 3afe8f8..4ced2eb 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -117,6 +117,17 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "receive.fsck.skiplist") == 0) { + const char *path; + + if (git_config_pathname(&path, var, value)) + return 1; + strbuf_addf(&fsck_msg_types, "%cskiplist=%s", + fsck_msg_types.len ? ',' : '=', path); + free((char *)path); + return 0; + } + if (skip_prefix(var, "receive.fsck.", &var)) { if (is_valid_msg_type(var, value)) strbuf_addf(&fsck_msg_types, "%c%s=%s", diff --git a/fsck.c b/fsck.c index 7a614bb..1523243 100644 --- a/fsck.c +++ b/fsck.c @@ -8,6 +8,7 @@ #include "fsck.h" #include "refs.h" #include "utf8.h" +#include "sha1-array.h" #define FSCK_FATAL -1 #define FSCK_INFO -2 @@ -127,6 +128,43 @@ static int fsck_msg_type(enum fsck_msg_id msg_id, return msg_type; } +static void init_skiplist(struct fsck_options *options, const char *path) +{ + static struct sha1_array skiplist = SHA1_ARRAY_INIT; + int sorted, fd; + char buffer[41]; + unsigned char sha1[20]; + + if (options->skiplist) + sorted = options->skiplist->sorted; + else { + sorted = 1; + options->skiplist = &skiplist; + } + + fd = open(path, O_RDONLY); + if (fd < 0) + die("Could not open skip list: %s", path); + for (;;) { + int result = read_in_full(fd, buffer, sizeof(buffer)); + if (result < 0) + die_errno("Could not read '%s'", path); + if (!result) + break; + if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') + die("Invalid SHA-1: %s", buffer); + sha1_array_append(&skiplist, sha1); + if (sorted && skiplist.nr > 1 && + hashcmp(skiplist.sha1[skiplist.nr - 2], + sha1) > 0) + sorted = 0; + } + close(fd); + + if (sorted) + skiplist.sorted = 1; +} + static int parse_msg_type(const char *str) { if (!strcmp(str, "error")) @@ -191,6 +229,14 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values) buf[equal] = tolower(buf[equal]); buf[equal] = '\0'; + if (!strcmp(buf, "skiplist")) { + if (equal == len) + die("skiplist requires a path"); + init_skiplist(options, buf + equal + 1); + buf += len + 1; + continue; + } + if (equal == len) die("Missing '=': '%s'", buf); @@ -229,6 +275,10 @@ static int report(struct fsck_options *options, struct object *object, if (msg_type == FSCK_IGNORE) return 0; + if (options->skiplist && object && + sha1_array_lookup(options->skiplist, object->sha1) >= 0) + return 0; + if (msg_type == FSCK_FATAL) msg_type = FSCK_ERROR; else if (msg_type == FSCK_INFO) diff --git a/fsck.h b/fsck.h index 1dab276..dded84b 100644 --- a/fsck.h +++ b/fsck.h @@ -32,6 +32,7 @@ struct fsck_options { fsck_error error_func; unsigned strict:1; int *msg_type; + struct sha1_array *skiplist; }; #define FSCK_OPTIONS_DEFAULT { NULL, fsck_error_function, 0, NULL } diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 720c121..44f3d5f 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -123,6 +123,18 @@ committer Bugs Bunny 1234567890 +0000 This commit object intentionally broken EOF +test_expect_success 'push with receive.fsck.skipList' ' + commit="$(git hash-object -t commit -w --stdin dst/.git/SKIP && + git push --porcelain dst bogus +' + test_expect_success 'push with receive.fsck.missingEmail=warn' ' commit="$(git hash-object -t commit -w --stdin Date: Mon, 22 Jun 2015 17:27:23 +0200 Subject: fsck: support ignoring objects in `git fsck` via fsck.skiplist Identical to support in `git receive-pack for the config option `receive.fsck.skiplist`, we now support ignoring given objects in `git fsck` via `fsck.skiplist` altogether. This is extremely handy in case of legacy repositories where it would cause more pain to change incorrect objects than to live with them (e.g. a duplicate 'author' line in an early commit object). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index f6cbd6d..18cc592 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1253,6 +1253,14 @@ that setting `fsck.missingEmail = ignore` will hide that issue. This feature is intended to support working with legacy repositories which cannot be repaired without disruptive changes. +fsck.skipList:: + The path to a sorted list of object names (i.e. one SHA-1 per + line) that are known to be broken in a non-fatal way and should + be ignored. This feature is useful when an established project + should be accepted despite early commits containing errors that + can be safely ignored such as invalid committer email addresses. + Note: corrupt objects cannot be skipped with this setting. + gc.aggressiveDepth:: The depth parameter used in the delta compression algorithm used by 'git gc --aggressive'. This defaults diff --git a/builtin/fsck.c b/builtin/fsck.c index 070909e..67237a6 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -49,6 +49,19 @@ static int show_dangling = 1; static int fsck_config(const char *var, const char *value, void *cb) { + if (strcmp(var, "fsck.skiplist") == 0) { + const char *path; + struct strbuf sb = STRBUF_INIT; + + if (git_config_pathname(&path, var, value)) + return 1; + strbuf_addf(&sb, "skiplist=%s", path); + free((char *)path); + fsck_set_msg_types(&fsck_obj_options, sb.buf); + strbuf_release(&sb); + return 0; + } + if (skip_prefix(var, "fsck.", &var)) { fsck_set_msg_type(&fsck_obj_options, var, value); return 0; -- cgit v0.10.2-6-g49f6