rebase (autostash): store the full OID in <state-dir>/autostash
[gitweb.git] / commit.c
index a7c0b5f8c6ce1d1d41c7ede357116fee4a643bbc..a3fc77a4eb592b64e295ffee9a904c8216415c55 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -17,6 +17,7 @@
 #include "sha1-lookup.h"
 #include "wt-status.h"
 #include "advice.h"
+#include "refs.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -24,24 +25,26 @@ int save_commit_buffer = 1;
 
 const char *commit_type = "commit";
 
-struct commit *lookup_commit_reference_gently(const struct object_id *oid,
-                                             int quiet)
+struct commit *lookup_commit_reference_gently(struct repository *r,
+               const struct object_id *oid, int quiet)
 {
-       struct object *obj = deref_tag(parse_object(oid), NULL, 0);
+       struct object *obj = deref_tag(r,
+                                      parse_object(r, oid),
+                                      NULL, 0);
 
        if (!obj)
                return NULL;
-       return object_as_type(obj, OBJ_COMMIT, quiet);
+       return object_as_type(r, obj, OBJ_COMMIT, quiet);
 }
 
-struct commit *lookup_commit_reference(const struct object_id *oid)
+struct commit *lookup_commit_reference(struct repository *r, const struct object_id *oid)
 {
-       return lookup_commit_reference_gently(oid, 0);
+       return lookup_commit_reference_gently(r, oid, 0);
 }
 
 struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name)
 {
-       struct commit *c = lookup_commit_reference(oid);
+       struct commit *c = lookup_commit_reference(the_repository, oid);
        if (!c)
                die(_("could not parse %s"), ref_name);
        if (oidcmp(oid, &c->object.oid)) {
@@ -51,13 +54,13 @@ struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref
        return c;
 }
 
-struct commit *lookup_commit(const struct object_id *oid)
+struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
 {
-       struct object *obj = lookup_object(oid->hash);
+       struct object *obj = lookup_object(r, oid->hash);
        if (!obj)
-               return create_object(the_repository, oid->hash,
-                                    alloc_commit_node(the_repository));
-       return object_as_type(obj, OBJ_COMMIT, 0);
+               return create_object(r, oid->hash,
+                                    alloc_commit_node(r));
+       return object_as_type(r, obj, OBJ_COMMIT, 0);
 }
 
 struct commit *lookup_commit_reference_by_name(const char *name)
@@ -67,7 +70,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
 
        if (get_oid_committish(name, &oid))
                return NULL;
-       commit = lookup_commit_reference(&oid);
+       commit = lookup_commit_reference(the_repository, &oid);
        if (parse_commit(commit))
                return NULL;
        return commit;
@@ -259,18 +262,32 @@ struct commit_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 buffer_slab *allocate_commit_buffer_slab(void)
 {
-       struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
+       struct buffer_slab *bs = xmalloc(sizeof(*bs));
+       init_buffer_slab(bs);
+       return bs;
+}
+
+void free_commit_buffer_slab(struct buffer_slab *bs)
+{
+       clear_buffer_slab(bs);
+       free(bs);
+}
+
+void set_commit_buffer(struct repository *r, struct commit *commit, void *buffer, unsigned long size)
+{
+       struct commit_buffer *v = buffer_slab_at(
+               r->parsed_objects->buffer_slab, commit);
        v->buffer = buffer;
        v->size = size;
 }
 
-const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
+const void *get_cached_commit_buffer(struct repository *r, const struct commit *commit, unsigned long *sizep)
 {
-       struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
+       struct commit_buffer *v = buffer_slab_peek(
+               r->parsed_objects->buffer_slab, commit);
        if (!v) {
                if (sizep)
                        *sizep = 0;
@@ -283,7 +300,7 @@ const void *get_cached_commit_buffer(const struct commit *commit, unsigned long
 
 const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
 {
-       const void *ret = get_cached_commit_buffer(commit, sizep);
+       const void *ret = get_cached_commit_buffer(the_repository, commit, sizep);
        if (!ret) {
                enum object_type type;
                unsigned long size;
@@ -302,14 +319,16 @@ const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
 
 void unuse_commit_buffer(const struct commit *commit, const void *buffer)
 {
-       struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
+       struct commit_buffer *v = buffer_slab_peek(
+               the_repository->parsed_objects->buffer_slab, commit);
        if (!(v && v->buffer == buffer))
                free((void *)buffer);
 }
 
 void free_commit_buffer(struct commit *commit)
 {
-       struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
+       struct commit_buffer *v = buffer_slab_peek(
+               the_repository->parsed_objects->buffer_slab, commit);
        if (v) {
                FREE_AND_NULL(v->buffer);
                v->size = 0;
@@ -324,7 +343,7 @@ struct tree *get_commit_tree(const struct commit *commit)
        if (commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
                BUG("commit has NULL tree, but was not loaded from commit-graph");
 
-       return get_commit_tree_in_graph(commit);
+       return get_commit_tree_in_graph(the_repository, commit);
 }
 
 struct object_id *get_commit_tree_oid(const struct commit *commit)
@@ -345,7 +364,8 @@ void release_commit_memory(struct commit *c)
 
 const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
 {
-       struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
+       struct commit_buffer *v = buffer_slab_peek(
+               the_repository->parsed_objects->buffer_slab, commit);
        void *ret;
 
        if (!v) {
@@ -362,15 +382,15 @@ const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
        return ret;
 }
 
-int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size, int check_graph)
+int parse_commit_buffer(struct repository *r, struct commit *item, const void *buffer, unsigned long size, int check_graph)
 {
        const char *tail = buffer;
        const char *bufptr = buffer;
        struct object_id parent;
        struct commit_list **pptr;
        struct commit_graft *graft;
-       const int tree_entry_len = GIT_SHA1_HEXSZ + 5;
-       const int parent_entry_len = GIT_SHA1_HEXSZ + 7;
+       const int tree_entry_len = the_hash_algo->hexsz + 5;
+       const int parent_entry_len = the_hash_algo->hexsz + 7;
 
        if (item->object.parsed)
                return 0;
@@ -382,11 +402,11 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
        if (get_oid_hex(bufptr + 5, &parent) < 0)
                return error("bad tree pointer in commit %s",
                             oid_to_hex(&item->object.oid));
-       item->maybe_tree = lookup_tree(&parent);
+       item->maybe_tree = lookup_tree(r, &parent);
        bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
 
-       graft = lookup_commit_graft(the_repository, &item->object.oid);
+       graft = lookup_commit_graft(r, &item->object.oid);
        while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
                struct commit *new_parent;
 
@@ -401,7 +421,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
                 */
                if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
                        continue;
-               new_parent = lookup_commit(&parent);
+               new_parent = lookup_commit(r, &parent);
                if (new_parent)
                        pptr = &commit_list_insert(new_parent, pptr)->next;
        }
@@ -409,7 +429,8 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
                int i;
                struct commit *new_parent;
                for (i = 0; i < graft->nr_parent; i++) {
-                       new_parent = lookup_commit(&graft->parent[i]);
+                       new_parent = lookup_commit(r,
+                                                  &graft->parent[i]);
                        if (!new_parent)
                                continue;
                        pptr = &commit_list_insert(new_parent, pptr)->next;
@@ -418,12 +439,12 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
        item->date = parse_commit_date(bufptr, tail);
 
        if (check_graph)
-               load_commit_graph_info(item);
+               load_commit_graph_info(the_repository, item);
 
        return 0;
 }
 
-int parse_commit_gently(struct commit *item, int quiet_on_missing)
+int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_commit_graph)
 {
        enum object_type type;
        void *buffer;
@@ -434,7 +455,7 @@ int parse_commit_gently(struct commit *item, int quiet_on_missing)
                return -1;
        if (item->object.parsed)
                return 0;
-       if (parse_commit_in_graph(item))
+       if (use_commit_graph && parse_commit_in_graph(the_repository, item))
                return 0;
        buffer = read_object_file(&item->object.oid, &type, &size);
        if (!buffer)
@@ -446,15 +467,21 @@ int parse_commit_gently(struct commit *item, int quiet_on_missing)
                return error("Object %s not a commit",
                             oid_to_hex(&item->object.oid));
        }
-       ret = parse_commit_buffer(item, buffer, size, 0);
+
+       ret = parse_commit_buffer(the_repository, item, buffer, size, 0);
        if (save_commit_buffer && !ret) {
-               set_commit_buffer(item, buffer, size);
+               set_commit_buffer(the_repository, item, buffer, size);
                return 0;
        }
        free(buffer);
        return ret;
 }
 
+int parse_commit_gently(struct commit *item, int quiet_on_missing)
+{
+       return parse_commit_internal(item, quiet_on_missing, 1);
+}
+
 void parse_commit_or_die(struct commit *item)
 {
        if (parse_commit(item))
@@ -932,6 +959,86 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
        return result;
 }
 
+struct rev_collect {
+       struct commit **commit;
+       int nr;
+       int alloc;
+       unsigned int initial : 1;
+};
+
+static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
+{
+       struct commit *commit;
+
+       if (is_null_oid(oid))
+               return;
+
+       commit = lookup_commit(the_repository, oid);
+       if (!commit ||
+           (commit->object.flags & TMP_MARK) ||
+           parse_commit(commit))
+               return;
+
+       ALLOC_GROW(revs->commit, revs->nr + 1, revs->alloc);
+       revs->commit[revs->nr++] = commit;
+       commit->object.flags |= TMP_MARK;
+}
+
+static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
+                                 const char *ident, timestamp_t timestamp,
+                                 int tz, const char *message, void *cbdata)
+{
+       struct rev_collect *revs = cbdata;
+
+       if (revs->initial) {
+               revs->initial = 0;
+               add_one_commit(ooid, revs);
+       }
+       add_one_commit(noid, revs);
+       return 0;
+}
+
+struct commit *get_fork_point(const char *refname, struct commit *commit)
+{
+       struct object_id oid;
+       struct rev_collect revs;
+       struct commit_list *bases;
+       int i;
+       struct commit *ret = NULL;
+
+       memset(&revs, 0, sizeof(revs));
+       revs.initial = 1;
+       for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
+
+       if (!revs.nr && !get_oid(refname, &oid))
+               add_one_commit(&oid, &revs);
+
+       for (i = 0; i < revs.nr; i++)
+               revs.commit[i]->object.flags &= ~TMP_MARK;
+
+       bases = get_merge_bases_many(commit, revs.nr, revs.commit);
+
+       /*
+        * There should be one and only one merge base, when we found
+        * a common ancestor among reflog entries.
+        */
+       if (!bases || bases->next)
+               goto cleanup_return;
+
+       /* And the found one must be one of the reflog entries */
+       for (i = 0; i < revs.nr; i++)
+               if (&bases->item->object == &revs.commit[i]->object)
+                       break; /* found */
+       if (revs.nr <= i)
+               goto cleanup_return;
+
+       ret = bases->item;
+
+cleanup_return:
+       free_commit_list(bases);
+       return ret;
+}
+
 struct commit_list *get_octopus_merge_bases(struct commit_list *in)
 {
        struct commit_list *i, *j, *k, *ret = NULL;
@@ -1692,7 +1799,7 @@ struct commit *get_merge_parent(const char *name)
        struct object_id oid;
        if (get_oid(name, &oid))
                return NULL;
-       obj = parse_object(&oid);
+       obj = parse_object(the_repository, &oid);
        commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
        if (commit && !merge_remote_util(commit))
                set_merge_remote_desc(commit, name, obj);