summaryrefslogtreecommitdiff
path: root/commit.c
diff options
context:
space:
mode:
Diffstat (limited to 'commit.c')
-rw-r--r--commit.c303
1 files changed, 211 insertions, 92 deletions
diff --git a/commit.c b/commit.c
index ef679a0b93..540660359d 100644
--- a/commit.c
+++ b/commit.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "tag.h"
#include "commit.h"
@@ -27,6 +29,7 @@
#include "tree.h"
#include "hook.h"
#include "parse.h"
+#include "object-file-convert.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -82,12 +85,18 @@ struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
struct commit *lookup_commit_reference_by_name(const char *name)
{
+ return lookup_commit_reference_by_name_gently(name, 0);
+}
+
+struct commit *lookup_commit_reference_by_name_gently(const char *name,
+ int quiet)
+{
struct object_id oid;
struct commit *commit;
if (repo_get_oid_committish(the_repository, name, &oid))
return NULL;
- commit = lookup_commit_reference(the_repository, &oid);
+ commit = lookup_commit_reference_gently(the_repository, &oid, quiet);
if (repo_parse_commit(the_repository, commit))
return NULL;
return commit;
@@ -174,7 +183,7 @@ int commit_graft_pos(struct repository *r, const struct object_id *oid)
commit_graft_oid_access);
}
-static void unparse_commit(struct repository *r, const struct object_id *oid)
+void unparse_commit(struct repository *r, const struct object_id *oid)
{
struct commit *c = lookup_commit(r, oid);
@@ -267,7 +276,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)
"to convert the grafts into replace refs.\n"
"\n"
"Turn this message off by running\n"
- "\"git config advice.graftFileDeprecated false\""));
+ "\"git config set 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);
@@ -283,14 +292,14 @@ static int read_graft_file(struct repository *r, const char *graft_file)
void prepare_commit_graft(struct repository *r)
{
- char *graft_file;
+ const char *graft_file;
if (r->parsed_objects->commit_graft_prepared)
return;
if (!startup_info->have_repository)
return;
- graft_file = get_graft_file(r);
+ graft_file = repo_get_graft_file(r);
read_graft_file(r, graft_file);
/* make sure shallows are read */
is_repository_shallow(r);
@@ -315,18 +324,6 @@ int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
return ret;
}
-void reset_commit_grafts(struct repository *r)
-{
- int i;
-
- for (i = 0; i < r->parsed_objects->grafts_nr; i++) {
- unparse_commit(r, &r->parsed_objects->grafts[i]->oid);
- free(r->parsed_objects->grafts[i]);
- }
- r->parsed_objects->grafts_nr = 0;
- r->parsed_objects->commit_graft_prepared = 0;
-}
-
struct commit_buffer {
void *buffer;
unsigned long size;
@@ -598,7 +595,8 @@ int repo_parse_commit_internal(struct repository *r,
}
ret = parse_commit_buffer(r, item, buffer, size, 0);
- if (save_commit_buffer && !ret) {
+ if (save_commit_buffer && !ret &&
+ !get_cached_commit_buffer(r, item, NULL)) {
set_commit_buffer(r, item, buffer, size);
return 0;
}
@@ -679,7 +677,7 @@ unsigned commit_list_count(const struct commit_list *l)
return c;
}
-struct commit_list *copy_commit_list(struct commit_list *list)
+struct commit_list *copy_commit_list(const struct commit_list *list)
{
struct commit_list *head = NULL;
struct commit_list **pp = &head;
@@ -780,11 +778,11 @@ static void clear_commit_marks_1(struct commit_list **plist,
}
}
-void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark)
+void clear_commit_marks_many(size_t nr, struct commit **commit, unsigned int mark)
{
struct commit_list *list = NULL;
- while (nr--) {
+ for (size_t i = 0; i < nr; i++) {
clear_commit_marks_1(&list, *commit, mark);
commit++;
}
@@ -1052,7 +1050,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
{
struct object_id oid;
struct rev_collect revs;
- struct commit_list *bases;
+ struct commit_list *bases = NULL;
int i;
struct commit *ret = NULL;
char *full_refname;
@@ -1069,7 +1067,8 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
memset(&revs, 0, sizeof(revs));
revs.initial = 1;
- for_each_reflog_ent(full_refname, collect_one_reflog_ent, &revs);
+ refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+ full_refname, collect_one_reflog_ent, &revs);
if (!revs.nr)
add_one_commit(&oid, &revs);
@@ -1077,8 +1076,9 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
for (i = 0; i < revs.nr; i++)
revs.commit[i]->object.flags &= ~TMP_MARK;
- bases = repo_get_merge_bases_many(the_repository, commit, revs.nr,
- revs.commit);
+ if (repo_get_merge_bases_many(the_repository, commit, revs.nr,
+ revs.commit, &bases) < 0)
+ exit(128);
/*
* There should be one and only one merge base, when we found
@@ -1112,12 +1112,11 @@ static const char *gpg_sig_headers[] = {
"gpgsig-sha256",
};
-int sign_with_header(struct strbuf *buf, const char *keyid)
+int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct git_hash_algo *algo)
{
- struct strbuf sig = STRBUF_INIT;
int inspos, copypos;
const char *eoh;
- const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
+ const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algo)];
int gpg_sig_header_len = strlen(gpg_sig_header);
/* find the end of the header */
@@ -1127,15 +1126,8 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
else
inspos = eoh - buf->buf + 1;
- if (!keyid || !*keyid)
- keyid = get_signing_key();
- if (sign_buffer(buf, &sig, keyid)) {
- strbuf_release(&sig);
- return -1;
- }
-
- for (copypos = 0; sig.buf[copypos]; ) {
- const char *bol = sig.buf + copypos;
+ for (copypos = 0; sig->buf[copypos]; ) {
+ const char *bol = sig->buf + copypos;
const char *eol = strchrnul(bol, '\n');
int len = (eol - bol) + !!*eol;
@@ -1148,11 +1140,20 @@ int sign_with_header(struct strbuf *buf, const char *keyid)
inspos += len;
copypos += len;
}
- strbuf_release(&sig);
return 0;
}
-
+static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid)
+{
+ char *keyid_to_free = NULL;
+ int ret = 0;
+ if (!keyid || !*keyid)
+ keyid = keyid_to_free = get_signing_key();
+ if (sign_buffer(buf, sig, keyid))
+ ret = -1;
+ free(keyid_to_free);
+ return ret;
+}
int parse_signed_commit(const struct commit *commit,
struct strbuf *payload, struct strbuf *signature,
@@ -1261,7 +1262,7 @@ int remove_signature(struct strbuf *buf)
return sigs[0].start != NULL;
}
-static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
+static void handle_signed_tag(const struct commit *parent, struct commit_extra_header ***tail)
{
struct merge_remote_desc *desc;
struct commit_extra_header *mergetag;
@@ -1358,18 +1359,51 @@ void verify_merge_signature(struct commit *commit, int verbosity,
signature_check_clear(&signature_check);
}
-void append_merge_tag_headers(struct commit_list *parents,
+void append_merge_tag_headers(const struct commit_list *parents,
struct commit_extra_header ***tail)
{
while (parents) {
- struct commit *parent = parents->item;
+ const struct commit *parent = parents->item;
handle_signed_tag(parent, tail);
parents = parents->next;
}
}
+static int convert_commit_extra_headers(const struct commit_extra_header *orig,
+ struct commit_extra_header **result)
+{
+ const struct git_hash_algo *compat = the_repository->compat_hash_algo;
+ const struct git_hash_algo *algo = the_repository->hash_algo;
+ struct commit_extra_header *extra = NULL, **tail = &extra;
+ struct strbuf out = STRBUF_INIT;
+ while (orig) {
+ struct commit_extra_header *new;
+ CALLOC_ARRAY(new, 1);
+ if (!strcmp(orig->key, "mergetag")) {
+ if (convert_object_file(&out, algo, compat,
+ orig->value, orig->len,
+ OBJ_TAG, 1)) {
+ free(new);
+ free_commit_extra_headers(extra);
+ return -1;
+ }
+ new->key = xstrdup("mergetag");
+ new->value = strbuf_detach(&out, &new->len);
+ } else {
+ new->key = xstrdup(orig->key);
+ new->len = orig->len;
+ new->value = xmemdupz(orig->value, orig->len);
+ }
+ *tail = new;
+ tail = &new->next;
+ orig = orig->next;
+ }
+ *result = extra;
+ return 0;
+}
+
static void add_extra_header(struct strbuf *buffer,
- struct commit_extra_header *extra)
+ const struct commit_extra_header *extra)
{
strbuf_addstr(buffer, extra->key);
if (extra->len)
@@ -1483,7 +1517,7 @@ void free_commit_extra_headers(struct commit_extra_header *extra)
}
int commit_tree(const char *msg, size_t msg_len, const struct object_id *tree,
- struct commit_list *parents, struct object_id *ret,
+ const struct commit_list *parents, struct object_id *ret,
const char *author, const char *sign_commit)
{
struct commit_extra_header *extra = NULL, **tail = &extra;
@@ -1611,77 +1645,173 @@ N_("Warning: commit message did not conform to UTF-8.\n"
"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 char *msg, size_t msg_len,
- const struct object_id *tree,
- struct commit_list *parents, struct object_id *ret,
- const char *author, const char *committer,
- const char *sign_commit,
- struct commit_extra_header *extra)
+static void write_commit_tree(struct strbuf *buffer, const char *msg, size_t msg_len,
+ const struct object_id *tree,
+ const struct object_id *parents, size_t parents_len,
+ const char *author, const char *committer,
+ const struct commit_extra_header *extra)
{
- int result;
int encoding_is_utf8;
- struct strbuf buffer;
-
- assert_oid_type(tree, OBJ_TREE);
-
- if (memchr(msg, '\0', msg_len))
- return error("a NUL byte in commit log message not allowed.");
+ size_t i;
/* Not having i18n.commitencoding is the same as having utf-8 */
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", oid_to_hex(tree));
+ strbuf_grow(buffer, 8192); /* should avoid reallocs for the headers */
+ strbuf_addf(buffer, "tree %s\n", oid_to_hex(tree));
/*
* NOTE! This ordering means that the same exact tree merged with a
* different order of parents will be a _different_ changeset even
* if everything else stays the same.
*/
- while (parents) {
- struct commit *parent = pop_commit(&parents);
- strbuf_addf(&buffer, "parent %s\n",
- oid_to_hex(&parent->object.oid));
- }
+ for (i = 0; i < parents_len; i++)
+ strbuf_addf(buffer, "parent %s\n", oid_to_hex(&parents[i]));
/* Person/date information */
if (!author)
author = git_author_info(IDENT_STRICT);
- strbuf_addf(&buffer, "author %s\n", author);
+ strbuf_addf(buffer, "author %s\n", author);
if (!committer)
committer = git_committer_info(IDENT_STRICT);
- strbuf_addf(&buffer, "committer %s\n", committer);
+ strbuf_addf(buffer, "committer %s\n", committer);
if (!encoding_is_utf8)
- strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+ strbuf_addf(buffer, "encoding %s\n", git_commit_encoding);
while (extra) {
- add_extra_header(&buffer, extra);
+ add_extra_header(buffer, extra);
extra = extra->next;
}
- strbuf_addch(&buffer, '\n');
+ strbuf_addch(buffer, '\n');
/* And add the comment */
- strbuf_add(&buffer, msg, msg_len);
+ strbuf_add(buffer, msg, msg_len);
+}
- /* And check the encoding */
- if (encoding_is_utf8 && !verify_utf8(&buffer))
- fprintf(stderr, _(commit_utf8_warn));
+int commit_tree_extended(const char *msg, size_t msg_len,
+ const struct object_id *tree,
+ const struct commit_list *parents, struct object_id *ret,
+ const char *author, const char *committer,
+ const char *sign_commit,
+ const struct commit_extra_header *extra)
+{
+ struct repository *r = the_repository;
+ int result = 0;
+ int encoding_is_utf8;
+ struct strbuf buffer = STRBUF_INIT, compat_buffer = STRBUF_INIT;
+ struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
+ struct object_id *parent_buf = NULL, *compat_oid = NULL;
+ struct object_id compat_oid_buf;
+ size_t i, nparents;
+
+ /* Not having i18n.commitencoding is the same as having utf-8 */
+ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+ assert_oid_type(tree, OBJ_TREE);
- if (sign_commit && sign_with_header(&buffer, sign_commit)) {
+ if (memchr(msg, '\0', msg_len))
+ return error("a NUL byte in commit log message not allowed.");
+
+ nparents = commit_list_count(parents);
+ CALLOC_ARRAY(parent_buf, nparents);
+ i = 0;
+ for (const struct commit_list *p = parents; p; p = p->next)
+ oidcpy(&parent_buf[i++], &p->item->object.oid);
+
+ write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
+ if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) {
result = -1;
goto out;
}
+ if (r->compat_hash_algo) {
+ struct commit_extra_header *compat_extra = NULL;
+ struct object_id mapped_tree;
+ struct object_id *mapped_parents;
+
+ CALLOC_ARRAY(mapped_parents, nparents);
+
+ if (repo_oid_to_algop(r, tree, r->compat_hash_algo, &mapped_tree)) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ for (i = 0; i < nparents; i++)
+ if (repo_oid_to_algop(r, &parent_buf[i], r->compat_hash_algo, &mapped_parents[i])) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ if (convert_commit_extra_headers(extra, &compat_extra)) {
+ result = -1;
+ free(mapped_parents);
+ goto out;
+ }
+ write_commit_tree(&compat_buffer, msg, msg_len, &mapped_tree,
+ mapped_parents, nparents, author, committer, compat_extra);
+ free_commit_extra_headers(compat_extra);
+ free(mapped_parents);
+
+ if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) {
+ result = -1;
+ goto out;
+ }
+ }
- result = write_object_file(buffer.buf, buffer.len, OBJ_COMMIT, ret);
+ if (sign_commit) {
+ struct sig_pairs {
+ struct strbuf *sig;
+ const struct git_hash_algo *algo;
+ } bufs [2] = {
+ { &compat_sig, r->compat_hash_algo },
+ { &sig, r->hash_algo },
+ };
+
+ /*
+ * We write algorithms in the order they were implemented in
+ * Git to produce a stable hash when multiple algorithms are
+ * used.
+ */
+ if (r->compat_hash_algo && hash_algo_by_ptr(bufs[0].algo) > hash_algo_by_ptr(bufs[1].algo))
+ SWAP(bufs[0], bufs[1]);
+
+ /*
+ * We traverse each algorithm in order, and apply the signature
+ * to each buffer.
+ */
+ for (size_t i = 0; i < ARRAY_SIZE(bufs); i++) {
+ if (!bufs[i].algo)
+ continue;
+ add_header_signature(&buffer, bufs[i].sig, bufs[i].algo);
+ if (r->compat_hash_algo)
+ add_header_signature(&compat_buffer, bufs[i].sig, bufs[i].algo);
+ }
+ }
+
+ /* And check the encoding. */
+ if (encoding_is_utf8 && (!verify_utf8(&buffer) || !verify_utf8(&compat_buffer)))
+ fprintf(stderr, _(commit_utf8_warn));
+
+ if (r->compat_hash_algo) {
+ hash_object_file(r->compat_hash_algo, compat_buffer.buf, compat_buffer.len,
+ OBJ_COMMIT, &compat_oid_buf);
+ compat_oid = &compat_oid_buf;
+ }
+
+ result = write_object_file_flags(buffer.buf, buffer.len, OBJ_COMMIT,
+ ret, compat_oid, 0);
out:
+ free(parent_buf);
strbuf_release(&buffer);
+ strbuf_release(&compat_buffer);
+ strbuf_release(&sig);
+ strbuf_release(&compat_sig);
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)
+struct merge_remote_desc *merge_remote_util(const struct commit *commit)
{
return *merge_desc_slab_at(&merge_desc_slab, commit);
}
@@ -1737,20 +1867,12 @@ struct commit_list **commit_list_append(struct commit *commit,
return &new_commit->next;
}
-const char *find_header_mem(const char *msg, size_t len,
- const char *key, size_t *out_len)
+const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
{
int key_len = strlen(key);
const char *line = msg;
- /*
- * NEEDSWORK: It's possible for strchrnul() to scan beyond the range
- * given by len. However, current callers are safe because they compute
- * len by scanning a NUL-terminated block of memory starting at msg.
- * Nonetheless, it would be better to ensure the function does not look
- * at msg beyond the len provided by the caller.
- */
- while (line && line < msg + len) {
+ while (line) {
const char *eol = strchrnul(line, '\n');
if (line == eol)
@@ -1767,10 +1889,6 @@ const char *find_header_mem(const char *msg, size_t len,
return NULL;
}
-const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
-{
- return find_header_mem(msg, strlen(msg), key, out_len);
-}
/*
* Inspect the given string and determine the true "end" of the log message, in
* order to find where to put a new Signed-off-by trailer. Ignored are
@@ -1796,7 +1914,8 @@ size_t ignored_log_message_bytes(const char *buf, size_t len)
else
next_line++;
- if (buf[bol] == comment_line_char || buf[bol] == '\n') {
+ if (starts_with_mem(buf + bol, cutoff - bol, comment_line_str) ||
+ buf[bol] == '\n') {
/* is this the first of the run of comments? */
if (!boc)
boc = bol;
@@ -1838,5 +1957,5 @@ int run_commit_hook(int editor_is_used, const char *index_file,
va_end(args);
opt.invoked_hook = invoked_hook;
- return run_hooks_opt(name, &opt);
+ return run_hooks_opt(the_repository, name, &opt);
}