Skip to content

Commit cf6cce8

Browse files
committed
Merge branch 'pw/clean-sequencer-state-upon-final-commit' into next
"git chery-pick" (and "revert" that shares the same runtime engine) that deals with multiple commits got confused when the final step gets stopped with a conflict and the user concluded the sequence with "git commit". Attempt to fix it by cleaning up the state files used by these commands in such a situation. * pw/clean-sequencer-state-upon-final-commit: fix cherry-pick/revert status after commit commit/reset: try to clean up sequencer state
2 parents 1206aa6 + 4a72486 commit cf6cce8

File tree

7 files changed

+201
-9
lines changed

7 files changed

+201
-9
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
@@ -1658,8 +1658,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
16581658
die("%s", err.buf);
16591659
}
16601660

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

sequencer.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,6 +2168,41 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
21682168
return !item->commit;
21692169
}
21702170

2171+
int sequencer_get_last_command(struct repository *r, enum replay_action *action)
2172+
{
2173+
struct todo_item item;
2174+
char *eol;
2175+
const char *todo_file;
2176+
struct strbuf buf = STRBUF_INIT;
2177+
int ret = -1;
2178+
2179+
todo_file = git_path_todo_file();
2180+
if (strbuf_read_file(&buf, todo_file, 0) < 0) {
2181+
if (errno == ENOENT)
2182+
return -1;
2183+
else
2184+
return error_errno("unable to open '%s'", todo_file);
2185+
}
2186+
eol = strchrnul(buf.buf, '\n');
2187+
if (buf.buf != eol && eol[-1] == '\r')
2188+
eol--; /* strip Carriage Return */
2189+
if (parse_insn_line(r, &item, buf.buf, buf.buf, eol))
2190+
goto fail;
2191+
if (item.command == TODO_PICK)
2192+
*action = REPLAY_PICK;
2193+
else if (item.command == TODO_REVERT)
2194+
*action = REPLAY_REVERT;
2195+
else
2196+
goto fail;
2197+
2198+
ret = 0;
2199+
2200+
fail:
2201+
strbuf_release(&buf);
2202+
2203+
return ret;
2204+
}
2205+
21712206
int todo_list_parse_insn_buffer(struct repository *r, char *buf,
21722207
struct todo_list *todo_list)
21732208
{
@@ -2251,6 +2286,57 @@ static ssize_t strbuf_read_file_or_whine(struct strbuf *sb, const char *path)
22512286
return len;
22522287
}
22532288

2289+
static int have_finished_the_last_pick(void)
2290+
{
2291+
struct strbuf buf = STRBUF_INIT;
2292+
const char *eol;
2293+
const char *todo_path = git_path_todo_file();
2294+
int ret = 0;
2295+
2296+
if (strbuf_read_file(&buf, todo_path, 0) < 0) {
2297+
if (errno == ENOENT) {
2298+
return 0;
2299+
} else {
2300+
error_errno("unable to open '%s'", todo_path);
2301+
return 0;
2302+
}
2303+
}
2304+
/* If there is only one line then we are done */
2305+
eol = strchr(buf.buf, '\n');
2306+
if (!eol || !eol[1])
2307+
ret = 1;
2308+
2309+
strbuf_release(&buf);
2310+
2311+
return ret;
2312+
}
2313+
2314+
void sequencer_post_commit_cleanup(struct repository *r)
2315+
{
2316+
struct replay_opts opts = REPLAY_OPTS_INIT;
2317+
int need_cleanup = 0;
2318+
2319+
if (file_exists(git_path_cherry_pick_head(r))) {
2320+
unlink(git_path_cherry_pick_head(r));
2321+
opts.action = REPLAY_PICK;
2322+
need_cleanup = 1;
2323+
}
2324+
2325+
if (file_exists(git_path_revert_head(r))) {
2326+
unlink(git_path_revert_head(r));
2327+
opts.action = REPLAY_REVERT;
2328+
need_cleanup = 1;
2329+
}
2330+
2331+
if (!need_cleanup)
2332+
return;
2333+
2334+
if (!have_finished_the_last_pick())
2335+
return;
2336+
2337+
sequencer_remove_state(&opts);
2338+
}
2339+
22542340
static int read_populate_todo(struct repository *r,
22552341
struct todo_list *todo_list,
22562342
struct replay_opts *opts)

