Skip to content

Commit e032abd

Browse files
phillipwoodgitster
authored andcommitted
rebase: fix rewritten list for failed pick
git rebase keeps a list that maps the OID of each commit before it was rebased to the OID of the equivalent commit after the rebase. This list is used to drive the "post-rewrite" hook that is called at the end of a successful rebase. When a rebase stops for the user to resolve merge conflicts the OID of the commit being picked is written to ".git/rebase-merge/stopped-sha". Then when the rebase is continued that OID is added to the list of rewritten commits. Unfortunately if a commit cannot be picked because it would overwrite an untracked file we still write the "stopped-sha1" file. This means that when the rebase is continued the commit is added into the list of rewritten commits even though it has not been picked yet. Fix this by not calling error_with_patch() for failed commands. The pick has failed so there is nothing to commit and therefore we do not want to set up the state files for committing staged changes when the rebase continues. This change means we no-longer write a patch for the failed command or display the error message printed by error_with_patch(). As the command has failed the patch isn't really useful and in any case the user can inspect the commit associated with the failed command by inspecting REBASE_HEAD. Unless the user has disabled it we already print an advice message that is more helpful than the message from error_with_patch() which the user will still see. Even if the advice is disabled the user will see the messages from the merge machinery detailing the problem. The code to add a failed command back into the todo list is duplicated between pick_one_commit() and the loop in pick_commits(). Both sites print advice about the command being rescheduled, decrement the current item and save the todo list. To avoid duplicating this code pick_one_commit() is modified to set a flag to indicate that the command should be rescheduled in the main loop. This simplifies things as only the remaining copy of the code needs to be modified to set REBASE_HEAD rather than calling error_with_patch(). Signed-off-by: Phillip Wood <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f2b5f41 commit e032abd

File tree

4 files changed

+63
-14
lines changed

4 files changed

+63
-14
lines changed

