]> git.ipfire.org Git - thirdparty/git.git/commitdiff
sequencer: be more careful with external merge
authorPhillip Wood <phillip.wood@dunelm.org.uk>
Wed, 15 Jul 2026 15:21:56 +0000 (16:21 +0100)
committerJunio C Hamano <gitster@pobox.com>
Wed, 15 Jul 2026 19:16:21 +0000 (12:16 -0700)
If an external merge strategy cannot merge (for example because it
would overwrite an untracked file) it exits with a non-zero exit
code other than 1. This should be treated differently from a merge
with conflicts, which is signaled by an exit code of 1, because, as
the merge failed, we need to reschedule the last pick. The caller
expects us to return -1 in this case. Also reschedule without trying
to merge if the commit message cannot be written as that prevents us
from successfully picking the commit.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
sequencer.c
t/t3404-rebase-interactive.sh

index 57855b0066ac98646facc3b3a610248b5d80a28f..eaffa8ebb844ef65d73005b3856a42fa682a3453 100644 (file)
@@ -2453,14 +2453,25 @@ static int do_pick_commit(struct repository *r,
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
 
-               res = write_message(ctx->message.buf, ctx->message.len,
-                                   git_path_merge_msg(r), 0);
+               if (write_message(ctx->message.buf, ctx->message.len,
+                                 git_path_merge_msg(r), 0)) {
+                       res = -1;
+                       goto leave;
+               }
 
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
-               res |= try_merge_command(r, opts->strategy,
-                                        opts->xopts.nr, opts->xopts.v,
+               res = try_merge_command(r, opts->strategy,
+                                       opts->xopts.nr, opts->xopts.v,
                                        common, oid_to_hex(&head), remotes);
+               /*
+                * If there were conflicts, try_merge_command() returns 1,
+                * any other no-zero return code means that either the merge
+                * command could not be run, or it failed to merge.
+                */
+               if (res && res != 1)
+                       res = -1;
+
                commit_list_free(common);
                commit_list_free(remotes);
        }
index 58b3bb0c271aaebab4381795c9d9dcf8ba3a7593..297b84e60d59ccbb486bc8f3668dc30d8cfe1c71 100755 (executable)
@@ -1251,6 +1251,17 @@ test_expect_success 'interrupted rebase -i with --strategy and -X' '
        test $(cat file1) = Z
 '
 
+test_expect_success 'failing pick with --strategy is rescheduled' '
+       test_when_finished "rm -rf bin; test_might_fail git rebase --abort" &&
+       mkdir bin &&
+       echo exit 2 | write_script bin/git-merge-fail &&
+       git log -1 --format="pick %H # %s" HEAD >expect &&
+       test_must_fail env PATH="$PWD/bin:$PATH" \
+               git rebase --no-ff --strategy fail HEAD^ &&
+       test_cmp expect .git/rebase-merge/git-rebase-todo &&
+       test_cmp expect .git/rebase-merge/done
+'
+
 test_expect_success 'rebase -i error on commits with \ in message' '
        current_head=$(git rev-parse HEAD) &&
        test_when_finished "git rebase --abort; git reset --hard $current_head; rm -f error" &&