replay: fail gracefully when a merge input is unreadable

When objects involved in the merge cannot be read, the merge machinery
will return early with result.clean = -1, and result.tree left as NULL.
pick_regular_commit() tested only "if (!result->clean)", ignoring the
case where "clean < 0".  That causes the code to try to use
result->tree, resulting in a SIGSEGV.

Handle clean < 0 explicitly; the merge machinery will already have printed
messages such as "Could not read <object>" and "collecting merge info
failed for trees...", so we don't need to add much detail beyond the
fact that the merge failed.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
seen
Elijah Newren 2026-08-29 07:00:28 +00:00 committed by Junio C Hamano
parent 48fc56621c
commit b6f5e80fdf
2 changed files with 41 additions and 0 deletions

View File

@ -327,6 +327,13 @@ static struct commit *pick_regular_commit(struct repository *repo,
merge_opt->ancestor = NULL;
merge_opt->branch2 = NULL;

if (result->clean < 0) {
error(_("merge of %s onto %s failed"),
oid_to_hex(&pickme->object.oid),
oid_to_hex(&replayed_base->object.oid));
return NULL;
}

if (!result->clean)
return NULL;


View File

@ -565,4 +565,38 @@ test_expect_success '--onto with --ref rejects multiple revision ranges' '
test_grep "cannot be used with multiple revision ranges" err
'

test_expect_success 'replay fails without segfault when objects are missing' '
test_when_finished "rm -fr unreadable" &&
git init unreadable &&
(
cd unreadable &&

test_write_lines l1 l2 l3 l4 l5 l6 l7 l8 >f &&
git add f &&
git commit -m base &&
git branch base &&

test_write_lines l1 l2 l3 l4 l5 l6 l7 CHANGED >f &&
git commit -am side &&
git branch side &&

git switch -c onto base &&
test_write_lines CHANGED l2 l3 l4 l5 l6 l7 l8 >f &&
git commit -am onto &&

# The replay works while every object is readable.
git replay --onto onto base..side &&

# Removing the onto tree makes parse_tree() fail during the
# incore merge, driving clean < 0 with a NULL result tree.
onto_tree=$(git rev-parse onto^{tree}) &&
obj=$(test_oid_to_path "$onto_tree") &&
mv .git/objects/${obj} saved-tree &&

# Ensure replay gracefully handles the missing object
test_must_fail git replay --onto onto base..side 2>err &&
test_grep -e "Could not read" -e "collecting merge info failed" err
)
'

test_done