diff options
author | Phillip Wood <phillip.wood@dunelm.org.uk> | 2019-04-16 11:18:41 +0100 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2019-04-17 11:31:37 +0900 |
commit | b07d9bfd171d881a02d110b68673c58cb63e9f62 (patch) | |
tree | 50cb587f5e0e614a0892c2d46998c05d21a44f86 | |
parent | mingw: allow building with an MSYS2 runtime v3.x (diff) | |
download | tgif-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.c | 4 | ||||
-rw-r--r-- | builtin/commit.c | 3 | ||||
-rw-r--r-- | sequencer.c | 51 | ||||
-rw-r--r-- | sequencer.h | 1 | ||||
-rwxr-xr-x | t/t3507-cherry-pick-conflict.sh | 39 |
5 files changed, 94 insertions, 4 deletions
@@ -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 && |