Skip to content

Commit 4fcc091

Browse files
committed
Merge branch 'sb/sequencer-abort-safety'
Unlike "git am --abort", "git cherry-pick --abort" moved HEAD back to where cherry-pick started while picking multiple changes, when the cherry-pick stopped to ask for help from the user, and the user did "git reset --hard" to a different commit in order to re-attempt the operation. * sb/sequencer-abort-safety: Revert "sequencer: remove useless get_dir() function" sequencer: remove useless get_dir() function sequencer: make sequencer abort safer t3510: test that cherry-pick --abort does not unsafely change HEAD am: change safe_to_abort()'s not rewinding error into a warning am: fix filename in safe_to_abort() error message
2 parents 6610af8 + ce73bb2 commit 4fcc091

3 files changed

Lines changed: 61 additions & 2 deletions

File tree

builtin/am.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,7 +2124,7 @@ static int safe_to_abort(const struct am_state *state)
21242124

21252125
if (read_state_file(&sb, state, "abort-safety", 1) > 0) {
21262126
if (get_oid_hex(sb.buf, &abort_safety))
2127-
die(_("could not parse %s"), am_path(state, "abort_safety"));
2127+
die(_("could not parse %s"), am_path(state, "abort-safety"));
21282128
} else
21292129
oidclr(&abort_safety);
21302130

@@ -2134,7 +2134,7 @@ static int safe_to_abort(const struct am_state *state)
21342134
if (!oidcmp(&head, &abort_safety))
21352135
return 1;
21362136

2137-
error(_("You seem to have moved HEAD since the last 'am' failure.\n"
2137+
warning(_("You seem to have moved HEAD since the last 'am' failure.\n"
21382138
"Not rewinding to ORIG_HEAD"));
21392139

21402140
return 0;

sequencer.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
2828
static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
2929
static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
3030
static GIT_PATH_FUNC(git_path_head_file, "sequencer/head")
31+
static GIT_PATH_FUNC(git_path_abort_safety_file, "sequencer/abort-safety")
3132

3233
/*
3334
* A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
@@ -263,6 +264,20 @@ static int error_dirty_index(struct replay_opts *opts)
263264
return -1;
264265
}
265266

267+
static void update_abort_safety_file(void)
268+
{
269+
struct object_id head;
270+
271+
/* Do nothing on a single-pick */
272+
if (!file_exists(git_path_seq_dir()))
273+
return;
274+
275+
if (!get_oid("HEAD", &head))
276+
write_file(git_path_abort_safety_file(), "%s", oid_to_hex(&head));
277+
else
278+
write_file(git_path_abort_safety_file(), "%s", "");
279+
}
280+
266281
static int fast_forward_to(const unsigned char *to, const unsigned char *from,
267282
int unborn, struct replay_opts *opts)
268283
{
@@ -292,6 +307,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
292307
strbuf_release(&sb);
293308
strbuf_release(&err);
294309
ref_transaction_free(transaction);
310+
update_abort_safety_file();
295311
return 0;
296312
}
297313

@@ -766,6 +782,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
766782

767783
leave:
768784
free_message(commit, &msg);
785+
update_abort_safety_file();
769786

770787
return res;
771788
}
@@ -1085,9 +1102,34 @@ static int save_head(const char *head)
10851102
return 0;
10861103
}
10871104

1105+
static int rollback_is_safe(void)
1106+
{
1107+
struct strbuf sb = STRBUF_INIT;
1108+
struct object_id expected_head, actual_head;
1109+
1110+
if (strbuf_read_file(&sb, git_path_abort_safety_file(), 0) >= 0) {
1111+
strbuf_trim(&sb);
1112+
if (get_oid_hex(sb.buf, &expected_head)) {
1113+
strbuf_release(&sb);
1114+
die(_("could not parse %s"), git_path_abort_safety_file());
1115+
}
1116+
strbuf_release(&sb);
1117+
}
1118+
else if (errno == ENOENT)
1119+
oidclr(&expected_head);
1120+
else
1121+
die_errno(_("could not read '%s'"), git_path_abort_safety_file());
1122+
1123+
if (get_oid("HEAD", &actual_head))
1124+
oidclr(&actual_head);
1125+
1126+
return !oidcmp(&actual_head, &expected_head);
1127+
}
1128+
10881129
static int reset_for_rollback(const unsigned char *sha1)
10891130
{
10901131
const char *argv[4]; /* reset --merge <arg> + NULL */
1132+
10911133
argv[0] = "reset";
10921134
argv[1] = "--merge";
10931135
argv[2] = sha1_to_hex(sha1);
@@ -1142,6 +1184,12 @@ int sequencer_rollback(struct replay_opts *opts)
11421184
error(_("cannot abort from a branch yet to be born"));
11431185
goto fail;
11441186
}
1187+
1188+
if (!rollback_is_safe()) {
1189+
/* Do not error, just do not rollback */
1190+
warning(_("You seem to have moved HEAD. "
1191+
"Not rewinding, check your HEAD!"));
1192+
} else
11451193
if (reset_for_rollback(sha1))
11461194
goto fail;
11471195
strbuf_release(&buf);
@@ -1346,6 +1394,7 @@ int sequencer_pick_revisions(struct replay_opts *opts)
13461394
return -1;
13471395
if (save_opts(opts))
13481396
return -1;
1397+
update_abort_safety_file();
13491398
res = pick_commits(&todo_list, opts);
13501399
todo_list_release(&todo_list);
13511400
return res;

t/t3510-cherry-pick-sequence.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,16 @@ test_expect_success '--abort to cancel single cherry-pick' '
147147
git diff-index --exit-code HEAD
148148
'
149149

150+
test_expect_success '--abort does not unsafely change HEAD' '
151+
pristine_detach initial &&
152+
test_must_fail git cherry-pick picked anotherpick &&
153+
git reset --hard base &&
154+
test_must_fail git cherry-pick picked anotherpick &&
155+
git cherry-pick --abort 2>actual &&
156+
test_i18ngrep "You seem to have moved HEAD" actual &&
157+
test_cmp_rev base HEAD
158+
'
159+
150160
test_expect_success 'cherry-pick --abort to cancel multiple revert' '
151161
pristine_detach anotherpick &&
152162
test_expect_code 1 git revert base..picked &&

0 commit comments

Comments
 (0)