Merge branch 'js/larger-timestamps'
[gitweb.git] / builtin / fsck.c
index 9923b1089898eaee82fa394dc758b5b2d62c2649..32a32e55c8539372c11a36eba85f3bf14f0e221a 100644 (file)
@@ -13,6 +13,7 @@
 #include "dir.h"
 #include "progress.h"
 #include "streaming.h"
+#include "decorate.h"
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
@@ -35,11 +36,43 @@ static int write_lost_and_found;
 static int verbose;
 static int show_progress = -1;
 static int show_dangling = 1;
+static int name_objects;
 #define ERROR_OBJECT 01
 #define ERROR_REACHABLE 02
 #define ERROR_PACK 04
 #define ERROR_REFS 010
 
+static const char *describe_object(struct object *obj)
+{
+       static struct strbuf buf = STRBUF_INIT;
+       char *name = name_objects ?
+               lookup_decoration(fsck_walk_options.object_names, obj) : NULL;
+
+       strbuf_reset(&buf);
+       strbuf_addstr(&buf, oid_to_hex(&obj->oid));
+       if (name)
+               strbuf_addf(&buf, " (%s)", name);
+
+       return buf.buf;
+}
+
+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);
+               if (type > 0)
+                       object_as_type(obj, type, 0);
+       }
+
+       ret = typename(obj->type);
+       if (!ret)
+               ret = "unknown";
+
+       return ret;
+}
+
 static int fsck_config(const char *var, const char *value, void *cb)
 {
        if (strcmp(var, "fsck.skiplist") == 0) {
@@ -67,7 +100,7 @@ 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), oid_to_hex(&obj->oid), err);
+               msg_type, printable_type(obj), describe_object(obj), err);
 }
 
 static int objerror(struct object *obj, const char *err)
@@ -77,7 +110,8 @@ static int objerror(struct object *obj, const char *err)
        return -1;
 }
 
-static int fsck_error_func(struct object *obj, int type, const char *message)
+static int fsck_error_func(struct fsck_options *o,
+       struct object *obj, int type, const char *message)
 {
        objreport(obj, (type == FSCK_WARN) ? "warning" : "error", message);
        return (type == FSCK_WARN) ? 0 : 1;
@@ -97,7 +131,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
        if (!obj) {
                /* ... these references to parent->fld are safe here */
                printf("broken link from %7s %s\n",
-                          typename(parent->type), oid_to_hex(&parent->oid));
+                          printable_type(parent), describe_object(parent));
                printf("broken link from %7s %s\n",
                           (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
                errors_found |= ERROR_REACHABLE;
@@ -114,9 +148,9 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
        if (!(obj->flags & HAS_OBJ)) {
                if (parent && !has_object_file(&obj->oid)) {
                        printf("broken link from %7s %s\n",
-                                typename(parent->type), oid_to_hex(&parent->oid));
+                                printable_type(parent), describe_object(parent));
                        printf("              to %7s %s\n",
-                                typename(obj->type), oid_to_hex(&obj->oid));
+                                printable_type(obj), describe_object(obj));
                        errors_found |= ERROR_REACHABLE;
                }
                return 1;
@@ -188,9 +222,8 @@ 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), oid_to_hex(&obj->oid));
+               printf("missing %s %s\n", printable_type(obj),
+                       describe_object(obj));
                errors_found |= ERROR_REACHABLE;
                return;
        }
@@ -207,7 +240,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;
 
        /*
@@ -215,7 +248,8 @@ static void check_unreachable_object(struct object *obj)
         * since this is something that is prunable.
         */
        if (show_unreachable) {
-               printf("unreachable %s %s\n", typename(obj->type), oid_to_hex(&obj->oid));
+               printf("unreachable %s %s\n", printable_type(obj),
+                       describe_object(obj));
                return;
        }
 
@@ -233,12 +267,12 @@ static void check_unreachable_object(struct object *obj)
         */
        if (!obj->used) {
                if (show_dangling)
-                       printf("dangling %s %s\n", typename(obj->type),
-                              oid_to_hex(&obj->oid));
+                       printf("dangling %s %s\n", printable_type(obj),
+                              describe_object(obj));
                if (write_lost_and_found) {
                        char *filename = git_pathdup("lost-found/%s/%s",
                                obj->type == OBJ_COMMIT ? "commit" : "other",
-                               oid_to_hex(&obj->oid));
+                               describe_object(obj));
                        FILE *f;
 
                        if (safe_create_leading_directories_const(filename)) {
@@ -249,10 +283,10 @@ static void check_unreachable_object(struct object *obj)
                        if (!(f = fopen(filename, "w")))
                                die_errno("Could not open '%s'", filename);
                        if (obj->type == OBJ_BLOB) {
-                               if (stream_blob_to_fd(fileno(f), obj->oid.hash, NULL, 1))
+                               if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1))
                                        die_errno("Could not write '%s'", filename);
                        } else
