Merge branch 'jc/doc-gc-prune-now' into maint
[gitweb.git] / builtin / fsck.c
index 267979304994158c491b2306388b932c50d7438b..b9a74f0cf6e169073994e466110fd1bde9febba7 100644 (file)
@@ -23,8 +23,11 @@ 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;
+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;
@@ -35,6 +38,7 @@ static int show_dangling = 1;
 #define ERROR_OBJECT 01
 #define ERROR_REACHABLE 02
 #define ERROR_PACK 04
+#define ERROR_REFS 010
 
 #ifdef NO_D_INO_IN_DIRENT
 #define SORT_DIRENT 0
@@ -44,39 +48,52 @@ 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 int fsck_config(const char *var, const char *value, void *cb)
 {
-       fprintf(stderr, "%s in %s %s: ",
-               severity, typename(obj->type), sha1_to_hex(obj->sha1));
-       vfprintf(stderr, err, params);
-       fputs("\n", stderr);
+       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;
+       }
+
+       return git_default_config(var, value, cb);
 }
 
-__attribute__((format (printf, 2, 3)))
-static int objerror(struct object *obj, const char *err, ...)
+static void objreport(struct object *obj, const char *msg_type,
+                       const char *err)
+{
+       fprintf(stderr, "%s in %s %s: %s\n",
+               msg_type, typename(obj->type), sha1_to_hex(obj->sha1), 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;
 }
 
 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 +136,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 +149,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 +175,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;
@@ -179,6 +196,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;
@@ -225,13 +244,14 @@ static void check_unreachable_object(struct object *obj)
                        printf("dangling %s %s\n", typename(obj->type),
                               sha1_to_hex(obj->sha1));
                if (write_lost_and_found) {
-                       const char *filename = git_path("lost-found/%s/%s",
+                       char *filename = git_pathdup("lost-found/%s/%s",
                                obj->type == OBJ_COMMIT ? "commit" : "other",
                                sha1_to_hex(obj->sha1));
                        FILE *f;
 
                        if (safe_create_leading_directories_const(filename)) {
                                error("Could not create lost-found");
+                               free(filename);
                                return;
                        }
                        if (!(f = fopen(filename, "w")))
@@ -244,6 +264,7 @@ static void check_unreachable_object(struct object *obj)
                        if (fclose(f))
                                die_errno("Could not finish '%s'",
                                          filename);
+                       free(filename);
                }
                return;
        }
@@ -296,9 +317,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) {
@@ -501,8 +522,10 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
                /* We'll continue with the rest despite the error.. */
                return 0;
        }
-       if (obj->type != OBJ_COMMIT && is_branch(refname))
+       if (obj->type != OBJ_COMMIT && is_branch(refname)) {
                error("%s: not a commit", refname);
+               errors_found |= ERROR_REFS;
+       }
        default_refs++;
        obj->used = 1;
        mark_object_reachable(obj);
@@ -565,17 +588,23 @@ static int fsck_head_link(void)
                fprintf(stderr, "Checking HEAD link\n");
 
        head_points_at = resolve_ref_unsafe("HEAD", 0, head_oid.hash, &flag);
-       if (!head_points_at)
+       if (!head_points_at) {
+               errors_found |= ERROR_REFS;
                return error("Invalid HEAD");
+       }
        if (!strcmp(head_points_at, "HEAD"))
                /* detached HEAD */
                null_is_error = 1;
-       else if (!starts_with(head_points_at, "refs/heads/"))
+       else if (!starts_with(head_points_at, "refs/heads/")) {
+               errors_found |= ERROR_REFS;
                return error("HEAD points to something strange (%s)",
                             head_points_at);
+       }
        if (is_null_oid(&head_oid)) {
-               if (null_is_error)
+               if (null_is_error) {
+                       errors_found |= ERROR_REFS;
                        return error("HEAD: detached HEAD points at nothing");
+               }
                fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
                        head_points_at + 11);
        }
@@ -595,6 +624,7 @@ static int fsck_cache_tree(struct cache_tree *it)
                if (!obj) {
                        error("%s: invalid sha1 pointer in cache-tree",
                              sha1_to_hex(it->sha1));
+                       errors_found |= ERROR_REFS;
                        return 1;
                }
                obj->used = 1;
@@ -621,6 +651,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")),
@@ -638,6 +669,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)
@@ -648,8 +685,11 @@ 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());
+       if (!connectivity_only)
+               fsck_object_dir(get_object_directory());
 
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {