Plan trunk first in batched bootstrap to cut per-branch graph fetches · Entire

Plan trunk first in batched bootstrap to cut per-branch graph fetches

d851ee0→main· Soph·2mo ago·9 files·+393 added/-52 removed

Before: each branch's checkpoint planning fetched the full commit graph from the source independently. For a repo with 152 feature branches that all descend from main, that meant ~152 full-history commit-graph fetches and ~152 independent first-parent walks, each traversing the same shared history. On a linux-sized source this was ~40 s of wall time during planning alone, before any object bytes moved.

Now the trunk is identified up front via the source's HEAD symref, planned first, and its commit reachability is captured as a hash set. Each subsequent branch's graph fetch passes trunk's tip as a have so the source returns only the divergence, and the first-parent walk terminates when it enters trunk's ancestry instead of walking back to root.

Pieces:

Empty-chain edge case: when stop-at subsumes the branch tip, FirstParentChainStoppingAt returns nil and planCheckpointsFromChain falls back to [tip] so the existing execute-phase logic still creates the target ref.

Falls back to the original per-branch behavior when HeadTarget is empty or when the trunk ref is filtered out of the desired set, so sources without symref advertisement and scoped runs (--branch, --map) are not regressed.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

Changes

9

// FetchCommitGraph fetches only the commit graph (tree:0 filter) for a ref.
// Requires v2 with filter support.
// Requires v2 with filter support. Optional haves let the source skip commits
// already reachable from those hashes, which is valuable when planning later
// branches that share history with an already-planned trunk.
func (s *RefService) FetchCommitGraph(
    ctx context.Context,
    store storer.Storer,
    conn *Conn,
    ref DesiredRef,
    haves []plumbing.Hash,
) error {
    if s.Protocol != "v2" {
        return errors.New("commit graph fetch requires protocol v2")
    }

cmdArgs := []string{
        sortedHaves := SortedUniqueHashes(haves)
        cmdArgs := make([]string, 0, 4+len(sortedHaves))
        cmdArgs = append(cmdArgs,
            "ofs-delta",
            "no-progress",
            "filter tree:0",
            "want " + ref.SourceHash.String(),
            "done",
            "want "+ref.SourceHash.String(),
        )
        for _, h := range sortedHaves {
            cmdArgs = append(cmdArgs, "have "+h.String())
        }
        cmdArgs = append(cmdArgs, "done")

body, err := EncodeCommand("fetch", s.V2Caps.RequestCapabilities(), cmdArgs)
        if err != nil {
            return err
        }
    }
}

Test Cases