fsck: do not fallback "git fsck <bogus>" to "git fsck"
[gitweb.git] / builtin / fsck.c
index f01b81eebfebc1c221e81c44e3f14a4e2781f0a7..3d9ee310d2d16a17b278f315f3571bbb9165941f 100644 (file)
@@ -205,8 +205,6 @@ static void check_reachable_object(struct object *obj)
        if (!(obj->flags & HAS_OBJ)) {
                if (has_sha1_pack(obj->oid.hash))
                        return; /* it is in pack - forget about it */
-               if (connectivity_only && has_object_file(&obj->oid))
-                       return;
                printf("missing %s %s\n", typename(obj->type),
                        describe_object(obj));
                errors_found |= ERROR_REACHABLE;
@@ -225,7 +223,7 @@ static void check_unreachable_object(struct object *obj)
         * to complain about it being unreachable (since it does
         * not exist).
         */
-       if (!obj->parsed)
+       if (!(obj->flags & HAS_OBJ))
                return;
 
        /*
@@ -584,6 +582,79 @@ static int fsck_cache_tree(struct cache_tree *it)
        return err;
 }
 
+static void mark_object_for_connectivity(const unsigned char *sha1)
+{
+       struct object *obj = lookup_object(sha1);
+
+       /*
+        * Setting the object type here isn't strictly necessary for a
+        * connectivity check. In most cases, our walk will expect a certain
+        * type (e.g., a tree referencing a blob) and will use lookup_blob() to
+        * assign the type. But doing it here has two advantages:
+        *
+        *   1. When the fsck_walk code looks at objects that _don't_ come from
+        *      links (e.g., the tip of a ref), it may complain about the
+        *      "unknown object type".
+        *
+        *   2. This serves as a nice cross-check that the graph links are
+        *      sane. So --connectivity-only does not check that the bits of
+        *      blobs are not corrupted, but it _does_ check that 100644 tree
+        *      entries point to blobs, and so forth.
+        *
+        * Unfortunately we can't just use parse_object() here, because the
+        * whole point of --connectivity-only is to avoid reading the object
+        * data more than necessary.
+        */
+       if (!obj || obj->type == OBJ_NONE) {
+               enum object_type type = sha1_object_info(sha1, NULL);
+               switch (type) {
+               case OBJ_BAD:
+                       error("%s: unable to read object type",
+                             sha1_to_hex(sha1));
+                       break;
+               case OBJ_COMMIT:
+                       obj = (struct object *)lookup_commit(sha1);
+                       break;
+               case OBJ_TREE:
+                       obj = (struct object *)lookup_tree(sha1);
+                       break;
+               case OBJ_BLOB:
+                       obj = (struct object *)lookup_blob(sha1);
+                       break;
+               case OBJ_TAG:
+                       obj = (struct object *)lookup_tag(sha1);
+                       break;
+               default:
+                       error("%s: unknown object type %d",
+                             sha1_to_hex(sha1), type);
+               }
+
+               if (!obj || obj->type == OBJ_NONE) {
+                       errors_found |= ERROR_OBJECT;
+                       return;
+               }
+       }
+
+       obj->flags |= HAS_OBJ;
+}
+
+static int mark_loose_for_connectivity(const unsigned char *sha1,
+                                      const char *path,
+                                      void *data)
+{
+       mark_object_for_connectivity(sha1);
+       return 0;
+}
+
+static int mark_packed_for_connectivity(const unsigned char *sha1,
+                                       struct packed_git *pack,
+                                       uint32_t pos,
+                                       void *data)
+{
+       mark_object_for_connectivity(sha1);
+       return 0;
+}
+
 static char const * const fsck_usage[] = {
        N_("git fsck [<options>] [<object>...]"),
        NULL
@@ -640,38 +711,41 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        git_config(fsck_config, NULL);
 
        fsck_head_link();
-       if (!connectivity_only) {
+       if (connectivity_only) {
+               for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
+               for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
+       } else {
                fsck_object_dir(get_object_directory());
 
                prepare_alt_odb();
                for (alt = alt_odb_list; alt; alt = alt->next)
                        fsck_object_dir(alt->path);
-       }
 
-       if (check_full) {
-               struct packed_git *p;
-               uint32_t total = 0, count = 0;
-               struct progress *progress = NULL;
+               if (check_full) {
+                       struct packed_git *p;
+                       uint32_t total = 0, count = 0;
+                       struct progress *progress = NULL;
 
-               prepare_packed_git();
+                       prepare_packed_git();
 
-               if (show_progress) {
+                       if (show_progress) {
+                               for (p = packed_git; p; p = p->next) {
+                                       if (open_pack_index(p))
+                                               continue;
+                                       total += p->num_objects;
+                               }
+
+                               progress = start_progress(_("Checking objects"), total);
+                       }
                        for (p = packed_git; p; p = p->next) {
-                               if (open_pack_index(p))
-                                       continue;
-                               total += p->num_objects;
+                               /* verify gives error messages itself */
+                               if (verify_pack(p, fsck_obj_buffer,
+                                               progress, count))
+                                       errors_found |= ERROR_PACK;
+                               count += p->num_objects;
                        }
-
-                       progress = start_progress(_("Checking objects"), total);
-               }
-               for (p = packed_git; p; p = p->next) {
-                       /* verify gives error messages itself */
-                       if (verify_pack(p, fsck_obj_buffer,
-                                       progress, count))
-                               errors_found |= ERROR_PACK;
-                       count += p->num_objects;
+                       stop_progress(&progress);
                }
-               stop_progress(&progress);
        }
 
        heads = 0;
@@ -681,9 +755,11 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                if (!get_sha1(arg, sha1)) {
                        struct object *obj = lookup_object(sha1);
 
-                       /* Error is printed by lookup_object(). */
-                       if (!obj)
+                       if (!obj) {
+                               error("%s: object missing", sha1_to_hex(sha1));
+                               errors_found |= ERROR_OBJECT;
                                continue;
+                       }
 
                        obj->used = 1;
                        if (name_objects)
@@ -694,6 +770,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        continue;
                }
                error("invalid parameter: expected sha1, got '%s'", arg);
+               errors_found |= ERROR_OBJECT;
        }
 
        /*
@@ -701,7 +778,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
         * default ones from .git/refs. We also consider the index file
         * in this case (ie this implies --cache).
         */
-       if (!heads) {
+       if (!argc) {
                get_default_heads();
                keep_cache_objects = 1;
        }