From 3120370db888889f32e07a082edb4722db8feef1 Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Wed, 16 Oct 2019 02:36:18 +0200 Subject: [PATCH 3/3] am: add --exact This uses exact metadata when creating the commit object, hopefully reconstructing the commit with the exact same SHA1. Signed-off-by: Vegard Nossum --- commit 3120370db888889f32e07a082edb4722db8feef1 tree 61b7556f06fd6fcb0f4a43940ec0cbc29ccf1bcc parent 51bb531eb57320caf3761680ebf77c25b89b3719 author Vegard Nossum 1571186178 +0200 committer Vegard Nossum 1571219301 +0200 --- builtin/am.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 94 insertions(+), 9 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 4190383bba..069a625895 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -118,6 +118,7 @@ struct am_state { int allow_rerere_autoupdate; const char *sign_commit; int rebasing; + int exact; }; /** @@ -399,6 +400,8 @@ static void am_load(struct am_state *state) state->rebasing = !!file_exists(am_path(state, "rebasing")); + state->exact = read_state_file(&sb, state, "exact", 1); + strbuf_release(&sb); } @@ -1005,6 +1008,8 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, else write_state_text(state, "applying", ""); + write_state_bool(state, "exact", state->exact); + if (!get_oid("HEAD", &curr_head)) { write_state_text(state, "abort-safety", oid_to_hex(&curr_head)); if (!state->rebasing) @@ -1548,19 +1553,88 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa */ static void do_commit(const struct am_state *state) { + struct object_id meta_commit = {}; + struct object_id meta_tree = {}; + struct object_id tree, parent, commit; const struct object_id *old_oid; struct commit_list *parents = NULL; const char *reflog_msg, *author; struct strbuf sb = STRBUF_INIT; + if (state->exact) { + /* + * Scan meta file for parents + other data + */ + + struct strbuf line = STRBUF_INIT; + FILE *fp = xfopen(am_path(state, "meta"), "r"); + + while (!strbuf_getline_lf(&line, fp)) { + const char *rest; + + if (skip_prefix(line.buf, "commit ", &rest)) { + if (get_oid_hex(rest, &meta_commit)) + die("invalid exact metadata (commit)"); + } else if (skip_prefix(line.buf, "tree ", &rest)) { + if (get_oid_hex(rest, &meta_tree)) + die("invalid exact metadata (tree)"); + } else if (skip_prefix(line.buf, "parent ", &rest)) { + if (get_oid_hex(rest, &parent)) + die("invalid exact metadata (parent)"); + + commit_list_insert(lookup_commit(the_repository, &parent), &parents); + } else if (skip_prefix(line.buf, "author ", &rest)) { + author = strdup(rest); + } else if (skip_prefix(line.buf, "committer ", &rest)) { + char *name_copy; + char *email; + char *email_copy; + char *date; + + email = strstr(rest, " <"); + if (!email) + die("invalid exact metadata (committer name)"); + + name_copy = xstrndup(rest, email - rest); + email += 2; + setenv("GIT_COMMITTER_NAME", name_copy, 1); + free(name_copy); + + date = strstr(email, "> "); + if (!date) + die("invalid exact metadata (committer email)"); + + email_copy = xstrndup(email, date - email); + date += 2; + setenv("GIT_COMMITTER_EMAIL", email_copy, 1); + free(email_copy); + + setenv("GIT_COMMITTER_DATE", date, 1); + } else if (line.len == 0) { + break; + } else { + die("unknown exact metadata: %.*s", line.len, line.buf); + } + } + + fclose(fp); + } + if (run_hook_le(NULL, "pre-applypatch", NULL)) exit(1); if (write_cache_as_tree(&tree, 0, NULL)) die(_("git write-tree failed to write a tree")); - if (!get_oid_commit("HEAD", &parent)) { + if (state->exact && !oideq(&tree, &meta_tree)) + die("tree mismatch"); + + if (state->exact) { + /* + * Already got parents above. + */ + } else if (!get_oid_commit("HEAD", &parent)) { old_oid = &parent; commit_list_insert(lookup_commit(the_repository, &parent), &parents); @@ -1569,19 +1643,28 @@ static void do_commit(const struct am_state *state) say(state, stderr, _("applying to an empty history")); } - author = fmt_ident(state->author_name, state->author_email, - WANT_AUTHOR_IDENT, - state->ignore_date ? NULL : state->author_date, - IDENT_STRICT); - - if (state->committer_date_is_author_date) - setenv("GIT_COMMITTER_DATE", - state->ignore_date ? "" : state->author_date, 1); + if (state->exact) { + /* + * Already got author above. + */ + } else { + author = fmt_ident(state->author_name, state->author_email, + WANT_AUTHOR_IDENT, + state->ignore_date ? NULL : state->author_date, + IDENT_STRICT); + + if (state->committer_date_is_author_date) + setenv("GIT_COMMITTER_DATE", + state->ignore_date ? "" : state->author_date, 1); + } if (commit_tree(state->msg, state->msg_len, &tree, parents, &commit, author, state->sign_commit)) die(_("failed to write commit object")); + if (state->exact && !oideq(&commit, &meta_commit)) + die("sha1 mismatch"); + reflog_msg = getenv("GIT_REFLOG_ACTION"); if (!reflog_msg) reflog_msg = "am"; @@ -2182,6 +2265,8 @@ int cmd_am(int argc, const char **argv, const char *prefix) 0, PARSE_OPT_NONEG), OPT_BOOL('c', "scissors", &state.scissors, N_("strip everything before a scissors line")), + OPT_BOOL('e', "exact", &state.exact, + N_("preserve exact metadata, including sha1")), OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"), N_("pass it through git-apply"), 0), -- 2.23.0.718.g3120370db8