Skip to content

Commit 1c91fdc

Browse files
committed
Merge branch 'pw/clean-sequencer-state-upon-final-commit' into pu
"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 e69aa3c + 4a72486 commit 1c91fdc

File tree

7 files changed

+205
-11
lines changed

7 files changed

+205
-11
lines changed

branch.c

Lines changed: 2 additions & 4 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,10 +340,7 @@ void create_branch(struct repository *r,
339340

340341
void remove_branch_state(struct repository *r, int verbose)
341342
{
342-
if (!unlink(git_path_cherry_pick_head(r)) && verbose)
343-
warning(_("cancelling a cherry picking in progress"));
344-
if (!unlink(git_path_revert_head(r)) && verbose)
345-
warning(_("cancelling a revert in progress"));
343+
sequencer_post_commit_cleanup(r, verbose);
346344
if (!unlink(git_path_merge_head(r)) && verbose)
347345
warning(_("cancelling a merge in progress"));
348346
unlink(git_path_merge_rr(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, verbose);
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: 90 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,61 @@ 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, int verbose)
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+
if (verbose)
2322+
warning(_("cancelling a cherry picking in progress"));
2323+
opts.action = REPLAY_PICK;
2324+
need_cleanup = 1;
2325+
}
2326+
2327+
if (file_exists(git_path_revert_head(r))) {
2328+
unlink(git_path_revert_head(r));
2329+
if (verbose)
2330+
warning(_("cancelling a revert in progress"));
2331+
opts.action = REPLAY_REVERT;
2332+
need_cleanup = 1;
2333+
}
2334+
2335+
if (!need_cleanup)
2336+
return;
2337+
2338+
if (!have_finished_the_last_pick())
2339+
return;
2340+
2341+
sequencer_remove_state(&opts);
2342+
}
2343+
22542344
static int read_populate_todo(struct repository *r,
22552345
struct todo_list *todo_list,
22562346
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, int verbose);
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";
@@ -1398,12 +1399,22 @@ static void show_rebase_in_progress(struct wt_status *s,
13981399
static void show_cherry_pick_in_progress(struct wt_status *s,
13991400
const char *color)
14001401
{
1401-
status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
1402-
find_unique_abbrev(&s->state.cherry_pick_head_oid, DEFAULT_ABBREV));
1402+
if (is_null_oid(&s->state.cherry_pick_head_oid))
1403+
status_printf_ln(s, color,
1404+
_("Cherry-pick currently in progress."));
1405+
else
1406+
status_printf_ln(s, color,
1407+
_("You are currently cherry-picking commit %s."),
1408+
find_unique_abbrev(&s->state.cherry_pick_head_oid,
1409+
DEFAULT_ABBREV));
1410+
14031411
if (s->hints) {
14041412
if (has_unmerged(s))
14051413
status_printf_ln(s, color,
14061414
_(" (fix conflicts and run \"git cherry-pick --continue\")"));
1415+
else if (is_null_oid(&s->state.cherry_pick_head_oid))
1416+
status_printf_ln(s, color,
1417+
_(" (run \"git cherry-pick --continue\" to continue)"));
14071418
else
14081419
status_printf_ln(s, color,
14091420
_(" (all conflicts fixed: run \"git cherry-pick --continue\")"));
@@ -1416,12 +1427,21 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
14161427
static void show_revert_in_progress(struct wt_status *s,
14171428
const char *color)
14181429
{
1419-
status_printf_ln(s, color, _("You are currently reverting commit %s."),
1420-
find_unique_abbrev(&s->state.revert_head_oid, DEFAULT_ABBREV));
1430+
if (is_null_oid(&s->state.revert_head_oid))
1431+
status_printf_ln(s, color,
1432+
_("Revert currently in progress."));
1433+
else
1434+
status_printf_ln(s, color,
1435+
_("You are currently reverting commit %s."),
1436+
find_unique_abbrev(&s->state.revert_head_oid,
1437+
DEFAULT_ABBREV));
14211438
if (s->hints) {
14221439
if (has_unmerged(s))
14231440
status_printf_ln(s, color,
14241441
_(" (fix conflicts and run \"git revert --continue\")"));
1442+
else if (is_null_oid(&s->state.revert_head_oid))
1443+
status_printf_ln(s, color,
1444+
_(" (run \"git revert --continue\" to continue)"));
14251445
else
14261446
status_printf_ln(s, color,
14271447
_(" (all conflicts fixed: run \"git revert --continue\")"));
@@ -1592,6 +1612,7 @@ void wt_status_get_state(struct repository *r,
15921612
{
15931613
struct stat st;
15941614
struct object_id oid;
1615+
enum replay_action action;
15951616

15961617
if (!stat(git_path_merge_head(r), &st)) {
15971618
wt_status_check_rebase(NULL, state);
@@ -1609,7 +1630,15 @@ void wt_status_get_state(struct repository *r,
16091630
state->revert_in_progress = 1;
16101631
oidcpy(&state->revert_head_oid, &oid);
16111632
}
1612-
1633+
if (!sequencer_get_last_command(r, &action)) {
1634+
if (action == REPLAY_PICK) {
1635+
state->cherry_pick_in_progress = 1;
1636+
oidcpy(&state->cherry_pick_head_oid, &null_oid);
1637+
} else {
1638+
state->revert_in_progress = 1;
1639+
oidcpy(&state->revert_head_oid, &null_oid);
1640+
}
1641+
}
16131642
if (get_detached_from)
16141643
wt_status_get_detached_from(r, state);
16151644
}

0 commit comments

Comments
 (0)