sha1_file: avoid comparison if no packed hash matches the first byte
[gitweb.git] / fsck.c
diff --git a/fsck.c b/fsck.c
index e41e753d6dcbb577148260a1392210d50fb0b325..c9cf3de8d315c05cd238529ba20fe3925c993ec1 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -9,6 +9,7 @@
 #include "refs.h"
 #include "utf8.h"
 #include "sha1-array.h"
+#include "decorate.h"
 
 #define FSCK_FATAL -1
 #define FSCK_INFO -2
@@ -59,6 +60,7 @@
        FUNC(HAS_DOTGIT, WARN) \
        FUNC(NULL_SHA1, WARN) \
        FUNC(ZERO_PADDED_FILEMODE, WARN) \
+       FUNC(NUL_IN_COMMIT, WARN) \
        /* infos (reported as warnings, but ignored by default) */ \
        FUNC(BAD_TAG_NAME, INFO) \
        FUNC(MISSING_TAGGER_ENTRY, INFO)
@@ -199,7 +201,8 @@ void fsck_set_msg_type(struct fsck_options *options,
 
        if (!options->msg_type) {
                int i;
-               int *msg_type = xmalloc(sizeof(int) * FSCK_MSG_MAX);
+               int *msg_type;
+               ALLOC_ARRAY(msg_type, FSCK_MSG_MAX);
                for (i = 0; i < FSCK_MSG_MAX; i++)
                        msg_type[i] = fsck_msg_type(i, options);
                options->msg_type = msg_type;
@@ -276,7 +279,7 @@ static int report(struct fsck_options *options, struct object *object,
                return 0;
 
        if (options->skiplist && object &&
-                       sha1_array_lookup(options->skiplist, object->sha1) >= 0)
+                       sha1_array_lookup(options->skiplist, object->oid.hash) >= 0)
                return 0;
 
        if (msg_type == FSCK_FATAL)
@@ -288,35 +291,87 @@ static int report(struct fsck_options *options, struct object *object,
 
        va_start(ap, fmt);
        strbuf_vaddf(&sb, fmt, ap);
-       result = options->error_func(object, msg_type, sb.buf);
+       result = options->error_func(options, object, msg_type, sb.buf);
        strbuf_release(&sb);
        va_end(ap);
 
        return result;
 }
 
+static char *get_object_name(struct fsck_options *options, struct object *obj)
+{
+       if (!options->object_names)
+               return NULL;
+       return lookup_decoration(options->object_names, obj);
+}
+
+static void put_object_name(struct fsck_options *options, struct object *obj,
+       const char *fmt, ...)
+{
+       va_list ap;
+       struct strbuf buf = STRBUF_INIT;
+       char *existing;
+
+       if (!options->object_names)
+               return;
+       existing = lookup_decoration(options->object_names, obj);
+       if (existing)
+               return;
+       va_start(ap, fmt);
+       strbuf_vaddf(&buf, fmt, ap);
+       add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));
+       va_end(ap);
+}
+
+static const char *describe_object(struct fsck_options *o, struct object *obj)
+{
+       static struct strbuf buf = STRBUF_INIT;
+       char *name;
+
+       strbuf_reset(&buf);
+       strbuf_addstr(&buf, oid_to_hex(&obj->oid));
+       if (o->object_names && (name = lookup_decoration(o->object_names, obj)))
+               strbuf_addf(&buf, " (%s)", name);
+
+       return buf.buf;
+}
+
 static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
 {
        struct tree_desc desc;
        struct name_entry entry;
        int res = 0;
+       const char *name;
 
        if (parse_tree(tree))
                return -1;
 
+       name = get_object_name(options, &tree->object);
        init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
+               struct object *obj;
                int result;
 
                if (S_ISGITLINK(entry.mode))
                        continue;
-               if (S_ISDIR(entry.mode))
-                       result = options->walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data, options);
-               else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
-                       result = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);
+
+               if (S_ISDIR(entry.mode)) {
+                       obj = &lookup_tree(entry.oid->hash)->object;
+                       if (name)
+                               put_object_name(options, obj, "%s%s/", name,
+                                       entry.path);
+                       result = options->walk(obj, OBJ_TREE, data, options);
+               }
+               else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
+                       obj = &lookup_blob(entry.oid->hash)->object;
+                       if (name)
+                               put_object_name(options, obj, "%s%s", name,
+                                       entry.path);
+                       result = options->walk(obj, 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);
+                                       describe_object(options, &tree->object), entry.path, entry.mode);
                }
                if (result < 0)
                        return result;
@@ -328,20 +383,55 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
 
 static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_options *options)
 {
+       int counter = 0, generation = 0, name_prefix_len = 0;
        struct commit_list *parents;
        int res;
        int result;
+       const char *name;
 
        if (parse_commit(commit))
                return -1;
 
+       name = get_object_name(options, &commit->object);
+       if (name)
+               put_object_name(options, &commit->tree->object, "%s:", name);
+
        result = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);
        if (result < 0)
                return result;
        res = result;
 
        parents = commit->parents;