sequencer.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4141,6 +4141,7 @@ static int do_merge(struct repository *r,
41414141
if (ret < 0) {
41424142
error(_("could not even attempt to merge '%.*s'"),
41434143
merge_arg_len, arg);
4144+
unlink(git_path_merge_msg(r));
41444145
goto leave_merge;
41454146
}
41464147
/*
@@ -4631,7 +4632,7 @@ N_("Could not execute the todo command\n"
46314632
static int pick_one_commit(struct repository *r,
46324633
struct todo_list *todo_list,
46334634
struct replay_opts *opts,
4634-
int *check_todo)
4635+
int *check_todo, int* reschedule)
46354636
{
46364637
int res;
46374638
struct todo_item *item = todo_list->items + todo_list->current;
@@ -4644,12 +4645,8 @@ static int pick_one_commit(struct repository *r,
46444645
check_todo);
46454646
if (is_rebase_i(opts) && res < 0) {
46464647
/* Reschedule */
4647-
advise(_(rescheduled_advice),
4648-
get_item_line_length(todo_list, todo_list->current),
4649-
get_item_line(todo_list, todo_list->current));
4650-
todo_list->current--;
4651-
if (save_todo(todo_list, opts))
4652-
return -1;
4648+
*reschedule = 1;
4649+
return -1;
46534650
}
46544651
if (item->command == TODO_EDIT) {
46554652
struct commit *commit = item->commit;
@@ -4749,7 +4746,8 @@ static int pick_commits(struct repository *r,
47494746
}
47504747
}
47514748
if (item->command <= TODO_SQUASH) {
4752-
res = pick_one_commit(r, todo_list, opts, &check_todo);
4749+
res = pick_one_commit(r, todo_list, opts, &check_todo,
4750+
&reschedule);
47534751
if (!res && item->command == TODO_EDIT)
47544752
return 0;
47554753
} else if (item->command == TODO_EXEC) {
@@ -4803,10 +4801,7 @@ static int pick_commits(struct repository *r,
48034801
if (save_todo(todo_list, opts))
48044802
return -1;
48054803
if (item->commit)
4806-
return error_with_patch(r,
4807-
item->commit,
4808-
arg, item->arg_len,
4809-
opts, res, 0);
4804+
write_rebase_head(&item->commit->object.oid);
48104805
} else if (is_rebase_i(opts) && check_todo && !res &&
48114806
reread_todo_if_changed(r, todo_list, opts)) {
48124807
return -1;

t/t3404-rebase-interactive.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,9 @@ test_expect_success 'rebase -i commits that overwrite untracked files (pick)' '
12871287
>file6 &&
12881288
test_must_fail git rebase --continue &&
12891289
test_cmp_rev HEAD F &&
1290+
test_cmp_rev REBASE_HEAD I &&
12901291
rm file6 &&
1292+
test_path_is_missing .git/rebase-merge/patch &&
12911293
git rebase --continue &&
12921294
test_cmp_rev HEAD I
12931295
'
@@ -1305,7 +1307,9 @@ test_expect_success 'rebase -i commits that overwrite untracked files (squash)'
13051307
>file6 &&
13061308
test_must_fail git rebase --continue &&
13071309
test_cmp_rev HEAD F &&
1310+
test_cmp_rev REBASE_HEAD I &&
13081311
rm file6 &&
1312+
test_path_is_missing .git/rebase-merge/patch &&
13091313
git rebase --continue &&
13101314
test $(git cat-file commit HEAD | sed -ne \$p) = I &&
13111315
git reset --hard original-branch2
@@ -1323,7 +1327,9 @@ test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
13231327
>file6 &&
13241328
test_must_fail git rebase --continue &&
13251329
test $(git cat-file commit HEAD | sed -ne \$p) = F &&
1330+
test_cmp_rev REBASE_HEAD I &&
13261331
rm file6 &&
1332+
test_path_is_missing .git/rebase-merge/patch &&
13271333
git rebase --continue &&
13281334
test $(git cat-file commit HEAD | sed -ne \$p) = I
13291335
'

t/t3430-rebase-merges.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,12 @@ test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
165165
test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
166166
test_tick &&
167167
test_must_fail git rebase -ir HEAD &&
168+
test_cmp_rev REBASE_HEAD H^0 &&
168169
grep "^merge -C .* G$" .git/rebase-merge/done &&
169170
grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
170-
test_path_is_file .git/rebase-merge/patch &&
171+
test_path_is_missing .git/rebase-merge/patch &&
171172
172173
: fail because of merge conflict &&
173-
rm G.t .git/rebase-merge/patch &&
174174
git reset --hard conflicting-G &&
175175
test_must_fail git rebase --continue &&
176176
! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&

t/t5407-post-rewrite-hook.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ test_expect_success 'setup' '
1717
git checkout A^0 &&
1818
test_commit E bar E &&
1919
test_commit F foo F &&
20+
git checkout B &&
21+
git merge E &&
22+
git tag merge-E &&
23+
test_commit G G &&
24+
test_commit H H &&
25+
test_commit I I &&
2026
git checkout main &&
2127
2228
test_hook --setup post-rewrite <<-EOF
@@ -173,6 +179,48 @@ test_fail_interactive_rebase () {
173179
)
174180
}
175181

182+
test_expect_success 'git rebase with failed pick' '
183+
clear_hook_input &&
184+
cat >todo <<-\EOF &&
185+
exec >bar
186+
merge -C merge-E E
187+
exec >G
188+
pick G
189+
exec >H 2>I
190+
pick H
191+
fixup I
192+
EOF
193+
194+
(
195+
set_replace_editor todo &&
196+
test_must_fail git rebase -i D D 2>err
197+
) &&
198+
grep "would be overwritten" err &&
199+
rm bar &&
200+
201+
test_must_fail git rebase --continue 2>err &&
202+
grep "would be overwritten" err &&
203+
rm G &&
204+
205+
test_must_fail git rebase --continue 2>err &&
206+
grep "would be overwritten" err &&
207+
rm H &&
208+
209+
test_must_fail git rebase --continue 2>err &&
210+
grep "would be overwritten" err &&
211+
rm I &&
212+
213+
git rebase --continue &&
214+
echo rebase >expected.args &&
215+
cat >expected.data <<-EOF &&
216+
$(git rev-parse merge-E) $(git rev-parse HEAD~2)
217+
$(git rev-parse G) $(git rev-parse HEAD~1)
218+
$(git rev-parse H) $(git rev-parse HEAD)
219+
$(git rev-parse I) $(git rev-parse HEAD)
220+
EOF
221+
verify_hook_input
222+
'
223+
176224
test_expect_success 'git rebase -i (unchanged)' '
177225
git reset --hard D &&
178226
clear_hook_input &&

0 commit comments

Comments
 (0)