Skip to content

Commit 269d73c

Browse files
Sophclaude
andcommitted
Skip pack push for branches fully subsumed by trunk
When trunk is planned first, its ancestry set already tells us which branches contain no new commits — their tip is already reachable from trunk, so trunk's batches have delivered every object those branches need. Previously those branches still ran the full checkpoint machinery: a commit-graph fetch (usually near-empty), a pack fetch (usually near-empty), a temp-ref push, a cutover push, and a temp-ref delete. With this change, the subsumed case is detected in planBatches before the commit-graph fetch, and the resulting plannedBatch carries a subsumed flag instead of checkpoints. executeBatched handles that flag with a single ref-create PushCommand that points at the source hash — no fetch, no pack, no temp ref, no cutover dance. Correctness rests on trunkStopSet containing every commit reachable from the trunk tip, not just the first-parent chain. collectCommitHashes iterates all CommitObjects in the tree:0 fetch result, so merges and their ancestors are included. A branch tip is therefore only marked subsumed when its entire history is already covered by trunk's batches. Added TestExecuteBatchedSubsumedBranchSkipsPack verifies that for a feature branch pointing at an older commit on trunk's chain, the planner emits zero commit-graph fetches and zero pack fetches for that branch, and the executor issues only a single ref-create via PushCommands. Webhook-count note: GitHub fires one push event per ref update, so the subsumed branch still generates one webhook when its ref is created. This change cuts the per-branch HTTP round-trips and object transfer for that branch, not the webhook event itself. Operators concerned about webhook volume during bulk migrations should still disable target webhooks for the duration of the run. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 4cb744e14612
1 parent 5ce21fc commit 269d73c

2 files changed

Lines changed: 126 additions & 0 deletions

File tree

internal/strategy/bootstrap/bootstrap.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ type plannedBatch struct {
7979
planner.BootstrapBatch
8080

8181
chain []plumbing.Hash // full first-parent chain (root→tip) for subdividing on push failure
82+
// subsumed is true when the branch tip is already reachable from the
83+
// trunk (planned first), so every object is already on the target after
84+
// trunk's batches. Execution skips the commit-graph fetch, the pack
85+
// fetch, the temp ref, and the pack push — emitting only a single ref
86+
// create command.
87+
subsumed bool
8288
}
8389