sequencer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,6 @@ int read_author_script(const char *path, char **name, char **email, char **date,
200200
void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
201201
int write_basic_state(struct replay_opts *opts, const char *head_name,
202202
struct commit *onto, const char *orig_head);
203+
void sequencer_post_commit_cleanup(struct repository *r);
204+
int sequencer_get_last_command(struct repository* r,
205+
enum replay_action *action);

t/t3507-cherry-pick-conflict.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ test_expect_success 'successful commit clears CHERRY_PICK_HEAD' '
161161
162162
test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
163163
'
164+
test_expect_success 'successful final commit clears cherry-pick state' '
165+
pristine_detach initial &&
166+
167+
test_must_fail git cherry-pick base picked-signed &&
168+
echo resolved >foo &&
169+
test_path_is_file .git/sequencer/todo &&
170+
git commit -a &&
171+
test_must_fail test_path_exists .git/sequencer
172+
'
173+
174+
test_expect_success 'reset after final pick clears cherry-pick state' '
175+
pristine_detach initial &&
176+
177+
test_must_fail git cherry-pick base picked-signed &&
178+
echo resolved >foo &&
179+
test_path_is_file .git/sequencer/todo &&
180+
git reset &&
181+
test_must_fail test_path_exists .git/sequencer
182+
'
164183

165184
test_expect_success 'failed cherry-pick produces dirty index' '
166185
pristine_detach initial &&
@@ -361,6 +380,26 @@ test_expect_success 'failed commit does not clear REVERT_HEAD' '
361380
test_cmp_rev picked REVERT_HEAD
362381
'
363382

383+
test_expect_success 'successful final commit clears revert state' '
384+
pristine_detach picked-signed &&
385+
386+
test_must_fail git revert picked-signed base &&
387+
echo resolved >foo &&
388+
test_path_is_file .git/sequencer/todo &&
389+
git commit -a &&
390+
test_must_fail test_path_exists .git/sequencer
391+
'
392+
393+
test_expect_success 'reset after final pick clears revert state' '
394+
pristine_detach picked-signed &&
395+
396+
test_must_fail git revert picked-signed base &&
397+
echo resolved >foo &&
398+
test_path_is_file .git/sequencer/todo &&
399+
git reset &&
400+
test_must_fail test_path_exists .git/sequencer
401+
'
402+
364403
test_expect_success 'revert conflict, diff3 -m style' '
365404
pristine_detach initial &&
366405
git config merge.conflictstyle diff3 &&

t/t7512-status-help.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,24 @@ EOF
780780
test_i18ncmp expected actual
781781
'
782782

783+
test_expect_success 'status when cherry-picking after committing conflict resolution' '
784+
git reset --hard cherry_branch &&
785+
test_when_finished "git cherry-pick --abort" &&
786+
test_must_fail git cherry-pick cherry_branch_second one_cherry &&
787+
echo end >main.txt &&
788+
git commit -a &&
789+
cat >expected <<EOF &&
790+
On branch cherry_branch
791+
Cherry-pick currently in progress.
792+
(run "git cherry-pick --continue" to continue)
793+
(use "git cherry-pick --abort" to cancel the cherry-pick operation)
794+
795+
nothing to commit (use -u to show untracked files)
796+
EOF
797+
git status --untracked-files=no >actual &&
798+
test_i18ncmp expected actual
799+
'
800+
783801
test_expect_success 'status showing detached at and from a tag' '
784802
test_commit atag tagging &&
785803
git checkout atag &&
@@ -857,6 +875,24 @@ EOF
857875
test_i18ncmp expected actual
858876
'
859877

878+
test_expect_success 'status while reverting after committing conflict resolution' '
879+
test_when_finished "git revert --abort" &&
880+
git reset --hard new &&
881+
test_must_fail git revert old new &&
882+
echo reverted >to-revert.txt &&
883+
git commit -a &&
884+
cat >expected <<EOF &&
885+
On branch master
886+
Revert currently in progress.
887+
(run "git revert --continue" to continue)
888+
(use "git revert --abort" to cancel the revert operation)
889+
890+
nothing to commit (use -u to show untracked files)
891+
EOF
892+
git status --untracked-files=no >actual &&
893+
test_i18ncmp expected actual
894+
'
895+
860896
test_expect_success 'prepare for different number of commits rebased' '
861897
git reset --hard master &&
862898
git checkout -b several_commits &&

wt-status.c

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "utf8.h"
1818
#include "worktree.h"
1919
#include "lockfile.h"
20+
#include "sequencer.h"
2021

2122
static const char cut_line[] =
2223
"------------------------ >8 ------------------------\n";
@@ -1386,12 +1387,22 @@ static void show_rebase_in_progress(struct wt_status *s,
13861387
static void show_cherry_pick_in_progress(struct wt_status *s,
13871388
const char *color)
13881389
{
1389-
status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
1390-
find_unique_abbrev(&s->state.cherry_pick_head_oid, DEFAULT_ABBREV));
1390+
if (is_null_oid(&s->state.cherry_pick_head_oid))
1391+
status_printf_ln(s, color,
1392+
_("Cherry-pick currently in progress."));
1393+
else
1394+
status_printf_ln(s, color,
1395+
_("You are currently cherry-picking commit %s."),
1396+
find_unique_abbrev(&s->state.cherry_pick_head_oid,
1397+
DEFAULT_ABBREV));
1398+
13911399
if (s->hints) {
13921400
if (has_unmerged(s))
13931401
status_printf_ln(s, color,
13941402
_(" (fix conflicts and run \"git cherry-pick --continue\")"));
1403+
else if (is_null_oid(&s->state.cherry_pick_head_oid))
1404+
status_printf_ln(s, color,
1405+
_(" (run \"git cherry-pick --continue\" to continue)"));
13951406
else
13961407
status_printf_ln(s, color,
13971408
_(" (all conflicts fixed: run \"git cherry-pick --continue\")"));
@@ -1404,12 +1415,21 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
14041415
static void show_revert_in_progress(struct wt_status *s,
14051416
const char *color)
14061417
{
1407-
status_printf_ln(s, color, _("You are currently reverting commit %s."),
1408-
find_unique_abbrev(&s->state.revert_head_oid, DEFAULT_ABBREV));
1418+
if (is_null_oid(&s->state.revert_head_oid))
1419+
status_printf_ln(s, color,
1420+
_("Revert currently in progress."));
1421+
else
1422+
status_printf_ln(s, color,
1423+
_("You are currently reverting commit %s."),
1424+
find_unique_abbrev(&s->state.revert_head_oid,
1425+
DEFAULT_ABBREV));
14091426
if (s->hints) {
14101427
if (has_unmerged(s))
14111428
status_printf_ln(s, color,
14121429
_(" (fix conflicts and run \"git revert --continue\")"));
1430+
else if (is_null_oid(&s->state.revert_head_oid))
1431+
status_printf_ln(s, color,
1432+
_(" (run \"git revert --continue\" to continue)"));
14131433
else
14141434
status_printf_ln(s, color,
14151435
_(" (all conflicts fixed: run \"git revert --continue\")"));
@@ -1580,6 +1600,7 @@ void wt_status_get_state(struct repository *r,
15801600
{
15811601
struct stat st;
15821602
struct object_id oid;
1603+
enum replay_action action;
15831604

15841605
if (!stat(git_path_merge_head(r), &st)) {
15851606
wt_status_check_rebase(NULL, state);
@@ -1597,7 +1618,15 @@ void wt_status_get_state(struct repository *r,
15971618
state->revert_in_progress = 1;
15981619
oidcpy(&state->revert_head_oid, &oid);
15991620
}
1600-
1621+
if (!sequencer_get_last_command(r, &action)) {
1622+
if (action == REPLAY_PICK) {
1623+
state->cherry_pick_in_progress = 1;
1624+
oidcpy(&state->cherry_pick_head_oid, &null_oid);
1625+
} else {
1626+
state->revert_in_progress = 1;
1627+
oidcpy(&state->revert_head_oid, &null_oid);
1628+
}
1629+
}
16011630
if (get_detached_from)
16021631
wt_status_get_detached_from(r, state);
16031632
}

0 commit comments

Comments
 (0)