-                               fprintf(f, "%s\n", oid_to_hex(&obj->oid));
+                               fprintf(f, "%s\n", describe_object(obj));
                        if (fclose(f))
                                die_errno("Could not finish '%s'",
                                          filename);
@@ -271,7 +305,7 @@ static void check_unreachable_object(struct object *obj)
 static void check_object(struct object *obj)
 {
        if (verbose)
-               fprintf(stderr, "Checking %s\n", oid_to_hex(&obj->oid));
+               fprintf(stderr, "Checking %s\n", describe_object(obj));
 
        if (obj->flags & REACHABLE)
                check_reachable_object(obj);
@@ -307,7 +341,7 @@ static int fsck_obj(struct object *obj)
 
        if (verbose)
                fprintf(stderr, "Checking %s %s\n",
-                       typename(obj->type), oid_to_hex(&obj->oid));
+                       printable_type(obj), describe_object(obj));
 
        if (fsck_walk(obj, NULL, &fsck_obj_options))
                objerror(obj, "broken links");
@@ -326,33 +360,23 @@ static int fsck_obj(struct object *obj)
                free_commit_buffer(commit);
 
                if (!commit->parents && show_root)
-                       printf("root %s\n", oid_to_hex(&commit->object.oid));
+                       printf("root %s\n", describe_object(&commit->object));
        }
 
        if (obj->type == OBJ_TAG) {
                struct tag *tag = (struct tag *) obj;
 
                if (show_tags && tag->tagged) {
-                       printf("tagged %s %s", typename(tag->tagged->type), oid_to_hex(&tag->tagged->oid));
-                       printf(" (%s) in %s\n", tag->tag, oid_to_hex(&tag->object.oid));
+                       printf("tagged %s %s", printable_type(tag->tagged),
+                               describe_object(tag->tagged));
+                       printf(" (%s) in %s\n", tag->tag,
+                               describe_object(&tag->object));
                }
        }
 
        return 0;
 }
 
-static int fsck_sha1(const unsigned char *sha1)
-{
-       struct object *obj = parse_object(sha1);
-       if (!obj) {
-               errors_found |= ERROR_OBJECT;
-               return error("%s: object corrupt or missing",
-                            sha1_to_hex(sha1));
-       }
-       obj->flags |= HAS_OBJ;
-       return fsck_obj(obj);
-}
-
 static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
                           unsigned long size, void *buffer, int *eaten)
 {
@@ -372,34 +396,39 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 
 static int default_refs;
 
-static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1)
+static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
+       timestamp_t timestamp)
 {
        struct object *obj;
 
-       if (!is_null_sha1(sha1)) {
-               obj = lookup_object(sha1);
-               if (obj) {
+       if (!is_null_oid(oid)) {
+               obj = lookup_object(oid->hash);
+               if (obj && (obj->flags & HAS_OBJ)) {
+                       if (timestamp && name_objects)
+                               add_decoration(fsck_walk_options.object_names,
+                                       obj,
+                                       xstrfmt("%s@{%"PRItime"}", refname, timestamp));
                        obj->used = 1;
                        mark_object_reachable(obj);
                } else {
-                       error("%s: invalid reflog entry %s", refname, sha1_to_hex(sha1));
+                       error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
        }
 }
 
-static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-               const char *email, unsigned long timestamp, int tz,
+static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
+               const char *email, timestamp_t timestamp, int tz,
                const char *message, void *cb_data)
 {
        const char *refname = cb_data;
 
        if (verbose)
                fprintf(stderr, "Checking reflog %s->%s\n",
-                       sha1_to_hex(osha1), sha1_to_hex(nsha1));
+                       oid_to_hex(ooid), oid_to_hex(noid));
 
-       fsck_handle_reflog_sha1(refname, osha1);
-       fsck_handle_reflog_sha1(refname, nsha1);
+       fsck_handle_reflog_oid(refname, ooid, 0);
+       fsck_handle_reflog_oid(refname, noid, timestamp);
        return 0;
 }
 
@@ -428,6 +457,9 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
        }
        default_refs++;
        obj->used = 1;
+       if (name_objects)
+               add_decoration(fsck_walk_options.object_names,
+                       obj, xstrdup(refname));
        mark_object_reachable(obj);
 
        return 0;
@@ -459,9 +491,41 @@ static void get_default_heads(void)
        }
 }
 
