index-pack: avoid excessive re-reading of pack directory
[gitweb.git] / commit.c
index f4793316a21fe5f3ee1932f7e24cf4ab0920b04c..65179f96eecc85082f9bf3f0312676878d92861d 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -17,20 +17,6 @@ static struct commit_extra_header *read_commit_extra_header_lines(const char *bu
 int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
-static int commit_count;
-
-static struct commit *check_commit(struct object *obj,
-                                  const unsigned char *sha1,
-                                  int quiet)
-{
-       if (obj->type != OBJ_COMMIT) {
-               if (!quiet)
-                       error("Object %s is a %s, not a commit",
-                             sha1_to_hex(sha1), typename(obj->type));
-               return NULL;
-       }
-       return (struct commit *) obj;
-}
 
 struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
                                              int quiet)
@@ -39,7 +25,7 @@ struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
 
        if (!obj)
                return NULL;
-       return check_commit(obj, sha1, quiet);
+       return object_as_type(obj, OBJ_COMMIT, quiet);
 }
 
 struct commit *lookup_commit_reference(const unsigned char *sha1)
@@ -62,14 +48,9 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n
 struct commit *lookup_commit(const unsigned char *sha1)
 {
        struct object *obj = lookup_object(sha1);
-       if (!obj) {
-               struct commit *c = alloc_commit_node();
-               c->index = commit_count++;
-               return create_object(sha1, OBJ_COMMIT, c);
-       }
-       if (!obj->type)
-               obj->type = OBJ_COMMIT;
-       return check_commit(obj, sha1, 0);
+       if (!obj)
+               return create_object(sha1, alloc_commit_node());
+       return object_as_type(obj, OBJ_COMMIT, 0);
 }
 
 struct commit *lookup_commit_reference_by_name(const char *name)
@@ -247,6 +228,76 @@ int unregister_shallow(const unsigned char *sha1)
        return 0;
 }
 
+struct commit_buffer {
+       void *buffer;
+       unsigned long size;
+};
+define_commit_slab(buffer_slab, struct commit_buffer);
+static struct buffer_slab buffer_slab = COMMIT_SLAB_INIT(1, buffer_slab);
+
+void set_commit_buffer(struct commit *commit, void *buffer, unsigned long size)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       v->buffer = buffer;
+       v->size = size;
+}
+
+const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       if (sizep)
+               *sizep = v->size;
+       return v->buffer;
+}
+
+const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
+{
+       const void *ret = get_cached_commit_buffer(commit, sizep);
+       if (!ret) {
+               enum object_type type;
+               unsigned long size;
+               ret = read_sha1_file(commit->object.sha1, &type, &size);
+               if (!ret)
+                       die("cannot read commit object %s",
+                           sha1_to_hex(commit->object.sha1));
+               if (type != OBJ_COMMIT)
+                       die("expected commit for %s, got %s",
+                           sha1_to_hex(commit->object.sha1), typename(type));
+               if (sizep)
+                       *sizep = size;
+       }
+       return ret;
+}
+
+void unuse_commit_buffer(const struct commit *commit, const void *buffer)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       if (v->buffer != buffer)
+               free((void *)buffer);
+}
+
+void free_commit_buffer(struct commit *commit)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       free(v->buffer);
+       v->buffer = NULL;
+       v->size = 0;
+}
+
+const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
+{
+       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       void *ret;
+
+       ret = v->buffer;
+       if (sizep)
+               *sizep = v->size;
+
+       v->buffer = NULL;
+       v->size = 0;
+       return ret;
+}
+
 int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
 {
        const char *tail = buffer;
@@ -324,7 +375,7 @@ int parse_commit(struct commit *item)
        }
        ret = parse_commit_buffer(item, buffer, size);
        if (save_commit_buffer && !ret) {
-               item->buffer = buffer;
+               set_commit_buffer(item, buffer, size);
                return 0;
        }
        free(buffer);