+       if (name && parents) {
+               int len = strlen(name), power;
+
+               if (len && name[len - 1] == '^') {
+                       generation = 1;
+                       name_prefix_len = len - 1;
+               }
+               else { /* parse ~<generation> suffix */
+                       for (generation = 0, power = 1;
+                            len && isdigit(name[len - 1]);
+                            power *= 10)
+                               generation += power * (name[--len] - '0');
+                       if (power > 1 && len && name[len - 1] == '~')
+                               name_prefix_len = len - 1;
+               }
+       }
+
        while (parents) {
+               if (name) {
+                       struct object *obj = &parents->item->object;
+
+                       if (++counter > 1)
+                               put_object_name(options, obj, "%s^%d",
+                                       name, counter);
+                       else if (generation > 0)
+                               put_object_name(options, obj, "%.*s~%d",
+                                       name_prefix_len, name, generation + 1);
+                       else
+                               put_object_name(options, obj, "%s^", name);
+               }
                result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
                if (result < 0)
                        return result;
@@ -354,8 +444,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 
 static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)
 {
+       char *name = get_object_name(options, &tag->object);
+
        if (parse_tag(tag))
                return -1;
+       if (name)
+               put_object_name(options, tag->tagged, "%s", name);
        return options->walk(tag->tagged, OBJ_ANY, data, options);
 }
 
@@ -373,7 +467,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
        case OBJ_TAG:
                return fsck_walk_tag((struct tag *)obj, data, options);
        default:
-               error("Unknown object type for %s", sha1_to_hex(obj->sha1));
+               error("Unknown object type for %s", describe_object(options, obj));
                return -1;
        }
 }
@@ -449,11 +543,11 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
        while (desc.size) {
                unsigned mode;
                const char *name;
-               const unsigned char *sha1;
+               const struct object_id *oid;
 
-               sha1 = tree_entry_extract(&desc, &name, &mode);
+               oid = tree_entry_extract(&desc, &name, &mode);
 
-               has_null_sha1 |= is_null_sha1(sha1);
+               has_null_sha1 |= is_null_oid(oid);
                has_full_path |= !!strchr(name, '/');
                has_empty_name |= !*name;
                has_dot |= !strcmp(name, ".");
@@ -609,6 +703,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
        struct commit_graft *graft;
        unsigned parent_count, parent_line_count = 0, author_count;
        int err;
+       const char *buffer_begin = buffer;
 
        if (verify_headers(buffer, size, &commit->object, options))
                return -1;
@@ -630,7 +725,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
                buffer += 41;
                parent_line_count++;
        }
-       graft = lookup_commit_graft(commit->object.sha1);
+       graft = lookup_commit_graft(commit->object.oid.hash);
        parent_count = commit_list_count(commit->parents);
        if (graft) {
                if (graft->nr_parent == -1 && !parent_count)
@@ -665,9 +760,17 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
        err = fsck_ident(&buffer, &commit->object, options);
        if (err)
                return err;
-       if (!commit->tree)
-               return report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
-
+       if (!commit->tree) {
+               err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
+               if (err)
+                       return err;
+       }
+       if (memchr(buffer_begin, '\0', size)) {
+               err = report(options, &commit->object, FSCK_MSG_NUL_IN_COMMIT,
+                            "NUL byte in the commit object body");
+               if (err)
+                       return err;
+       }
        return 0;
 }
 
@@ -696,7 +799,7 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,
                enum object_type type;
 
                buffer = to_free =
-                       read_sha1_file(tag->object.sha1, &type, &size);
+                       read_sha1_file(tag->object.oid.hash, &type, &size);
                if (!buffer)
                        return report(options, &tag->object,
                                FSCK_MSG_MISSING_TAG_OBJECT,
@@ -711,7 +814,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,
                }
        }
 
-       if (verify_headers(buffer, size, &tag->object, options))
+       ret = verify_headers(buffer, size, &tag->object, options);
+       if (ret)
                goto done;
 
        if (!skip_prefix(buffer, "object ", &buffer)) {
@@ -806,12 +910,13 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
                          obj->type);
 }
 
-int fsck_error_function(struct object *obj, int msg_type, const char *message)
+int fsck_error_function(struct fsck_options *o,
+       struct object *obj, int msg_type, const char *message)
 {
        if (msg_type == FSCK_WARN) {
-               warning("object %s: %s", sha1_to_hex(obj->sha1), message);
+               warning("object %s: %s", describe_object(o, obj), message);
                return 0;
        }
-       error("object %s: %s", sha1_to_hex(obj->sha1), message);
+       error("object %s: %s", describe_object(o, obj), message);
        return 1;
 }