From b6f5e80fdf32c270ff812c1743e720dedca7d5e2 Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Sat, 29 Aug 2026 07:00:28 +0000 Subject: [PATCH] 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 " 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 Signed-off-by: Junio C Hamano --- replay.c | 7 +++++++ t/t3650-replay-basics.sh | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/replay.c b/replay.c index 463c900d6c..33e21b2032 100644 --- a/replay.c +++ b/replay.c @@ -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; diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh index 3353bc4a4d..12348b4a5f 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -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