summaryrefslogtreecommitdiff
path: root/commit.c
diff options
context:
space:
mode:
Diffstat (limited to 'commit.c')
-rw-r--r--commit.c254
1 files changed, 178 insertions, 76 deletions
diff --git a/commit.c b/commit.c
index ff51c9f34a..a7c0b5f8c6 100644
--- a/commit.c
+++ b/commit.c
@@ -1,17 +1,22 @@
#include "cache.h"
#include "tag.h"
#include "commit.h"
+#include "commit-graph.h"
+#include "repository.h"
+#include "object-store.h"
#include "pkt-line.h"
#include "utf8.h"
#include "diff.h"
#include "revision.h"
#include "notes.h"
+#include "alloc.h"
#include "gpg-interface.h"
#include "mergesort.h"
#include "commit-slab.h"
#include "prio-queue.h"
#include "sha1-lookup.h"
#include "wt-status.h"
+#include "advice.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -50,7 +55,8 @@ struct commit *lookup_commit(const struct object_id *oid)
{
struct object *obj = lookup_object(oid->hash);
if (!obj)
- return create_object(oid->hash, alloc_commit_node());
+ return create_object(the_repository, oid->hash,
+ alloc_commit_node(the_repository));
return object_as_type(obj, OBJ_COMMIT, 0);
}
@@ -94,43 +100,44 @@ static timestamp_t parse_commit_date(const char *buf, const char *tail)
return parse_timestamp(dateptr, NULL, 10);
}
-static struct commit_graft **commit_graft;
-static int commit_graft_alloc, commit_graft_nr;
-
static const unsigned char *commit_graft_sha1_access(size_t index, void *table)
{
struct commit_graft **commit_graft_table = table;
return commit_graft_table[index]->oid.hash;
}
-static int commit_graft_pos(const unsigned char *sha1)
+static int commit_graft_pos(struct repository *r, const unsigned char *sha1)
{
- return sha1_pos(sha1, commit_graft, commit_graft_nr,
+ return sha1_pos(sha1, r->parsed_objects->grafts,
+ r->parsed_objects->grafts_nr,
commit_graft_sha1_access);
}
-int register_commit_graft(struct commit_graft *graft, int ignore_dups)
+int register_commit_graft(struct repository *r, struct commit_graft *graft,
+ int ignore_dups)
{
- int pos = commit_graft_pos(graft->oid.hash);
+ int pos = commit_graft_pos(r, graft->oid.hash);
if (0 <= pos) {
if (ignore_dups)
free(graft);
else {
- free(commit_graft[pos]);
- commit_graft[pos] = graft;
+ free(r->parsed_objects->grafts[pos]);
+ r->parsed_objects->grafts[pos] = graft;
}
return 1;
}
pos = -pos - 1;
- ALLOC_GROW(commit_graft, commit_graft_nr + 1, commit_graft_alloc);
- commit_graft_nr++;
- if (pos < commit_graft_nr)
- memmove(commit_graft + pos + 1,
- commit_graft + pos,
- (commit_graft_nr - pos - 1) *
- sizeof(*commit_graft));
- commit_graft[pos] = graft;
+ ALLOC_GROW(r->parsed_objects->grafts,
+ r->parsed_objects->grafts_nr + 1,
+ r->parsed_objects->grafts_alloc);
+ r->parsed_objects->grafts_nr++;
+ if (pos < r->parsed_objects->grafts_nr)
+ memmove(r->parsed_objects->grafts + pos + 1,
+ r->parsed_objects->grafts + pos,
+ (r->parsed_objects->grafts_nr - pos - 1) *
+ sizeof(*r->parsed_objects->grafts));
+ r->parsed_objects->grafts[pos] = graft;
return 0;
}
@@ -172,18 +179,27 @@ bad_graft_data:
return NULL;
}
-static int read_graft_file(const char *graft_file)
+static int read_graft_file(struct repository *r, const char *graft_file)
{
FILE *fp = fopen_or_warn(graft_file, "r");
struct strbuf buf = STRBUF_INIT;
if (!fp)
return -1;
+ if (advice_graft_file_deprecated)
+ advise(_("Support for <GIT_DIR>/info/grafts is deprecated\n"
+ "and will be removed in a future Git version.\n"
+ "\n"
+ "Please use \"git replace --convert-graft-file\"\n"
+ "to convert the grafts into replace refs.\n"
+ "\n"
+ "Turn this message off by running\n"
+ "\"git config advice.graftFileDeprecated false\""));
while (!strbuf_getwholeline(&buf, fp, '\n')) {
/* The format is just "Commit Parent1 Parent2 ...\n" */
struct commit_graft *graft = read_graft_line(&buf);
if (!graft)
continue;
- if (register_commit_graft(graft, 1))
+ if (register_commit_graft(r, graft, 1))
error("duplicate graft data: %s", buf.buf);
}
fclose(fp);
@@ -191,47 +207,50 @@ static int read_graft_file(const char *graft_file)
return 0;
}
-static void prepare_commit_graft(void)
+static void prepare_commit_graft(struct repository *r)
{
- static int commit_graft_prepared;
char *graft_file;
- if (commit_graft_prepared)
+ if (r->parsed_objects->commit_graft_prepared)
+ return;
+ if (!startup_info->have_repository)
return;
- graft_file = get_graft_file();
- read_graft_file(graft_file);
+
+ graft_file = get_graft_file(r);
+ read_graft_file(r, graft_file);
/* make sure shallows are read */
- is_repository_shallow();
- commit_graft_prepared = 1;
+ is_repository_shallow(r);
+ r->parsed_objects->commit_graft_prepared = 1;
}
-struct commit_graft *lookup_commit_graft(const struct object_id *oid)
+struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid)
{
int pos;
- prepare_commit_graft();
- pos = commit_graft_pos(oid->hash);
+ prepare_commit_graft(r);
+ pos = commit_graft_pos(r, oid->hash);
if (pos < 0)
return NULL;
- return commit_graft[pos];
+ return r->parsed_objects->grafts[pos];
}
int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
{
int i, ret;
- for (i = ret = 0; i < commit_graft_nr && !ret; i++)
- ret = fn(commit_graft[i], cb_data);
+ for (i = ret = 0; i < the_repository->parsed_objects->grafts_nr && !ret; i++)
+ ret = fn(the_repository->parsed_objects->grafts[i], cb_data);
return ret;
}
int unregister_shallow(const struct object_id *oid)
{
- int pos = commit_graft_pos(oid->hash);
+ int pos = commit_graft_pos(the_repository, oid->hash);
if (pos < 0)
return -1;
- if (pos + 1 < commit_graft_nr)
- MOVE_ARRAY(commit_graft + pos, commit_graft + pos + 1,
- commit_graft_nr - pos - 1);
- commit_graft_nr--;
+ if (pos + 1 < the_repository->parsed_objects->grafts_nr)
+ MOVE_ARRAY(the_repository->parsed_objects->grafts + pos,
+ the_repository->parsed_objects->grafts + pos + 1,
+ the_repository->parsed_objects->grafts_nr - pos - 1);
+ the_repository->parsed_objects->grafts_nr--;
return 0;
}
@@ -268,13 +287,13 @@ const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
if (!ret) {
enum object_type type;
unsigned long size;
- ret = read_sha1_file(commit->object.oid.hash, &type, &size);
+ ret = read_object_file(&commit->object.oid, &type, &size);
if (!ret)
die("cannot read commit object %s",
oid_to_hex(&commit->object.oid));
if (type != OBJ_COMMIT)
die("expected commit for %s, got %s",
- oid_to_hex(&commit->object.oid), typename(type));
+ oid_to_hex(&commit->object.oid), type_name(type));
if (sizep)
*sizep = size;
}
@@ -297,6 +316,33 @@ void free_commit_buffer(struct commit *commit)
}
}
+struct tree *get_commit_tree(const struct commit *commit)
+{
+ if (commit->maybe_tree || !commit->object.parsed)
+ return commit->maybe_tree;
+
+ 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);
+}
+
+struct object_id *get_commit_tree_oid(const struct commit *commit)
+{
+ return &get_commit_tree(commit)->object.oid;
+}
+
+void release_commit_memory(struct commit *c)
+{
+ c->maybe_tree = NULL;
+ c->index = 0;
+ free_commit_buffer(c);
+ free_commit_list(c->parents);
+ /* TODO: what about commit->util? */
+
+ c->object.parsed = 0;
+}
+
const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
{
struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit);
@@ -316,7 +362,7 @@ 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 parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size, int check_graph)
{
const char *tail = buffer;
const char *bufptr = buffer;
@@ -333,19 +379,19 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
bufptr[tree_entry_len] != '\n')
return error("bogus commit object %s", oid_to_hex(&item->object.oid));
- if (get_sha1_hex(bufptr + 5, parent.hash) < 0)
+ if (get_oid_hex(bufptr + 5, &parent) < 0)
return error("bad tree pointer in commit %s",
oid_to_hex(&item->object.oid));
- item->tree = lookup_tree(&parent);
+ item->maybe_tree = lookup_tree(&parent);
bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
pptr = &item->parents;
- graft = lookup_commit_graft(&item->object.oid);
+ graft = lookup_commit_graft(the_repository, &item->object.oid);
while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
struct commit *new_parent;
if (tail <= bufptr + parent_entry_len + 1 ||
- get_sha1_hex(bufptr + 7, parent.hash) ||
+ get_oid_hex(bufptr + 7, &parent) ||
bufptr[parent_entry_len] != '\n')
return error("bad parents in commit %s", oid_to_hex(&item->object.oid));
bufptr += parent_entry_len + 1;
@@ -371,6 +417,9 @@ 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);
+
return 0;
}
@@ -385,7 +434,9 @@ int parse_commit_gently(struct commit *item, int quiet_on_missing)
return -1;
if (item->object.parsed)
return 0;
- buffer = read_sha1_file(item->object.oid.hash, &type, &size);
+ if (parse_commit_in_graph(item))
+ return 0;
+ buffer = read_object_file(&item->object.oid, &type, &size);
if (!buffer)
return quiet_on_missing ? -1 :
error("Could not read %s",
@@ -395,7 +446,7 @@ 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);
+ ret = parse_commit_buffer(item, buffer, size, 0);
if (save_commit_buffer && !ret) {
set_commit_buffer(item, buffer, size);
return 0;
@@ -623,6 +674,24 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
return 0;
}
+int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
+{
+ const struct commit *a = a_, *b = b_;
+
+ /* newer commits first */
+ if (a->generation < b->generation)
+ return 1;
+ else if (a->generation > b->generation)
+ return -1;
+
+ /* use date as a heuristic when generations are equal */
+ if (a->date < b->date)
+ return 1;
+ else if (a->date > b->date)
+ return -1;
+ return 0;
+}
+
int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
{
const struct commit *a = a_, *b = b_;
@@ -770,11 +839,14 @@ static int queue_has_nonstale(struct prio_queue *queue)
}
/* all input commits in one and twos[] must have been parsed! */
-static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
+static struct commit_list *paint_down_to_common(struct commit *one, int n,
+ struct commit **twos,
+ int min_generation)
{
- struct prio_queue queue = { compare_commits_by_commit_date };
+ struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
struct commit_list *result = NULL;
int i;
+ uint32_t last_gen = GENERATION_NUMBER_INFINITY;
one->object.flags |= PARENT1;
if (!n) {
@@ -793,6 +865,15 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc
struct commit_list *parents;
int flags;
+ if (commit->generation > last_gen)
+ BUG("bad generation skip %8x > %8x at %s",
+ commit->generation, last_gen,
+ oid_to_hex(&commit->object.oid));
+ last_gen = commit->generation;
+
+ if (commit->generation < min_generation)
+ break;
+
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) {
if (!(commit->object.flags & RESULT)) {
@@ -841,7 +922,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
return NULL;
}
- list = paint_down_to_common(one, n, twos);
+ list = paint_down_to_common(one, n, twos, 0);
while (list) {
struct commit *commit = pop_commit(&list);
@@ -861,19 +942,19 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
commit_list_insert(in->item, &ret);
for (i = in->next; i; i = i->next) {
- struct commit_list *new = NULL, *end = NULL;
+ struct commit_list *new_commits = NULL, *end = NULL;
for (j = ret; j; j = j->next) {
struct commit_list *bases;
bases = get_merge_bases(i->item, j->item);
- if (!new)
- new = bases;
+ if (!new_commits)
+ new_commits = bases;
else
end->next = bases;
for (k = bases; k; k = k->next)
end = k;
}
- ret = new;
+ ret = new_commits;
}
return ret;
}
@@ -899,6 +980,7 @@ static int remove_redundant(struct commit **array, int cnt)
parse_commit(array[i]);
for (i = 0; i < cnt; i++) {
struct commit_list *common;
+ uint32_t min_generation = array[i]->generation;
if (redundant[i])
continue;
@@ -907,8 +989,12 @@ static int remove_redundant(struct commit **array, int cnt)
continue;
filled_index[filled] = j;
work[filled++] = array[j];
+
+ if (array[j]->generation < min_generation)
+ min_generation = array[j]->generation;
}
- common = paint_down_to_common(array[i], filled, work);
+ common = paint_down_to_common(array[i], filled, work,
+ min_generation);
if (array[i]->object.flags & PARENT2)
redundant[i] = 1;
for (j = 0; j < filled; j++)
@@ -1018,14 +1104,21 @@ int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit *
{
struct commit_list *bases;
int ret = 0, i;
+ uint32_t min_generation = GENERATION_NUMBER_INFINITY;
if (parse_commit(commit))
return ret;
- for (i = 0; i < nr_reference; i++)
+ for (i = 0; i < nr_reference; i++) {
if (parse_commit(reference[i]))
return ret;
+ if (reference[i]->generation < min_generation)
+ min_generation = reference[i]->generation;
+ }
+
+ if (commit->generation > min_generation)
+ return ret;
- bases = paint_down_to_common(commit, nr_reference, reference);
+ bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
if (commit->object.flags & PARENT2)
ret = 1;
clear_commit_marks(commit, all_flags);
@@ -1208,7 +1301,7 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
desc = merge_remote_util(parent);
if (!desc || !desc->obj)
return;
- buf = read_sha1_file(desc->obj->oid.hash, &type, &size);
+ buf = read_object_file(&desc->obj->oid, &type, &size);
if (!buf || type != OBJ_TAG)
goto free_return;
len = parse_signature(buf, size);
@@ -1290,17 +1383,19 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,
return extra;
}
-void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
+int for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data)
{
struct commit_extra_header *extra, *to_free;
+ int res = 0;
to_free = read_commit_extra_headers(commit, NULL);
- for (extra = to_free; extra; extra = extra->next) {
+ for (extra = to_free; !res && extra; extra = extra->next) {
if (strcmp(extra->key, "mergetag"))
continue; /* not a merge tag */
- fn(commit, extra, data);
+ res = fn(commit, extra, data);
}
free_commit_extra_headers(to_free);
+ return res;
}
static inline int standard_header_field(const char *field, size_t len)
@@ -1380,9 +1475,8 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
}
}
-int commit_tree(const char *msg, size_t msg_len,
- const unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
+int commit_tree(const char *msg, size_t msg_len, const struct object_id *tree,
+ struct commit_list *parents, struct object_id *ret,
const char *author, const char *sign_commit)
{
struct commit_extra_header *extra = NULL, **tail = &extra;
@@ -1511,8 +1605,8 @@ N_("Warning: commit message did not conform to UTF-8.\n"
"variable i18n.commitencoding to the encoding your project uses.\n");
int commit_tree_extended(const char *msg, size_t msg_len,
- const unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
+ const struct object_id *tree,
+ struct commit_list *parents, struct object_id *ret,
const char *author, const char *sign_commit,
struct commit_extra_header *extra)
{
@@ -1520,7 +1614,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
int encoding_is_utf8;
struct strbuf buffer;
- assert_sha1_type(tree, OBJ_TREE);
+ assert_oid_type(tree, OBJ_TREE);
if (memchr(msg, '\0', msg_len))
return error("a NUL byte in commit log message not allowed.");
@@ -1529,7 +1623,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
- strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
+ strbuf_addf(&buffer, "tree %s\n", oid_to_hex(tree));
/*
* NOTE! This ordering means that the same exact tree merged with a
@@ -1568,19 +1662,27 @@ int commit_tree_extended(const char *msg, size_t msg_len,
goto out;
}
- result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+ result = write_object_file(buffer.buf, buffer.len, commit_type, ret);
out:
strbuf_release(&buffer);
return result;
}
+define_commit_slab(merge_desc_slab, struct merge_remote_desc *);
+static struct merge_desc_slab merge_desc_slab = COMMIT_SLAB_INIT(1, merge_desc_slab);
+
+struct merge_remote_desc *merge_remote_util(struct commit *commit)
+{
+ return *merge_desc_slab_at(&merge_desc_slab, commit);
+}
+
void set_merge_remote_desc(struct commit *commit,
const char *name, struct object *obj)
{
struct merge_remote_desc *desc;
FLEX_ALLOC_STR(desc, name, name);
desc->obj = obj;
- commit->util = desc;
+ *merge_desc_slab_at(&merge_desc_slab, commit) = desc;
}
struct commit *get_merge_parent(const char *name)
@@ -1592,7 +1694,7 @@ struct commit *get_merge_parent(const char *name)
return NULL;
obj = parse_object(&oid);
commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
- if (commit && !commit->util)
+ if (commit && !merge_remote_util(commit))
set_merge_remote_desc(commit, name, obj);
return commit;
}
@@ -1617,11 +1719,11 @@ struct commit *get_merge_parent(const char *name)
struct commit_list **commit_list_append(struct commit *commit,
struct commit_list **next)
{
- struct commit_list *new = xmalloc(sizeof(struct commit_list));
- new->item = commit;
- *next = new;
- new->next = NULL;
- return &new->next;
+ struct commit_list *new_commit = xmalloc(sizeof(struct commit_list));
+ new_commit->item = commit;
+ *next = new_commit;
+ new_commit->next = NULL;
+ return &new_commit->next;
}
const char *find_commit_header(const char *msg, const char *key, size_t *out_len)