8490
// Execute runs the bootstrap strategy (one-shot or batched).
@@ -219,6 +225,22 @@ func executeBatched( //nolint:maintidx // complex batch logic is inherently bran
219225
completedRefs := planner.CopyRefHashMap(p.TargetRefs)
220226

221227
for _, batch := range batches {
228+
if batch.subsumed {
229+
cmds := []gitproto.PushCommand{{
230+
Name: batch.Plan.TargetRef,
231+
Old: plumbing.ZeroHash,
232+
New: batch.Plan.SourceHash,
233+
}}
234+
if err := p.TargetPusher.PushCommands(ctx, cmds); err != nil {
235+
return result, fmt.Errorf("create subsumed branch ref for %s: %w", batch.Plan.TargetRef, err)
236+
}
237+
completedRefs[batch.Plan.TargetRef] = batch.Plan.SourceHash
238+
result.BatchCount++
239+
p.log("bootstrap batch subsumed branch finalized",
240+
"branch", batch.Plan.TargetRef.String(),
241+
"source_hash", planner.ShortHash(batch.Plan.SourceHash))
242+
continue
243+
}
222244
result.PlannedBatchCount += len(batch.Checkpoints)
223245
result.TempRefs = append(result.TempRefs, batch.TempRef.String())
224246
p.log("bootstrap batch branch plan",
@@ -429,6 +451,28 @@ func planBatches(ctx context.Context, p Params, desired []planner.DesiredRef) ([
429451
)
430452

431453
for i, ref := range ordered {
454+
// Branches whose tip is already reachable from trunk's ancestry need
455+
// no pack transfer — trunk's batches already delivered every object.
456+
// Emit a subsumed batch that the executor handles with a single ref
457+
// create command.
458+
if i != trunkIdx && trunkStopSet != nil {
459+
if _, subsumed := trunkStopSet[ref.SourceHash]; subsumed {
460+
p.log("bootstrap batch branch subsumed by trunk",
461+
"branch", ref.TargetRef.String(),
462+
"source_hash", planner.ShortHash(ref.SourceHash))
463+
out = append(out, plannedBatch{
464+
BootstrapBatch: planner.BootstrapBatch{
465+
Plan: planner.BranchPlan{
466+
Branch: ref.Label, SourceRef: ref.SourceRef,
467+
TargetRef: ref.TargetRef, SourceHash: ref.SourceHash,
468+
Kind: ref.Kind, Action: planner.ActionCreate,
469+
},
470+
},
471+
subsumed: true,
472+
})
473+
continue
474+
}
475+
}
432476
var (
433477
haves []plumbing.Hash
434478
stopAt map[plumbing.Hash]struct{}

internal/strategy/bootstrap/bootstrap_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,88 @@ func TestOrderTrunkFirstHEADNotInDesired(t *testing.T) {
451451
}
452452
}
453453

454+
func TestExecuteBatchedSubsumedBranchSkipsPack(t *testing.T) {
455+
mainRef := plumbing.NewBranchReferenceName("main")
456+
featureRef := plumbing.NewBranchReferenceName("feature")
457+
// Linear chain: hashes[0] -> hashes[1] -> hashes[2]. main tip = hashes[2],
458+
// feature tip = hashes[0]. feature is entirely within main's ancestry, so
459+
// trunk-first planning should mark it subsumed and emit zero pack pushes
460+
// for it.
461+
hashes := makeLinearCommitChain(t, 3)
462+
mainHash := hashes[2]
463+
featureHash := hashes[0]
464+
465+
var (
466+
graphFetches int
467+
packFetches int
468+
pushPackCalls int
469+
pushCommandsBatches [][]gitproto.PushCommand
470+
)
471+
472+
_, err := Execute(context.Background(), Params{
473+
SourceService: fakeBootstrapSource{
474+
fetchCommitGraph: func(_ context.Context, store storer.Storer, _ *gitproto.Conn, ref gitproto.DesiredRef, _ []plumbing.Hash) error {
475+
graphFetches++
476+
if ref.SourceRef != mainRef {
477+
t.Errorf("unexpected commit-graph fetch for %s; subsumed branch should have been skipped", ref.SourceRef)
478+
}
479+
writeLinearCommitChain(t, store, 3)
480+
return nil
481+
},
482+
fetchPack: func(_ context.Context, _ *gitproto.Conn, desired map[plumbing.ReferenceName]gitproto.DesiredRef, _ map[plumbing.ReferenceName]plumbing.Hash) (io.ReadCloser, error) {
483+
packFetches++
484+
if _, ok := desired[featureRef]; ok {
485+
t.Errorf("unexpected pack fetch including feature ref: %+v", desired)
486+
}
487+
return io.NopCloser(bytes.NewReader([]byte("PACK"))), nil
488+
},
489+
},
490+
TargetPusher: fakeBootstrapPusher{
491+
pushPack: func(_ context.Context, _ []gitproto.PushCommand, pack io.ReadCloser) error {
492+
pushPackCalls++
493+
_ = pack.Close()
494+
return nil
495+
},
496+
pushCommands: func(_ context.Context, cmds []gitproto.PushCommand) error {
497+
pushCommandsBatches = append(pushCommandsBatches, append([]gitproto.PushCommand(nil), cmds...))
498+
return nil
499+
},
500+
},
501+
DesiredRefs: map[plumbing.ReferenceName]planner.DesiredRef{
502+
mainRef: {SourceRef: mainRef, TargetRef: mainRef, SourceHash: mainHash, Kind: planner.RefKindBranch, Label: "main"},
503+
featureRef: {SourceRef: featureRef, TargetRef: featureRef, SourceHash: featureHash, Kind: planner.RefKindBranch, Label: "feature"},
504+
},
505+
TargetRefs: map[plumbing.ReferenceName]plumbing.Hash{},
506+
SourceHeadTarget: mainRef,
507+
TargetMaxPack: 1024 * 1024,
508+
}, "empty target")
509+
if err != nil {
510+
t.Fatalf("Execute: %v", err)
511+
}
512+
513+
if graphFetches != 1 {
514+
t.Errorf("fetchCommitGraph called %d times, want 1 (trunk only)", graphFetches)
515+
}
516+
if packFetches != 1 {
517+
t.Errorf("fetchPack called %d times, want 1 (trunk only)", packFetches)
518+
}
519+
if pushPackCalls != 1 {
520+
t.Errorf("PushPack called %d times, want 1 (trunk only)", pushPackCalls)
521+
}
522+
523+
var foundFeatureCreate bool
524+
for _, cmds := range pushCommandsBatches {
525+
for _, cmd := range cmds {
526+
if cmd.Name == featureRef && cmd.New == featureHash && cmd.Old == plumbing.ZeroHash && !cmd.Delete {
527+
foundFeatureCreate = true
528+
}
529+
}
530+
}
531+
if !foundFeatureCreate {
532+
t.Fatalf("expected ref-create command for feature at %s; got %v", featureHash, pushCommandsBatches)
533+
}
534+
}
535+
454536
type fakeBootstrapSource struct {
455537
fetchPack func(context.Context, *gitproto.Conn, map[plumbing.ReferenceName]gitproto.DesiredRef, map[plumbing.ReferenceName]plumbing.Hash) (io.ReadCloser, error)
456538
fetchCommitGraph func(context.Context, storer.Storer, *gitproto.Conn, gitproto.DesiredRef, []plumbing.Hash) error

0 commit comments

Comments
 (0)