Volume XXII, number 279Tuesday, October 6, 2026Latest message 33 minutes ago

The Git List

News and archive of git@vger.kernel.org, since April 2005

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

4 messages between Sep 23, 2026 and Oct 2, 2026, from Guillaume CHAUVEL, Philippe Blain, Patrick Steinhardt.

Plain Markdown or JSON for tools and agents.

Guillaume CHAUVELSep 23, 2026, 20:20 UTC on lore
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

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.

Philippe BlainSep 30, 2026, 18:31 UTC in reply to Guillaume CHAUVEL on lore

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

Hi Guillaume,
Le 2026-09-23 à 16 h 20, Guillaume CHAUVEL a écrit :
Show 5 quoted lines
> 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

FYI, the above bug was fixed in 700f7b74de (commit-reach: parse commits in the given repository, 2026-09-16), which is currently in 'next' but not yet in master.

Show 9 quoted lines
> 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.

Thanks for the reproducer, I confirm I see the same behaviour with v2.56.0-rc2, on RHEL 9. With v2.48.1, the merge results in a conflict, instead of aborting, although I get a spurious "hash mismatch" message, and the reason for the conflict ("commits not present") is wrong:

git version 2.48.1 git merge exit status: 1 error: hash mismatch 2ca9f0f330e976b992fc18633d1d267b8aad596e Failed to merge submodule A (commits not present) CONFLICT (submodule): Merge conflict in A Failed to merge submodule B CONFLICT (submodule): Merge conflict in B Automatic merge failed; fix conflicts and then commit the result.

With 2.33.0, which I chose randomly, we get the correct behaviour:

git version 2.33.0 git merge exit status: 1 Failed to merge submodule A CONFLICT (submodule): Merge conflict in A Failed to merge submodule B CONFLICT (submodule): Merge conflict in B Automatic merge failed; fix conflicts and then commit the result.

I turned your reproducer into a bisection script (~/bisect-merge.sh) by tweaking the final 'if':

```
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"
        exit 1
    fi
elif [[ $merge_output =~ hash\ mismatch ]];then
        [ ${1:-""} = MISMATCH ] && exit 1 || exit 0
else
    exit 0
fi
```
and invoking it in my ~/bisect-git.sh script:

``` #!/bin/bash

make clean > /dev/null
# build but keep the output on one line
if	make -j |& { while read line; do  printf "\033[K%s\r" "${line}" ; done; 
                     printf "\033[KFinished building $(cat GIT-VERSION-FILE)\n" ; }
then
	# run project specific test and report its status
	export PATH="$PWD/bin-wrappers/:$PATH"
	~/bisect-merge.sh "$@"
	status=$?
else
	# tell the caller this is untestable
	status=125
fi

# return control echo exit $status ```

Bisecting the merge failure with:
	git bisect start v2.56.0-rc2 v2.48.1 && git bisect run ~/bisect-git.sh

finds bb5da75d61 (commit: use commit graph in lookup_commit_reference_gently(), 2026-02-16), i.e. v2.54.0-rc0~136^2, which is the same commit from which the commit-graph bug mentioned above originates. I CC'ed Patrick, its author.

Bisecting the "hash mismatch" behaviour with:
	git bisect start v2.48.1 v2.33.0 && git bisect run ~/bisect-git.sh MISMATCH

finds 6f1e9394e2 (object: fix leaking packfiles when closing object store, 2024-08-08), i.e. v2.47.0-rc0~123^2, which is also authored by Patrick.

I did not yet dig further, but I have a few additional observations:
- in contrast to the commit-graph bug, disabling the use of commit-graphs via
  'git config --global core.commitGraph false' early in the script, by moving the 'tmpdir'
  definition to the top and setting GIT_CONFIG_GLOBAL=$tmpdir/.gitconfig, does not change
  the behaviour, neither in the "repository corrupt" case, nor in the "hash mismatch" case.
- On Ubuntu 22.02 under WSL, the reproducer does not trigger the bug on v2.56.0-rc2 (on a dozen runs),
  but it does trigger it on v2.55.0. Funnily on that system with v2.56.0-rc2 I get the correct behaviour !
  (no "hash mismatch" either).
- On a Ubuntu 22.04 Docker container, I get the same behaviour as on RHEL 9.
Show 5 quoted lines
> 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.
> 
Patrick SteinhardtOct 1, 2026, 14:09 UTC in reply to Philippe Blain on lore

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

On Wed, Sep 30, 2026 at 02:31:44PM -0400, Philippe Blain wrote:
Show 15 quoted lines
> I did not yet dig further, but I have a few additional observations:
> 
> - in contrast to the commit-graph bug, disabling the use of commit-graphs via
>   'git config --global core.commitGraph false' early in the script, by moving the 'tmpdir'
>   definition to the top and setting GIT_CONFIG_GLOBAL=$tmpdir/.gitconfig, does not change
>   the behaviour, neither in the "repository corrupt" case, nor in the "hash mismatch" case.
> - On Ubuntu 22.02 under WSL, the reproducer does not trigger the bug on v2.56.0-rc2 (on a dozen runs),
>   but it does trigger it on v2.55.0. Funnily on that system with v2.56.0-rc2 I get the correct behaviour !
>   (no "hash mismatch" either).
> - On a Ubuntu 22.04 Docker container, I get the same behaviour as on RHEL 9.
> 
> > 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.

Yup, that seems to be the issue indeed. We should really be clearing packfiles out of the delta base cache when closing packfiles, but we don't right now. I'll investigate tomorrow.

Thanks!
Patrick
Patrick SteinhardtOct 2, 2026, 08:10 UTC in reply to Patrick Steinhardt on lore

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

On Thu, Oct 01, 2026 at 04:09:41PM +0200, Patrick Steinhardt wrote:
Show 20 quoted lines
> On Wed, Sep 30, 2026 at 02:31:44PM -0400, Philippe Blain wrote:
> > I did not yet dig further, but I have a few additional observations:
> > 
> > - in contrast to the commit-graph bug, disabling the use of commit-graphs via
> >   'git config --global core.commitGraph false' early in the script, by moving the 'tmpdir'
> >   definition to the top and setting GIT_CONFIG_GLOBAL=$tmpdir/.gitconfig, does not change
> >   the behaviour, neither in the "repository corrupt" case, nor in the "hash mismatch" case.
> > - On Ubuntu 22.02 under WSL, the reproducer does not trigger the bug on v2.56.0-rc2 (on a dozen runs),
> >   but it does trigger it on v2.55.0. Funnily on that system with v2.56.0-rc2 I get the correct behaviour !
> >   (no "hash mismatch" either).
> > - On a Ubuntu 22.04 Docker container, I get the same behaviour as on RHEL 9.
> > 
> > > 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.
> 
> Yup, that seems to be the issue indeed. We should really be clearing
> packfiles out of the delta base cache when closing packfiles, but we
> don't right now. I'll investigate tomorrow.
I've sent [1] now to fix this issue. Thanks!
Patrick
[1]: <20261002-pks-packfile-stale-delta-base-cache-v1-0-7592a3e31ae0@pks.im>

Back to recent threads