Skip to content

Commit b07d9bf

Browse files
phillipwoodgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent aeb582a commit b07d9bf

File tree

5 files changed

+94
-4
lines changed

5 files changed

+94
-4
lines changed

branch.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "refs.h"
66
#include "refspec.h"
77
#include "remote.h"
8+
#include "sequencer.h"
89
#include "commit.h"
910
#include "worktree.h"
1011

@@ -339,8 +340,7 @@ void create_branch(struct repository *r,
339340

340341
void remove_branch_state(struct repository *r)
341342
{
342-
unlink(git_path_cherry_pick_head(r));
343-
unlink(git_path_revert_head(r));
343+
sequencer_post_commit_cleanup(r);
344344
unlink(git_path_merge_head(r));
345345
unlink(git_path_merge_rr(r));
346346
unlink(git_path_merge_msg(r));

builtin/commit.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,8 +1657,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
16571657
die("%s", err.buf);
16581658
}
16591659

1660-
unlink(git_path_cherry_pick_head(the_repository));
1661-
unlink(git_path_revert_head(the_repository));
1660+
sequencer_post_commit_cleanup(the_repository);
16621661
unlink(git_path_merge_head(the_repository));
16631662
unlink(git_path_merge_msg(the_repository));
16641663
unlink(git_path_merge_mode(the_repository));

sequencer.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,57 @@ static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path)
22202220
return len;
22212221
}
22222222

2223+
static int have_finished_the_last_pick(void)
2224+
{
2225+
struct strbuf buf = STRBUF_INIT;
2226+
const char *eol;
2227+
const char *todo_path = git_path_todo_file();
2228+
int ret = 0;
2229+
2230+
if (strbuf_read_file(&buf, todo_path, 0) < 0) {
2231+
if (errno == ENOENT) {
2232+
return 0;
2233+
} else {
2234+
error_errno("unable to open '%s'", todo_path);
2235+
return 0;
2236+
}
2237+
}
2238+
/* If there is only one line then we are done */
2239+
eol = strchr(buf.buf, '\n');
2240+
if (!eol || !eol[1])
2241+
ret = 1;
2242+
2243+
strbuf_release(&buf);
2244+
2245+
return ret;
2246+
}
2247+
2248+
void sequencer_post_commit_cleanup(struct repository *r)
2249+
{
2250+
struct replay_opts opts = REPLAY_OPTS_INIT;
2251+
int need_cleanup = 0;
2252+
2253+
if (file_exists(git_path_cherry_pick_head(r))) {
2254+
unlink(git_path_cherry_pick_head(r));
2255+
opts.action = REPLAY_PICK;
2256+
need_cleanup = 1;
2257+
}
2258+
2259+
if (file_exists(git_path_revert_head(r))) {
2260+
unlink(git_path_revert_head(r));
2261+
opts.action = REPLAY_REVERT;
2262+
need_cleanup = 1;
2263+
}
2264+
2265+
if (!need_cleanup)
2266+
return;
2267+
2268+
if (!have_finished_the_last_pick())
2269+
return;
2270+
2271+
sequencer_remove_state(&opts);
2272+
}
2273+
22232274
static int read_populate_todo(struct repository *r,
22242275
struct todo_list *todo_list,
22252276
struct replay_opts *opts)

sequencer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,4 @@ int read_author_script(const char *path, char **name, char **email, char **date,
144144
void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
145145
int write_basic_state(struct replay_opts *opts, const char *head_name,
146146
const char *onto, const char *orig_head);
147+
void sequencer_post_commit_cleanup(struct repository *r);

t/t3507-cherry-pick-conflict.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,25 @@ test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '
156156
157157
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
158158
'
159+
test_expect_success 'successful final commit clears cherry-pick state' '
160+
pristine_detach initial &&
161+
162+
test_must_fail git cherry-pick base picked-signed &&
163+
echo resolved >foo &&
164+
test_path_is_file .git/sequencer/todo &&
165+
git commit -a &&
166+
test_must_fail test_path_exists .git/sequencer
167+
'
168+
169+
test_expect_success 'reset after final pick clears cherry-pick state' '
170+
pristine_detach initial &&
171+
172+
test_must_fail git cherry-pick base picked-signed &&
173+
echo resolved >foo &&
174+
test_path_is_file .git/sequencer/todo &&
175+
git reset &&
176+
test_must_fail test_path_exists .git/sequencer
177+
'
159178

160179
test_expect_success 'failed cherry-pick produces dirty index' '
161180
pristine_detach initial &&
@@ -316,6 +335,26 @@ test_expect_success 'failed commit does not clear REVERT_HEAD' '
316335
test_cmp_rev picked REVERT_HEAD
317336
'
318337

338+
test_expect_success 'successful final commit clears revert state' '
339+
pristine_detach picked-signed &&
340+
341+
test_must_fail git revert picked-signed base &&
342+
echo resolved >foo &&
343+
test_path_is_file .git/sequencer/todo &&
344+
git commit -a &&
345+
test_must_fail test_path_exists .git/sequencer
346+
'
347+
348+
test_expect_success 'reset after final pick clears revert state' '
349+
pristine_detach picked-signed &&
350+
351+
test_must_fail git revert picked-signed base &&
352+
echo resolved >foo &&
353+
test_path_is_file .git/sequencer/todo &&
354+
git reset &&
355+
test_must_fail test_path_exists .git/sequencer
356+
'
357+
319358
test_expect_success 'revert conflict, diff3 -m style' '
320359
pristine_detach initial &&
321360
git config merge.conflictstyle diff3 &&

0 commit comments

Comments
 (0)