merge-ort: maintain expected invariant for priv member

The calling convention for the merge machinery is
   One call to          init_merge_options()
   One or more calls to merge_incore_[non]recursive()
   One call to          merge_finalize()
      (possibly indirectly via merge_switch_to_result())
Both merge_switch_to_result() and merge_finalize() expect
   opt->priv == NULL && result->priv != NULL
which is supposed to be set up by our move_opt_priv_to_result_priv()
function.  However, two codepaths dealing with error cases did not
execute this necessary logic, which could result in assertion failures
(or, if assertions were compiled out, could result in segfaults).  Fix
the oversight and add a test that would have caught one of these
problems.

While at it, also tighten an existing test for a non-recursive merge
to verify that it fails with appropriate status.  Most merge tests in
the testsuite check either for success or conflicts; those testing for
neither are rare and it is good to ensure they support the invariant
assumed by builtin/merge.c in this comment:
    /*
     * The backend exits with 1 when conflicts are
     * left to be resolved, with 2 when it does not
     * handle the given merge at all.
     */
So, explicitly check for the exit status of 2 in these cases.

Reported-by: Matt Cree <matt.cree@gearset.com>
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Elijah Newren
2024-06-19 03:00:14 +00:00
committed by Junio C Hamano
parent e79bdb426c
commit 0b4f726cde
2 changed files with 43 additions and 2 deletions

View File

@ -5050,6 +5050,7 @@ redo:
oid_to_hex(&side1->object.oid),
oid_to_hex(&side2->object.oid));
result->clean = -1;
move_opt_priv_to_result_priv(opt, result);
return;
}
trace2_region_leave("merge", "collect_merge_info", opt->repo);
@ -5080,7 +5081,7 @@ redo:
/* existence of conflicted entries implies unclean */
result->clean &= strmap_empty(&opt->priv->conflicted);
}
if (!opt->priv->call_depth)
if (!opt->priv->call_depth || result->clean < 0)
move_opt_priv_to_result_priv(opt, result);
}

View File

@ -185,7 +185,7 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal'
>./please-abort &&
echo "* merge=custom" >.gitattributes &&
test_must_fail git merge main 2>err &&
test_expect_code 2 git merge main 2>err &&
grep "^error: failed to execute internal merge" err &&
git ls-files -u >output &&
git diff --name-only HEAD >>output &&
@ -261,4 +261,44 @@ test_expect_success 'binary files with union attribute' '
grep -i "warning.*cannot merge.*HEAD vs. bin-main" output
'
test_expect_success !WINDOWS 'custom merge driver that is killed with a signal on recursive merge' '
test_when_finished "rm -f output please-abort" &&
test_when_finished "git checkout side" &&
git reset --hard anchor &&
git checkout -b base-a main^ &&
echo base-a >text &&
git commit -m base-a text &&
git checkout -b base-b main^ &&
echo base-b >text &&
git commit -m base-b text &&
git checkout -b recursive-a base-a &&
test_must_fail git merge base-b &&
echo recursive-a >text &&
git add text &&
git commit -m recursive-a &&
git checkout -b recursive-b base-b &&
test_must_fail git merge base-a &&
echo recursive-b >text &&
git add text &&
git commit -m recursive-b &&
git config --replace-all \
merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
git config --replace-all \
merge.custom.name "custom merge driver for testing" &&
>./please-abort &&
echo "* merge=custom" >.gitattributes &&
test_expect_code 2 git merge recursive-a 2>err &&
grep "^error: failed to execute internal merge" err &&
git ls-files -u >output &&
git diff --name-only HEAD >>output &&
test_must_be_empty output
'
test_done