stash: convert branch to builtin
[gitweb.git] / builtin / fsck.c
index 92ce775a74d88036b024b24d74e3987da521ef41..3c3e0f06e78009ee3ee2d54333dfc4740b8792a9 100644 (file)
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "repository.h"
 #include "config.h"
 #include "commit.h"
 #include "tree.h"
@@ -16,6 +17,9 @@
 #include "streaming.h"
 #include "decorate.h"
 #include "packfile.h"
+#include "object-store.h"
+#include "run-command.h"
+#include "worktree.h"
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
@@ -33,8 +37,6 @@ 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;
 static int write_lost_and_found;
 static int verbose;
@@ -45,6 +47,7 @@ static int name_objects;
 #define ERROR_REACHABLE 02
 #define ERROR_PACK 04
 #define ERROR_REFS 010
+#define ERROR_COMMIT_GRAPH 020
 
 static const char *describe_object(struct object *obj)
 {
@@ -65,12 +68,13 @@ static const char *printable_type(struct object *obj)
        const char *ret;
 
        if (obj->type == OBJ_NONE) {
-               enum object_type type = sha1_object_info(obj->oid.hash, NULL);
+               enum object_type type = oid_object_info(the_repository,
+                                                       &obj->oid, NULL);
                if (type > 0)
-                       object_as_type(obj, type, 0);
+                       object_as_type(the_repository, obj, type, 0);
        }
 
-       ret = typename(obj->type);
+       ret = type_name(obj->type);
        if (!ret)
                ret = "unknown";
 
@@ -137,7 +141,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
                printf("broken link from %7s %s\n",
                           printable_type(parent), describe_object(parent));
                printf("broken link from %7s %s\n",
-                          (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
+                          (type == OBJ_ANY ? "unknown" : type_name(type)), "unknown");
                errors_found |= ERROR_REACHABLE;
                return 1;
        }
@@ -149,6 +153,15 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
        if (obj->flags & REACHABLE)
                return 0;
        obj->flags |= REACHABLE;
+
+       if (is_promisor_object(&obj->oid))
+               /*
+                * Further recursion does not need to be performed on this
+                * object since it is a promisor object (so it does not need to
+                * be added to "pending").
+                */
+               return 0;
+
        if (!(obj->flags & HAS_OBJ)) {
                if (parent && !has_object_file(&obj->oid)) {
                        printf("broken link from %7s %s\n",
@@ -214,7 +227,9 @@ static void check_reachable_object(struct object *obj)
         * do a full fsck
         */
        if (!(obj->flags & HAS_OBJ)) {
-               if (has_sha1_pack(obj->oid.hash))
+               if (is_promisor_object(&obj->oid))
+                       return;
+               if (has_object_pack(&obj->oid))
                        return; /* it is in pack - forget about it */
                printf("missing %s %s\n", printable_type(obj),
                        describe_object(obj));
@@ -326,7 +341,7 @@ static void check_connectivity(void)
        }
 }
 
-static int fsck_obj(struct object *obj)
+static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 {
        int err;
 
@@ -340,7 +355,7 @@ static int fsck_obj(struct object *obj)
 
        if (fsck_walk(obj, NULL, &fsck_obj_options))
                objerror(obj, "broken links");
-       err = fsck_object(obj, NULL, 0, &fsck_obj_options);
+       err = fsck_object(obj, buffer, size, &fsck_obj_options);
        if (err)
                goto out;
 
@@ -378,14 +393,15 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
         * verify_packfile(), data_valid variable for details.
         */
        struct object *obj;
-       obj = parse_object_buffer(oid, type, size, buffer, eaten);
+       obj = parse_object_buffer(the_repository, oid, type, size, buffer,
+                                 eaten);
        if (!obj) {
                errors_found |= ERROR_OBJECT;
                return error("%s: object corrupt or missing", oid_to_hex(oid));
        }
        obj->flags &= ~(REACHABLE | SEEN);
        obj->flags |= HAS_OBJ;
-       return fsck_obj(obj);
+       return fsck_obj(obj, buffer, size);
 }
 
 static int default_refs;
@@ -396,7 +412,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
        struct object *obj;
 
        if (!is_null_oid(oid)) {
-               obj = lookup_object(oid->hash);
+               obj = lookup_object(the_repository, oid->hash);
                if (obj && (obj->flags & HAS_OBJ)) {
                        if (timestamp && name_objects)
                                add_decoration(fsck_walk_options.object_names,
@@ -404,7 +420,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
                                        xstrfmt("%s@{%"PRItime"}", refname, timestamp));
                        obj->flags |= USED;
                        mark_object_reachable(obj);
-               } else {
+               } else if (!is_promisor_object(oid)) {
                        error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
@@ -429,7 +445,11 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
 static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
                              int flag, void *cb_data)
 {
-       for_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname);
+       struct strbuf refname = STRBUF_INIT;
+
+       strbuf_worktree_ref(cb_data, &refname, logname);
+       for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);
+       strbuf_release(&refname);
        return 0;
 }
 
@@ -438,8 +458,16 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
 {
        struct object *obj;
 
-       obj = parse_object(oid);
+       obj = parse_object(the_repository, oid);
        if (!obj) {
+               if (is_promisor_object(oid)) {
+                       /*
+                        * Increment default_refs anyway, because this is a
+                        * valid ref.
+                        */
+                        default_refs++;
+                        return 0;
+               }
                error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
                errors_found |= ERROR_REACHABLE;
                /* We'll continue with the rest despite the error.. */
@@ -459,13 +487,34 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
        return 0;
 }
 
+static int fsck_head_link(const char *head_ref_name,
+                         const char **head_points_at,
+                         struct object_id *head_oid);
+
 static void get_default_heads(void)
 {
-       if (head_points_at && !is_null_oid(&head_oid))
-               fsck_handle_ref("HEAD", &head_oid, 0, NULL);
+       struct worktree **worktrees, **p;
+       const char *head_points_at;
+       struct object_id head_oid;
+
        for_each_rawref(fsck_handle_ref, NULL);
-       if (include_reflogs)
-               for_each_reflog(fsck_handle_reflog, NULL);
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+               struct strbuf ref = STRBUF_INIT;
+
+               strbuf_worktree_ref(wt, &ref, "HEAD");
+               fsck_head_link(ref.buf, &head_points_at, &head_oid);
+               if (head_points_at && !is_null_oid(&head_oid))
+                       fsck_handle_ref(ref.buf, &head_oid, 0, NULL);
+               strbuf_release(&ref);
+
+               if (include_reflogs)
+                       refs_for_each_reflog(get_worktree_ref_store(wt),
+                                            fsck_handle_reflog, wt);
+       }
+       free_worktrees(worktrees);
 
        /*
         * Not having any default heads isn't really fatal, but
@@ -485,44 +534,44 @@ static void get_default_heads(void)
        }
 }
 
-static struct object *parse_loose_object(const struct object_id *oid,
-                                        const char *path)
+static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 {
        struct object *obj;
-       void *contents;
        enum object_type type;
        unsigned long size;
+       void *contents;
        int eaten;
 
-       if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0)
-               return NULL;
+       if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
+               errors_found |= ERROR_OBJECT;
+               error("%s: object corrupt or missing: %s",
+                     oid_to_hex(oid), path);
+               return 0; /* keep checking other objects */
+       }
 
        if (!contents && type != OBJ_BLOB)
-               die("BUG: read_loose_object streamed a non-blob");
-
-       obj = parse_object_buffer(oid, type, size, contents, &eaten);
-
-       if (!eaten)
-               free(contents);
-       return obj;
-}
+               BUG("read_loose_object streamed a non-blob");
 
-static int fsck_loose(const struct object_id *oid, const char *path, void *data)
-{
-       struct object *obj = parse_loose_object(oid, path);
+       obj = parse_object_buffer(the_repository, oid, type, size,
+                                 contents, &eaten);
 
        if (!obj) {
                errors_found |= ERROR_OBJECT;
-               error("%s: object corrupt or missing: %s",
+               error("%s: object could not be parsed: %s",
                      oid_to_hex(oid), path);
+               if (!eaten)
+                       free(contents);
                return 0; /* keep checking other objects */
        }
 
        obj->flags &= ~(REACHABLE | SEEN);
        obj->flags |= HAS_OBJ;
-       if (fsck_obj(obj))
+       if (fsck_obj(obj, contents, size))
                errors_found |= ERROR_OBJECT;
-       return 0;
+
+       if (!eaten)
+               free(contents);
+       return 0; /* keep checking other objects, even if we saw an error */
 }
 
 static int fsck_cruft(const char *basename, const char *path, void *data)
@@ -554,33 +603,36 @@ static void fsck_object_dir(const char *path)
        stop_progress(&progress);
 }
 
-static int fsck_head_link(void)
+static int fsck_head_link(const char *head_ref_name,
+                         const char **head_points_at,
+                         struct object_id *head_oid)
 {
        int null_is_error = 0;
 
        if (verbose)
-               fprintf(stderr, "Checking HEAD link\n");
+               fprintf(stderr, "Checking %s link\n", head_ref_name);
 
-       head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL);
-       if (!head_points_at) {
+       *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
+       if (!*head_points_at) {
                errors_found |= ERROR_REFS;
-               return error("Invalid HEAD");
+               return error("Invalid %s", head_ref_name);
        }
-       if (!strcmp(head_points_at, "HEAD"))
+       if (!strcmp(*head_points_at, head_ref_name))
                /* 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);
+               return error("%s points to something strange (%s)",
+                            head_ref_name, *head_points_at);
        }
-       if (is_null_oid(&head_oid)) {
+       if (is_null_oid(head_oid)) {
                if (null_is_error) {
                        errors_found |= ERROR_REFS;
-                       return error("HEAD: detached HEAD points at nothing");
+                       return error("%s: detached HEAD points at nothing",
+                                    head_ref_name);
                }
-               fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
-                       head_points_at + 11);
+               fprintf(stderr, "notice: %s points to an unborn branch (%s)\n",
+                       head_ref_name, *head_points_at + 11);
        }
        return 0;
 }
@@ -594,7 +646,7 @@ static int fsck_cache_tree(struct cache_tree *it)
                fprintf(stderr, "Checking cache tree\n");
 
        if (0 <= it->entry_count) {
-               struct object *obj = parse_object(&it->oid);
+               struct object *obj = parse_object(the_repository, &it->oid);
                if (!obj) {
                        error("%s: invalid sha1 pointer in cache-tree",
                              oid_to_hex(&it->oid));
@@ -665,8 +717,11 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        int i;
        struct alternate_object_database *alt;
 
+       /* fsck knows how to handle missing promisor objects */
+       fetch_if_missing = 0;
+
        errors_found = 0;
-       check_replace_refs = 0;
+       read_replace_refs = 0;
 
        argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
 
@@ -692,14 +747,16 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 
        git_config(fsck_config, NULL);
 
-       fsck_head_link();
        if (connectivity_only) {
                for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
                for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
        } else {
+               struct alternate_object_database *alt_odb_list;
+
                fsck_object_dir(get_object_directory());
 
-               prepare_alt_odb();
+               prepare_alt_odb(the_repository);
+               alt_odb_list = the_repository->objects->alt_odb_list;
                for (alt = alt_odb_list; alt; alt = alt->next)
                        fsck_object_dir(alt->path);
 
@@ -708,10 +765,9 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        uint32_t total = 0, count = 0;
                        struct progress *progress = NULL;
 
-                       prepare_packed_git();
-
                        if (show_progress) {
-                               for (p = packed_git; p; p = p->next) {
+                               for (p = get_all_packs(the_repository); p;
+                                    p = p->next) {
                                        if (open_pack_index(p))
                                                continue;
                                        total += p->num_objects;
@@ -719,7 +775,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
 
                                progress = start_progress(_("Checking objects"), total);
                        }
-                       for (p = packed_git; p; p = p->next) {
+                       for (p = get_all_packs(the_repository); p;
+                            p = p->next) {
                                /* verify gives error messages itself */
                                if (verify_pack(p, fsck_obj_buffer,
                                                progress, count))
@@ -728,15 +785,21 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        }
                        stop_progress(&progress);
                }
+
+               if (fsck_finish(&fsck_obj_options))
+                       errors_found |= ERROR_OBJECT;
        }
 
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                struct object_id oid;
                if (!get_oid(arg, &oid)) {
-                       struct object *obj = lookup_object(oid.hash);
+                       struct object *obj = lookup_object(the_repository,
+                                                          oid.hash);
 
                        if (!obj || !(obj->flags & HAS_OBJ)) {
+                               if (is_promisor_object(&oid))
+                                       continue;
                                error("%s: object missing", oid_to_hex(&oid));
                                errors_found |= ERROR_OBJECT;
                                continue;
@@ -775,7 +838,8 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        mode = active_cache[i]->ce_mode;
                        if (S_ISGITLINK(mode))
                                continue;
-                       blob = lookup_blob(&active_cache[i]->oid);
+                       blob = lookup_blob(the_repository,
+                                          &active_cache[i]->oid);
                        if (!blob)
                                continue;
                        obj = &blob->object;
@@ -791,5 +855,42 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        }
 
        check_connectivity();
+
+       if (!git_config_get_bool("core.commitgraph", &i) && i) {
+               struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
+               const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
+
+               commit_graph_verify.argv = verify_argv;
+               commit_graph_verify.git_cmd = 1;
+               if (run_command(&commit_graph_verify))
+                       errors_found |= ERROR_COMMIT_GRAPH;
+
+               prepare_alt_odb(the_repository);
+               for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
+                       verify_argv[2] = "--object-dir";
+                       verify_argv[3] = alt->path;
+                       if (run_command(&commit_graph_verify))
+                               errors_found |= ERROR_COMMIT_GRAPH;
+               }
+       }
+
+       if (!git_config_get_bool("core.multipackindex", &i) && i) {
+               struct child_process midx_verify = CHILD_PROCESS_INIT;
+               const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
+
+               midx_verify.argv = midx_argv;
+               midx_verify.git_cmd = 1;
+               if (run_command(&midx_verify))
+                       errors_found |= ERROR_COMMIT_GRAPH;
+
+               prepare_alt_odb(the_repository);
+               for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
+                       midx_argv[2] = "--object-dir";
+                       midx_argv[3] = alt->path;
+                       if (run_command(&midx_verify))
+                               errors_found |= ERROR_COMMIT_GRAPH;
+               }
+       }
+
        return errors_found;
 }