summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Phillip Wood <phillip.wood@dunelm.org.uk>2019-04-16 11:18:41 +0100
committerLibravatar Junio C Hamano <gitster@pobox.com>2019-04-17 11:31:37 +0900
commitb07d9bfd171d881a02d110b68673c58cb63e9f62 (patch)
tree50cb587f5e0e614a0892c2d46998c05d21a44f86
parentmingw: allow building with an MSYS2 runtime v3.x (diff)
downloadtgif-b07d9bfd171d881a02d110b68673c58cb63e9f62.tar.xz
commit/reset: try to clean up sequencer state
When cherry-picking or reverting a sequence of commits and if the final pick/revert has conflicts and the user uses `git commit` to commit the conflict resolution and does not run `git cherry-pick --continue` then the sequencer state is left behind. This can cause problems later. In my case I cherry-picked a sequence of commits the last one of which I committed with `git commit` after resolving some conflicts, then a while later, on a different branch I aborted a revert which rewound my HEAD to the end of the cherry-pick sequence on the previous branch. Avoid this potential problem by removing the sequencer state if we're committing or resetting the final pick in a sequence. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--branch.c4
-rw-r--r--builtin/commit.c3
-rw-r--r--sequencer.c51
-rw-r--r--sequencer.h1
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh39
5 files changed, 94 insertions, 4 deletions
diff --git a/branch.c b/branch.c
index 28b81a7e02..643694542a 100644
--- a/branch.c
+++ b/branch.c
@@ -5,6 +5,7 @@
#include "refs.h"
#include "refspec.h"
#include "remote.h"
+#include "sequencer.h"
#include "commit.h"
#include "worktree.h"
@@ -339,8 +340,7 @@ void create_branch(struct repository *r,
void remove_branch_state(struct repository *r)
{
- unlink(git_path_cherry_pick_head(r));
- unlink(git_path_revert_head(r));
+ sequencer_post_commit_cleanup(r);
unlink(git_path_merge_head(r));
unlink(git_path_merge_rr(r));
unlink(git_path_merge_msg(r));
diff --git a/builtin/commit.c b/builtin/commit.c
index 2986553d5f..9df3414d80 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1657,8 +1657,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
die("%s", err.buf);
}
- unlink(git_path_cherry_pick_head(the_repository));
- unlink(git_path_revert_head(the_repository));
+ sequencer_post_commit_cleanup(the_repository);
unlink(git_path_merge_head(the_repository));
unlink(git_path_merge_msg(the_repository));
unlink(git_path_merge_mode(the_repository));
diff --git a/sequencer.c b/sequencer.c
index 0db410d590..7c7b8a07c4 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2220,6 +2220,57 @@ static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path)
return len;
}
+static int have_finished_the_last_pick(void)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *eol;
+ const char *todo_path = git_path_todo_file();
+ int ret = 0;
+
+ if (strbuf_read_file(&buf, todo_path, 0) < 0) {
+ if (errno == ENOENT) {
+ return 0;
+ } else {
+ error_errno("unable to open '%s'", todo_path);
+ return 0;
+ }
+ }
+ /* If there is only one line then we are done */
+ eol = strchr(buf.buf, '\n');
+ if (!eol || !eol[1])
+ ret = 1;
+
+ strbuf_release(&buf);
+
+ return ret;
+}
+
+void sequencer_post_commit_cleanup(struct repository *r)
+{
+ struct replay_opts opts = REPLAY_OPTS_INIT;
+ int need_cleanup = 0;
+
+ if (file_exists(git_path_cherry_pick_head(r))) {
+ unlink(git_path_cherry_pick_head(r));
+ opts.action = REPLAY_PICK;
+ need_cleanup = 1;
+ }
+
+ if (file_exists(git_path_revert_head(r))) {
+ unlink(git_path_revert_head(r));
+ opts.action = REPLAY_REVERT;
+ need_cleanup = 1;
+ }
+
+ if (!need_cleanup)
+ return;
+
+ if (!have_finished_the_last_pick())
+ return;
+
+ sequencer_remove_state(&opts);
+}
+
static int read_populate_todo(struct repository *r,
struct todo_list *todo_list,
struct replay_opts *opts)
diff --git a/sequencer.h b/sequencer.h
index 4d505b3590..6c7cf8d72f 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -144,3 +144,4 @@ int read_author_script(const char *path, char **name, char **email, char **date,
void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
int write_basic_state(struct replay_opts *opts, const char *head_name,
const char *onto, const char *orig_head);
+void sequencer_post_commit_cleanup(struct repository *r);
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
index 0db166152a..cebf91dce2 100755
--- a/t/t3507-cherry-pick-conflict.sh
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -156,6 +156,25 @@ test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
'
+test_expect_success 'successful final commit clears cherry-pick state' '
+ pristine_detach initial &&
+
+ test_must_fail git cherry-pick base picked-signed &&
+ echo resolved >foo &&
+ test_path_is_file .git/sequencer/todo &&
+ git commit -a &&
+ test_must_fail test_path_exists .git/sequencer
+'
+
+test_expect_success 'reset after final pick clears cherry-pick state' '
+ pristine_detach initial &&
+
+ test_must_fail git cherry-pick base picked-signed &&
+ echo resolved >foo &&
+ test_path_is_file .git/sequencer/todo &&
+ git reset &&
+ test_must_fail test_path_exists .git/sequencer
+'
test_expect_success 'failed cherry-pick produces dirty index' '
pristine_detach initial &&
@@ -316,6 +335,26 @@ test_expect_success 'failed commit does not clear REVERT_HEAD' '
test_cmp_rev picked REVERT_HEAD
'
+test_expect_success 'successful final commit clears revert state' '
+ pristine_detach picked-signed &&
+
+ test_must_fail git revert picked-signed base &&
+ echo resolved >foo &&
+ test_path_is_file .git/sequencer/todo &&
+ git commit -a &&
+ test_must_fail test_path_exists .git/sequencer
+'
+
+test_expect_success 'reset after final pick clears revert state' '
+ pristine_detach picked-signed &&
+
+ test_must_fail git revert picked-signed base &&
+ echo resolved >foo &&
+ test_path_is_file .git/sequencer/todo &&
+ git reset &&
+ test_must_fail test_path_exists .git/sequencer
+'
+
test_expect_success 'revert conflict, diff3 -m style' '
pristine_detach initial &&
git config merge.conflictstyle diff3 &&