diff options
Diffstat (limited to 'sequencer.c')
-rw-r--r-- | sequencer.c | 94 |
1 files changed, 83 insertions, 11 deletions
diff --git a/sequencer.c b/sequencer.c index 71929ba8cd..4e3af82442 100644 --- a/sequencer.c +++ b/sequencer.c @@ -13,6 +13,7 @@ #include "rerere.h" #include "merge-recursive.h" #include "refs.h" +#include "argv-array.h" #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -251,6 +252,30 @@ static int do_recursive_merge(struct commit *base, struct commit *next, return !clean; } +static int is_index_unchanged(void) +{ + unsigned char head_sha1[20]; + struct commit *head_commit; + + if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL)) + return error(_("Could not resolve HEAD commit\n")); + + head_commit = lookup_commit(head_sha1); + if (!head_commit || parse_commit(head_commit)) + return error(_("could not parse commit %s\n"), + sha1_to_hex(head_commit->object.sha1)); + + if (!active_cache_tree) + active_cache_tree = cache_tree(); + + if (!cache_tree_fully_valid(active_cache_tree)) + if (cache_tree_update(active_cache_tree, active_cache, + active_nr, 0)) + return error(_("Unable to update cache tree\n")); + + return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1); +} + /* * If we are cherry-pick, and if the merge did not result in * hand-editing, we will hit this commit and inherit the original @@ -260,24 +285,46 @@ static int do_recursive_merge(struct commit *base, struct commit *next, */ static int run_git_commit(const char *defmsg, struct replay_opts *opts) { - /* 7 is max possible length of our args array including NULL */ - const char *args[7]; - int i = 0; + struct argv_array array; + int rc; + + argv_array_init(&array); + argv_array_push(&array, "commit"); + argv_array_push(&array, "-n"); - args[i++] = "commit"; - args[i++] = "-n"; if (opts->signoff) - args[i++] = "-s"; + argv_array_push(&array, "-s"); if (!opts->edit) { - args[i++] = "-F"; - args[i++] = defmsg; + argv_array_push(&array, "-F"); + argv_array_push(&array, defmsg); } + if (opts->allow_empty) - args[i++] = "--allow-empty"; + argv_array_push(&array, "--allow-empty"); - args[i] = NULL; + rc = run_command_v_opt(array.argv, RUN_GIT_CMD); + argv_array_clear(&array); + return rc; +} + +static int is_original_commit_empty(struct commit *commit) +{ + const unsigned char *ptree_sha1; + + if (parse_commit(commit)) + return error(_("Could not parse commit %s\n"), + sha1_to_hex(commit->object.sha1)); + if (commit->parents) { + struct commit *parent = commit->parents->item; + if (parse_commit(parent)) + return error(_("Could not parse parent commit %s\n"), + sha1_to_hex(parent->object.sha1)); + ptree_sha1 = parent->tree->object.sha1; + } else { + ptree_sha1 = EMPTY_TREE_SHA1_BIN; /* commit is root */ + } - return run_command_v_opt(args, RUN_GIT_CMD); + return !hashcmp(ptree_sha1, commit->tree->object.sha1); } static int do_pick_commit(struct commit *commit, struct replay_opts *opts) @@ -289,6 +336,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) char *defmsg = NULL; struct strbuf msgbuf = STRBUF_INIT; int res; + int empty_commit; + int index_unchanged; if (opts->no_commit) { /* @@ -414,6 +463,10 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) free_commit_list(remotes); } + empty_commit = is_original_commit_empty(commit); + if (empty_commit < 0) + return empty_commit; + /* * If the merge was clean or if it failed due to conflict, we write * CHERRY_PICK_HEAD for the subsequent invocation of commit to use. @@ -434,6 +487,25 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts) print_advice(res == 1, opts); rerere(opts->allow_rerere_auto); } else { + index_unchanged = is_index_unchanged(); + /* + * If index_unchanged is less than 0, that indicates we either + * couldn't parse HEAD or the index, so error out here. + */ + if (index_unchanged < 0) + return index_unchanged; + + if (!empty_commit && !opts->keep_redundant_commits && index_unchanged) + /* + * The head tree and the index match + * meaning the commit is empty. Since it wasn't created + * empty (based on the previous test), we can conclude + * the commit has been made redundant. Since we don't + * want to keep redundant commits, we can just return + * here, skipping this commit + */ + return 0; + if (!opts->no_commit) res = run_git_commit(defmsg, opts); } |