@@ -539,22 +590,12 @@ static void record_author_date(struct author_date_slab *author_date,
                               struct commit *commit)
 {
        const char *buf, *line_end, *ident_line;
-       char *buffer = NULL;
+       const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
        char *date_end;
        unsigned long date;
 
-       if (!commit->buffer) {
-               unsigned long size;
-               enum object_type type;
-               buffer = read_sha1_file(commit->object.sha1, &type, &size);
-               if (!buffer)
-                       return;
-       }
-
-       for (buf = commit->buffer ? commit->buffer : buffer;
-            buf;
-            buf = line_end + 1) {
+       for (buf = buffer; buf; buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
                ident_line = skip_prefix(buf, "author ");
                if (!ident_line) {
@@ -575,7 +616,7 @@ static void record_author_date(struct author_date_slab *author_date,
        *(author_date_slab_at(author_date, commit)) = date;
 
 fail_exit:
-       free(buffer);
+       unuse_commit_buffer(commit, buffer);
 }
 
 static int compare_commits_by_author_date(const void *a_, const void *b_,
@@ -1031,7 +1072,7 @@ struct commit_list *reduce_heads(struct commit_list *heads)
                p->item->object.flags |= STALE;
                num_head++;
        }
-       array = xcalloc(sizeof(*array), num_head);
+       array = xcalloc(num_head, sizeof(*array));
        for (p = heads, i = 0; p; p = p->next) {
                if (p->item->object.flags & STALE) {
                        array[i++] = p->item;
@@ -1080,17 +1121,14 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid)
        return 0;
 }
 
-int parse_signed_commit(const unsigned char *sha1,
+int parse_signed_commit(const struct commit *commit,
                        struct strbuf *payload, struct strbuf *signature)
 {
+
        unsigned long size;
-       enum object_type type;
-       char *buffer = read_sha1_file(sha1, &type, &size);
+       const char *buffer = get_commit_buffer(commit, &size);
        int in_signature, saw_signature = -1;
-       char *line, *tail;
-
-       if (!buffer || type != OBJ_COMMIT)
-               goto cleanup;
+       const char *line, *tail;
 
        line = buffer;
        tail = buffer + size;
@@ -1098,7 +1136,7 @@ int parse_signed_commit(const unsigned char *sha1,
        saw_signature = 0;
        while (line < tail) {
                const char *sig = NULL;
-               char *next = memchr(line, '\n', tail - line);
+               const char *next = memchr(line, '\n', tail - line);
 
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
@@ -1119,8 +1157,7 @@ int parse_signed_commit(const unsigned char *sha1,
                }
                line = next;
        }
- cleanup:
-       free(buffer);
+       unuse_commit_buffer(commit, buffer);
        return saw_signature;
 }
 
@@ -1211,8 +1248,7 @@ void check_commit_signature(const struct commit* commit, struct signature_check
 
        sigc->result = 'N';
 
-       if (parse_signed_commit(commit->object.sha1,
-                               &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
        status = verify_signed_buffer(payload.buf, payload.len,
                                      signature.buf, signature.len,
@@ -1257,11 +1293,9 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
 {
        struct commit_extra_header *extra = NULL;
        unsigned long size;
-       enum object_type type;
-       char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
-       if (buffer && type == OBJ_COMMIT)
-               extra = read_commit_extra_header_lines(buffer, size, exclude);
-       free(buffer);
+       const char *buffer = get_commit_buffer(commit, &size);
+       extra = read_commit_extra_header_lines(buffer, size, exclude);
+       unuse_commit_buffer(commit, buffer);
        return extra;
 }
 
@@ -1344,7 +1378,8 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
        }
 }
 
-int commit_tree(const struct strbuf *msg, const unsigned char *tree,
+int commit_tree(const char *msg, size_t msg_len,
+               const unsigned char *tree,
                struct commit_list *parents, unsigned char *ret,
                const char *author, const char *sign_commit)
 {
@@ -1352,7 +1387,7 @@ int commit_tree(const struct strbuf *msg, const unsigned char *tree,
        int result;
 
        append_merge_tag_headers(parents, &tail);
-       result = commit_tree_extended(msg, tree, parents, ret,
+       result = commit_tree_extended(msg, msg_len, tree, parents, ret,
                                      author, sign_commit, extra);
        free_commit_extra_headers(extra);
        return result;
@@ -1473,7 +1508,8 @@ static const char commit_utf8_warn[] =
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
+int commit_tree_extended(const char *msg, size_t msg_len,
+                        const unsigned char *tree,
                         struct commit_list *parents, unsigned char *ret,
                         const char *author, const char *sign_commit,
                         struct commit_extra_header *extra)
@@ -1484,7 +1520,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
 
        assert_sha1_type(tree, OBJ_TREE);
 
-       if (memchr(msg->buf, '\0', msg->len))
+       if (memchr(msg, '\0', msg_len))
                return error("a NUL byte in commit log message not allowed.");
 
        /* Not having i18n.commitencoding is the same as having utf-8 */
@@ -1523,7 +1559,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
        strbuf_addch(&buffer, '\n');
 
        /* And add the comment */
-       strbuf_addbuf(&buffer, msg);
+       strbuf_add(&buffer, msg, msg_len);
 
        /* And check the encoding */
        if (encoding_is_utf8 && !verify_utf8(&buffer))