* [BUG] git bundle create with bitmaps omits tree required by advertised ref
@ 2026-09-08 8:23 Peter Elmers
2026-09-08 22:33 ` Taylor Blau
0 siblings, 1 reply; 2+ messages in thread
From: Peter Elmers @ 2026-09-08 8:23 UTC (permalink / raw)
To: git
What did you do before the bug happened? (Steps to reproduce your issue)
I created two sibling commits with the same root tree, stored one at a local
branch and the other at a remote-tracking ref, wrote a pack bitmap, and created
a bundle that included the local branch while excluding the remote-tracking
ref.
Adding `-c pack.useBitmaps=false` appears to fix the issue.
The following script reproduces the issue:
#!/bin/sh
set -eu
repro_dir=$(mktemp -d)
source_repo="$repro_dir/source.git"
recipient_repo="$repro_dir/recipient.git"
git init -q --bare -b main "$source_repo"
git -C "$source_repo" config user.name A
git -C "$source_repo" config user.email a@example.com
empty_tree=$(git -C "$source_repo" mktree </dev/null)
base=$(printf 'base\n' | git -C "$source_repo" commit-tree "$empty_tree")
git -C "$source_repo" update-ref refs/heads/main "$base"
git clone -q --bare "$source_repo" "$recipient_repo"
blob=$(printf 'change\n' | git -C "$source_repo" hash-object -w --stdin)
shared_tree=$(printf '100644 blob %s\tfile\n' "$blob" |
git -C "$source_repo" mktree)
remote_tip=$(printf 'original\n' |
git -C "$source_repo" commit-tree "$shared_tree" -p "$base")
local_tip=$(printf 'rewritten\n' |
git -C "$source_repo" commit-tree "$shared_tree" -p "$base")
git -C "$source_repo" update-ref refs/heads/feature "$local_tip"
git -C "$source_repo" update-ref refs/remotes/origin/feature "$remote_tip"
git -C "$source_repo" repack -q -ad --write-bitmap-index
git -C "$source_repo" bundle create "$repro_dir/broken.bundle" \
feature ^refs/remotes/origin/feature
git -C "$recipient_repo" bundle verify "$repro_dir/broken.bundle"
git -C "$recipient_repo" bundle unbundle "$repro_dir/broken.bundle"
git -C "$recipient_repo" cat-file -e "$local_tip^{tree}"
The resulting commit and object topology is:
refs/remotes/origin/feature (excluded)
|
remote_tip
/ \
parent / \ tree
/ \
base shared_tree
\ /
parent \ / tree
\ /
local_tip
|
refs/heads/feature (advertised)
Both tip commits have `base` as their parent and `shared_tree` as their root
tree. The bundle advertises `local_tip` and excludes `remote_tip`.
What did you expect to happen? (Expected behavior)
After `git bundle verify` confirms that the recipient has every declared
prerequisite and `git bundle unbundle` succeeds, every object required by the
advertised `feature` commit should be available. The final `git cat-file`
command should exit successfully.
What happened instead? (Actual behavior)
`git bundle verify` reports that the bundle is okay and `git bundle unbundle`
succeeds, but the final command (cat-file) exits 128:
fatal: Not a valid object name <local-tip>^{tree}
The advertised local commit object is present, but its root tree is absent.
What's different between what you expected and what actually happened?
The bitmap-backed bundle omits an object required by an advertised ref without
declaring the excluded sibling commit as a prerequisite. A recipient containing
all declared prerequisites can therefore accept the bundle but cannot traverse
or check out the advertised commit.
Anything else you want to add:
Adding `-c pack.useBitmaps=false` to `git bundle create` makes the final
`git cat-file` command succeed.
`pack.useBitmapBoundaryTraversal=true` did not prevent the omission in a
separate run of the same commit topology.
So it looks like a bitmap format bug.
Relevant documentation:
https://git-scm.com/docs/git-config#Documentation/git-config.txt-packuseBitmaps
https://git-scm.com/docs/git-config#Documentation/git-config.txt-packuseBitmapBoundaryTraversal
https://git-scm.com/docs/git-bundle#_object_prerequisites
[System Info]
git version:
git version 2.54.0
cpu: x86_64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
rust: disabled
SHA-1: SHA1_DC
SHA-256: SHA256_BLK
default-ref-format: files
default-hash: sha1
uname: Linux 7.0.0-1012-aws #12~24.04.1-Ubuntu SMP PREEMPT Wed Aug 12
14:00:57 UTC 2026 x86_64
compiler info: gnuc: 9.4
libc info: glibc: 2.39
$SHELL (typically, interactive shell): /usr/bin/zsh
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [BUG] git bundle create with bitmaps omits tree required by advertised ref
2026-09-08 8:23 [BUG] git bundle create with bitmaps omits tree required by advertised ref Peter Elmers
@ 2026-09-08 22:33 ` Taylor Blau
0 siblings, 0 replies; 2+ messages in thread
From: Taylor Blau @ 2026-09-08 22:33 UTC (permalink / raw)
To: Peter Elmers; +Cc: git, Jeff King
On Tue, Sep 08, 2026 at 10:23:37AM +0200, Peter Elmers wrote:
> What did you do before the bug happened? (Steps to reproduce your issue)
>
> I created two sibling commits with the same root tree, stored one at a local
> branch and the other at a remote-tracking ref, wrote a pack bitmap, and created
> a bundle that included the local branch while excluding the remote-tracking
> ref.
>
> Adding `-c pack.useBitmaps=false` appears to fix the issue.
>
> The following script reproduces the issue:
Interesting. I reproduced what you wrote here using your script. I
highly suspect what's going on here is that the non-bitmap case
overcounts some objects beyond the boundary whereas the bitmap case
builds an exact answer.
The non-bitmap traversal only marked boundary trees UNINTERESTING in
this case, so it happened to keep the shared tree.
We can fix this with something like the following (only lightly tested)
patch, but it has some test fallout for cases where we generate bundles
with only tags (+CC Peff who may have some opinions).
--- 8< ---
Subject: [PATCH] bundle: restrict pack haves to recorded prerequisites
`write_pack_data()` uses every UNINTERESTING tip as a pack have, but the
bundle header records only boundary commits. If an included commit
shares a tree with an excluded sibling, a bitmap walk can omit that tree
while the header requires only their parent. A recipient with the parent
can verify and unbundle the result while lacking the advertised
commit's tree.
Restrict pack haves to UNINTERESTING commits marked BOUNDARY, which
are recorded as prerequisites. Other excluded tips must not suppress
objects that the advertised refs need.
As a consequence, there is a bit of test fallout in t6020.13. That test
includes the tags while excluding all branch tips. Its header records
no prerequisites, but its pack previously contained only the three
tag objects: the excluded tips caused their target histories to be
omitted.
But that bundle was already incomplete! With no prerequisites, it must
provide the history reachable from its advertised tags. Restricting
pack haves to recorded prerequisites includes that history, increasing
the expected object count as below.
Reported-by: Peter Elmers <peter.elmers@databricks.com>
Signed-off-by: Taylor Blau <ttaylorr@openai.com>
---
bundle.c | 7 +++++--
t/t6020-bundle-misc.sh | 14 ++++++++++++--
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/bundle.c b/bundle.c
index f55a521b2a1..06a52b8e705 100644
--- a/bundle.c
+++ b/bundle.c
@@ -359,10 +359,13 @@ static int write_pack_data(int bundle_fd, struct rev_info *revs, int progress)
for (i = 0; i < revs->pending.nr; i++) {
struct object *object = revs->pending.objects[i].item;
- if (object->flags & UNINTERESTING)
+ if (object->flags & UNINTERESTING) {
+ if (!(object->flags & BOUNDARY))
+ continue; /* Not a bundle prerequisite. */
oid_array_append(&opts.haves, &object->oid);
- else
+ } else {
oid_array_append(&opts.wants, &object->oid);
+ }
}
if (odb_generate_pack(revs->repo->objects, &generator, &opts)) {
diff --git a/t/t6020-bundle-misc.sh b/t/t6020-bundle-misc.sh
index 939d4214f4d..6fce6252a11 100755
--- a/t/t6020-bundle-misc.sh
+++ b/t/t6020-bundle-misc.sh
@@ -446,8 +446,9 @@ test_expect_success 'create bundle 4 - with tags' '
make_user_friendly_and_stable_output >actual &&
test_cmp expect actual &&
- test_bundle_object_count 4.bdl 3 &&
- test_bundle_object_count stdin-4.bdl 3
+ # With no prerequisites, include the tag targets and their history.
+ test_bundle_object_count 4.bdl 40 &&
+ test_bundle_object_count stdin-4.bdl 40
'
test_expect_success 'clone from bundle' '
@@ -784,4 +785,13 @@ do
'
done
+test_expect_success 'bundle with bitmaps includes trees shared with an excluded sibling' '
+ commit=$(git commit-tree main^{tree} -p main^ -m rewritten) &&
+ git branch rewritten "$commit" &&
+ test_when_finished "git branch -D rewritten" &&
+ git repack -adb &&
+ git -c pack.useBitmaps=true bundle create bitmap.bdl main..rewritten &&
+ test_bundle_object_count bitmap.bdl 3
+'
+
test_done
base-commit: b8242b093d9e941a34460d715e3ce616a34ac3fe
--
2.55.0.openai.744.g47c847ce2641
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-08 22:33 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-08 8:23 [BUG] git bundle create with bitmaps omits tree required by advertised ref Peter Elmers
2026-09-08 22:33 ` Taylor Blau
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox