summaryrefslogtreecommitdiff
path: root/sequencer.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2012-04-30 14:58:00 -0700
committerJunio C Hamano <gitster@pobox.com>2012-04-30 14:58:01 -0700
commit1692579dd3569ebc01187872db91ee2ad93cc962 (patch)
treea0f3f06c4a4d4fd9ee038de63ce69d165fc7ac22 /sequencer.c
parent563b3527b41a978eeef77d9426be460013c35d88 (diff)
parent90e1818f9a06015159712e204dd90868e0a6c421 (diff)
downloadgit-1692579dd3569ebc01187872db91ee2ad93cc962.tar.gz
Merge branch 'nh/empty-rebase'
"git rebase" learned to optionally keep commits that do not introduce any change in the original history. By Neil Horman * nh/empty-rebase: git-rebase: add keep_empty flag git-cherry-pick: Add test to validate new options git-cherry-pick: Add keep-redundant-commits option git-cherry-pick: add allow-empty option
Diffstat (limited to 'sequencer.c')
-rw-r--r--sequencer.c95
1 files changed, 85 insertions, 10 deletions
diff --git a/sequencer.c b/sequencer.c
index 81d8ace35f..f83cdfd637 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,21 +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)
{
- /* 6 is max possible length of our args array including NULL */
- const char *args[6];
- 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)
+ argv_array_push(&array, "--allow-empty");
+
+ 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 */
}
- args[i] = NULL;
- 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)
@@ -286,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) {
/*
@@ -411,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.
@@ -431,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);
}