-static int fsck_loose(const unsigned char *sha1, const char *path, void *data)
+static struct object *parse_loose_object(const struct object_id *oid,
+                                        const char *path)
+{
+       struct object *obj;
+       void *contents;
+       enum object_type type;
+       unsigned long size;
+       int eaten;
+
+       if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0)
+               return NULL;
+
+       if (!contents && type != OBJ_BLOB)
+               die("BUG: read_loose_object streamed a non-blob");
+
+       obj = parse_object_buffer(oid->hash, type, size, contents, &eaten);
+
+       if (!eaten)
+               free(contents);
+       return obj;
+}
+
+static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 {
-       if (fsck_sha1(sha1))
+       struct object *obj = parse_loose_object(oid, path);
+
+       if (!obj) {
+               errors_found |= ERROR_OBJECT;
+               error("%s: object corrupt or missing: %s",
+                     oid_to_hex(oid), path);
+               return 0; /* keep checking other objects */
+       }
+
+       obj->flags = HAS_OBJ;
+       if (fsck_obj(obj))
                errors_found |= ERROR_OBJECT;
        return 0;
 }
@@ -543,6 +607,9 @@ static int fsck_cache_tree(struct cache_tree *it)
                        return 1;
                }
                obj->used = 1;
+               if (name_objects)
+                       add_decoration(fsck_walk_options.object_names,
+                               obj, xstrdup(":"));
                mark_object_reachable(obj);
                if (obj->type != OBJ_TREE)
                        err |= objerror(obj, "non-tree in cache-tree");
@@ -552,6 +619,29 @@ static int fsck_cache_tree(struct cache_tree *it)
        return err;
 }
 
+static void mark_object_for_connectivity(const struct object_id *oid)
+{
+       struct object *obj = lookup_unknown_object(oid->hash);
+       obj->flags |= HAS_OBJ;
+}
+
+static int mark_loose_for_connectivity(const struct object_id *oid,
+                                      const char *path,
+                                      void *data)
+{
+       mark_object_for_connectivity(oid);
+       return 0;
+}
+
+static int mark_packed_for_connectivity(const struct object_id *oid,
+                                       struct packed_git *pack,
+                                       uint32_t pos,
+                                       void *data)
+{
+       mark_object_for_connectivity(oid);
+       return 0;
+}
+
 static char const * const fsck_usage[] = {
        N_("git fsck [<options>] [<object>...]"),
        NULL
@@ -571,6 +661,7 @@ static struct option fsck_opts[] = {
        OPT_BOOL(0, "lost-found", &write_lost_and_found,
                                N_("write dangling objects in .git/lost-found")),
        OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+       OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")),
        OPT_END(),
 };
 
@@ -600,47 +691,48 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                include_reflogs = 0;
        }
 
+       if (name_objects)
+               fsck_walk_options.object_names =
+                       xcalloc(1, sizeof(struct decoration));
+
        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) {
-                       /* directory name, minus trailing slash */
-                       size_t namelen = alt->name - alt->base - 1;
-                       struct strbuf name = STRBUF_INIT;
-                       strbuf_add(&name, alt->base, namelen);
-                       fsck_object_dir(name.buf);
-                       strbuf_release(&name);
-               }
-       }
+               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);
+                       stop_progress(&progress);
                }
-               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);
        }
 
        heads = 0;
@@ -650,16 +742,22 @@ 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 || !(obj->flags & HAS_OBJ)) {
+                               error("%s: object missing", sha1_to_hex(sha1));
+                               errors_found |= ERROR_OBJECT;
                                continue;
+                       }
 
                        obj->used = 1;
+                       if (name_objects)
+                               add_decoration(fsck_walk_options.object_names,
+                                       obj, xstrdup(arg));
                        mark_object_reachable(obj);
                        heads++;
                        continue;
                }
                error("invalid parameter: expected sha1, got '%s'", arg);
+               errors_found |= ERROR_OBJECT;
        }
 
        /*
@@ -667,12 +765,13 @@ 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;
        }
 
        if (keep_cache_objects) {
+               verify_index_checksum = 1;
                read_cache();
                for (i = 0; i < active_nr; i++) {
                        unsigned int mode;
@@ -682,11 +781,15 @@ 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]->sha1);
+                       blob = lookup_blob(active_cache[i]->oid.hash);
                        if (!blob)
                                continue;
                        obj = &blob->object;
                        obj->used = 1;
+                       if (name_objects)
+                               add_decoration(fsck_walk_options.object_names,
+                                       obj,
+                                       xstrfmt(":%s", active_cache[i]->name));
                        mark_object_reachable(obj);
                }
                if (active_cache_tree)