[BUG] submodule merge tries to read B's commit from A

From: Guillaume CHAUVEL <hidden>
Date: 2026-09-23 20:20:37

I ran into two problems while merging a superproject with submodules.

One problem, involving the repository used for commit-graph lookups, was
reported in this thread:
https://lore.kernel.org/git/d3241733-d015-4646-88e0-06e56a04e77b@nutanix.com/T/#m174067937aaf76e9fa844386961b3e9e66c1e4d9 (local)

The other problem is that during a merge, Git sometimes tries to read
from submodule A a commit that exists only in submodule B. I reproduced
this with Git v2.56.0-rc2, built from source in an Ubuntu 26.04
container and an Alpine container. The reproducer below triggered the
issue in all 50 Ubuntu runs and in 43 out of 50 Alpine runs.

The merge should report a submodule conflict, not look for B's commit
in A or report A as corrupt. The script checks the OID's presence in
both submodules and prints the "BUG" line when it finds this case.

---------
#!/usr/bin/env bash

set -euo pipefail

unset $(git rev-parse --local-env-vars)
export LC_ALL=C
export GIT_CONFIG_NOSYSTEM=1
export GIT_CONFIG_GLOBAL=/dev/null
export GIT_DEFAULT_HASH=sha1
export GIT_TEMPLATE_DIR=
export GIT_AUTHOR_NAME=Reproducer
export GIT_AUTHOR_EMAIL=reproducer@example.invalid
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
export GIT_AUTHOR_DATE='2000-01-01T00:00:00 +0000'
export GIT_COMMITTER_DATE='2000-01-01T00:00:00 +0000'

tmpdir=$(mktemp -d)

for name in A B; do
mkdir "$tmpdir/source-$name"
cd "$tmpdir/source-$name"
git init -q -b main
printf '%s base\n' "$name" >file
git add file
git commit -qm "$name base"
git switch -qc branch-a
git commit --allow-empty -qm "$name branch-a"
git switch -qc branch-b main
git commit --allow-empty -qm "$name branch-b"
git switch -q main
done

mkdir "$tmpdir/super"
cd "$tmpdir/super"
git init -q -b base
git config --local protocol.file.allow always
for name in A B; do
# reproduces the bug
git -c protocol.file.allow=always submodule add -q
"file://$tmpdir/source-$name" "$name"

# does not reproduce the bug
# git  -c protocol.file.allow=always submodule add -q
"$tmpdir/source-$name" "$name"
done
git add .
git commit -qm base

git switch -qc branch-a
for name in A B; do
(cd "$name" && git switch -q -c branch-a --track origin/branch-a)
done
git add A B
git commit -qm branch-a

git switch -qc branch-b base
for name in A B; do
(cd "$name" && git switch -q -c branch-b --track origin/branch-b)
done
git add A B
git commit -qm branch-b

cd "$tmpdir"
git -c protocol.file.allow=always clone -q --no-local
"file://$tmpdir/super" clone
cd clone
git -c protocol.file.allow=always submodule update --init -q
git switch -q -c branch-a --track origin/branch-a
if merge_output=$(git merge branch-b 2>&1); then
merge_status=0
else
merge_status=$?
fi
printf 'git merge exit status: %s\n%s\n' "$merge_status" "$merge_output"

if [[ $merge_output =~ Could\ not\ read\ ([0-9a-f]{40}|[0-9a-f]{64}) ]]; then
foreign_oid=${BASH_REMATCH[1]}
if ! (cd A && git cat-file -e "$foreign_oid" 2>/dev/null) &&
(cd B && git cat-file -e "$foreign_oid" 2>/dev/null); then
printf 'BUG: OID %s belongs to B instead of A\n' "$foreign_oid"
fi
fi
---------

One run produced:

git merge exit status: 2
error: Could not read 7d549ba7e9152029e66ddca8dd23ee7da32b036f
error: could not parse commit 7d549ba7e9152029e66ddca8dd23ee7da32b036f
error: failed to merge submodule A (repository corrupt)
Merge with strategy ort failed.
BUG: OID 7d549ba7e9152029e66ddca8dd23ee7da32b036f belongs to B instead of A

An AI analysis identified a likely cause: a delta-base cache entry may
remain after its pack is closed. If a pack from another submodule reuses
the same packed_git address and base offset, Git may return stale cached
data.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help