Skip to content

Commit 203573b

Browse files
phillipwoodgitster
authored andcommitted
rebase -i: fix adding failed command to the todo list
When rebasing commands are moved from the todo list in "git-rebase-todo" to the "done" file (which is used by "git status" to show the recently executed commands) just before they are executed. This means that if a command fails because it would overwrite an untracked file it has to be added back into the todo list before the rebase stops for the user to fix the problem. Unfortunately when a failed command is added back into the todo list the command preceding it is erroneously appended to the "done" file. This means that when rebase stops after "pick B" fails the "done" file contains pick A pick B pick A instead of pick A pick B This happens because save_todo() updates the "done" file with the previous command whenever "git-rebase-todo" is updated. When we add the failed pick back into "git-rebase-todo" we do not want to update "done". Fix this by adding a "reschedule" parameter to save_todo() which prevents the "done" file from being updated when adding a failed command back into the "git-rebase-todo" file. A couple of the existing tests are modified to improve their coverage as none of them trigger this bug or check the "done" file. Reported-by: Stefan Haller <[email protected]> Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 405509c commit 203573b

File tree

3 files changed

+40
-23
lines changed

3 files changed

+40
-23
lines changed

sequencer.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3380,7 +3380,8 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts)
33803380
return -1;
33813381
}
33823382

3383-
static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
3383+
static int save_todo(struct todo_list *todo_list, struct replay_opts *opts,
3384+
int reschedule)
33843385
{
33853386
struct lock_file todo_lock = LOCK_INIT;
33863387
const char *todo_path = get_todo_path(opts);
@@ -3390,7 +3391,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
33903391
* rebase -i writes "git-rebase-todo" without the currently executing
33913392
* command, appending it to "done" instead.
33923393
*/
3393-
if (is_rebase_i(opts))
3394+
if (is_rebase_i(opts) && !reschedule)
33943395
next++;
33953396

33963397
fd = hold_lock_file_for_update(&todo_lock, todo_path, 0);
@@ -3403,7 +3404,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)
34033404
if (commit_lock_file(&todo_lock) < 0)
34043405
return error(_("failed to finalize '%s'"), todo_path);
34053406

3406-
if (is_rebase_i(opts) && next > 0) {
3407+
if (is_rebase_i(opts) && !reschedule && next > 0) {
34073408
const char *done = rebase_path_done();
34083409
int fd = open(done, O_CREAT | O_WRONLY | O_APPEND, 0666);
34093410
int ret = 0;
@@ -4716,7 +4717,7 @@ static int pick_commits(struct repository *r,
47164717
const char *arg = todo_item_get_arg(todo_list, item);
47174718
int check_todo = 0;
47184719

4719-
if (save_todo(todo_list, opts))
4720+
if (save_todo(todo_list, opts, reschedule))
47204721
return -1;
47214722
if (is_rebase_i(opts)) {
47224723
if (item->command != TODO_COMMENT) {
@@ -4797,8 +4798,7 @@ static int pick_commits(struct repository *r,
47974798
get_item_line_length(todo_list,
47984799
todo_list->current),
47994800
get_item_line(todo_list, todo_list->current));
4800-
todo_list->current--;
4801-
if (save_todo(todo_list, opts))
4801+
if (save_todo(todo_list, opts, reschedule))
48024802
return -1;
48034803
if (item->commit)
48044804
write_rebase_head(&item->commit->object.oid);

t/t3404-rebase-interactive.sh

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,27 +1277,34 @@ test_expect_success 'todo count' '
12771277
'
12781278

12791279
test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
1280-
git checkout --force branch2 &&
1280+
git checkout --force A &&
12811281
git clean -f &&
1282+
cat >todo <<-EOF &&
1283+
exec >file2
1284+
pick $(git rev-parse B) B
1285+
pick $(git rev-parse C) C
1286+
pick $(git rev-parse D) D
1287+
exec cat .git/rebase-merge/done >actual
1288+
EOF
12821289
(
1283-
set_fake_editor &&
1284-
FAKE_LINES="edit 1 2" git rebase -i A
1290+
set_replace_editor todo &&
1291+
test_must_fail git rebase -i A
12851292
) &&
1286-
test_cmp_rev HEAD F &&
1287-
test_path_is_missing file6 &&
1288-
>file6 &&
1289-
test_must_fail git rebase --continue &&
1290-
test_cmp_rev HEAD F &&
1291-
test_cmp_rev REBASE_HEAD I &&
1292-
rm file6 &&
1293+
test_cmp_rev HEAD B &&
1294+
test_cmp_rev REBASE_HEAD C &&
1295+
head -n3 todo >expect &&
1296+
test_cmp expect .git/rebase-merge/done &&
1297+
rm file2 &&
12931298
test_path_is_missing .git/rebase-merge/patch &&
12941299
echo changed >file1 &&
12951300
git add file1 &&
12961301
test_must_fail git rebase --continue 2>err &&
12971302
grep "error: you have staged changes in your working tree" err &&
12981303
git reset --hard HEAD &&
12991304
git rebase --continue &&
1300-
test_cmp_rev HEAD I
1305+
test_cmp_rev HEAD D &&
1306+
tail -n3 todo >>expect &&
1307+
test_cmp expect actual
13011308
'
13021309

13031310
test_expect_success 'rebase -i commits that overwrite untracked files (squash)' '

t/t3430-rebase-merges.sh

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,24 @@ test_expect_success 'generate correct todo list' '
128128
'
129129

130130
test_expect_success '`reset` refuses to overwrite untracked files' '
131-
git checkout -b refuse-to-reset &&
131+
git checkout B &&
132132
test_commit dont-overwrite-untracked &&
133-
git checkout @{-1} &&
134-
: >dont-overwrite-untracked.t &&
135-
echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
133+
cat >script-from-scratch <<-EOF &&
134+
exec >dont-overwrite-untracked.t
135+
pick $(git rev-parse B) B
136+
reset refs/tags/dont-overwrite-untracked
137+
pick $(git rev-parse C) C
138+
exec cat .git/rebase-merge/done >actual
139+
EOF
136140
test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
137-
test_must_fail git rebase -ir HEAD &&
138-
git rebase --abort
141+
test_must_fail git rebase -ir A &&
142+
test_cmp_rev HEAD B &&
143+
head -n3 script-from-scratch >expect &&
144+
test_cmp expect .git/rebase-merge/done &&
145+
rm dont-overwrite-untracked.t &&
146+
git rebase --continue &&
147+
tail -n3 script-from-scratch >>expect &&
148+
test_cmp expect actual
139149
'
140150

141151
test_expect_success '`reset` rejects trees' '

0 commit comments

Comments
 (0)