Surface source HEAD symref in Result and ProbeResult · Entire

Surface source HEAD symref in Result and ProbeResult

8ce7a4a→main·

Soph·2mo ago·3 files·+108 added/-29 removed

The session already captures source HEAD's symref target during ref discovery (it's used by bootstrap-batch trunk ordering), but never escapes to callers. Expose it on syncer.Result.SourceHEAD, syncer.ProbeResult.SourceHEAD, and the matching internalbridge fields, so library callers and the CLI can compare against the target's intended default branch without out-of-band metadata.

Human Lines() output picks up a "source-head: " line; JSON output gets execution.sourceHead for sync paths and sourceHead on probe. Field is omitempty so detached-HEAD sources omit it.

Refs #45.

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

Sessions

1d796b864aa7View transcript

Changes

3

2780 unmodified lines
}

// Sync surfaces the source's symref HEAD target on Result, parsed from the // upload-pack advertisement we already make. Used by callers to know the // source's default branch without out-of-band metadata. func TestRun_IntegrationSyncSurfacesSourceHEAD(t *testing.T) { sourceRepo, sourceFS := newSourceRepo(t) makeCommits(t, sourceRepo, sourceFS, 1)

targetRepo, err := git.Init(memory.NewStorage()) if err != nil { t.Fatalf("init target repo: %v", err) }

sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo) targetServer := newSmartHTTPRepoServer(t, targetRepo) defer sourceServer.Close() defer targetServer.Close()

result, err := Run(context.Background(), Config{ Source: Endpoint{URL: sourceServer.RepoURL()}, Target: Endpoint{URL: targetServer.RepoURL()}, ProtocolMode: protocolModeAuto, }) if err != nil { t.Fatalf("sync: %v", err) } if got, want := result.SourceHEAD, plumbing.NewBranchReferenceName(testBranch); got != want { t.Errorf("SourceHEAD = %q, want %q", got, want) } }

// Probe surfaces the source's symref HEAD target without performing a sync. func TestProbe_IntegrationSurfacesSourceHEAD(t *testing.T) { sourceRepo, sourceFS := newSourceRepo(t) makeCommits(t, sourceRepo, sourceFS, 1)

sourceServer := newSmartHTTPRepoServerV2(t, sourceRepo) defer sourceServer.Close()

result, err := Probe(context.Background(), Config{ Source: Endpoint{URL: sourceServer.RepoURL()}, ProtocolMode: protocolModeAuto, }) if err != nil { t.Fatalf("probe: %v", err) } if got, want := result.SourceHEAD, plumbing.NewBranchReferenceName(testBranch); got != want { t.Errorf("SourceHEAD = %q, want %q", got, want) } }

func TestRun_IntegrationAllRefsBootstrapsCustomNamespace(t *testing.T) { sourceRepo, sourceFS := newSourceRepo(t) makeCommits(t, sourceRepo, sourceFS, 2) }

// Result holds the outcome of a sync or bootstrap operation. // SourceHEAD carries the source's symref HEAD target when advertised; lets // callers compare against the target's intended default branch. type Result struct { Plans []BranchPlan json:"plans" Pushed int json:"pushed" Skipped int json:"skipped" Blocked int json:"blocked" Deleted int json:"deleted" Warned int json:"warned" DryRun bool json:"dryRun" OperationMode string json:"operationMode" Relay bool json:"relay" RelayMode string json:"relayMode" RelayReason string json:"relayReason" Batching bool json:"batching" BatchCount int json:"batchCount" PlannedBatchCount int json:"plannedBatchCount" TempRefs []string json:"tempRefs" BootstrapSuggested bool json:"bootstrapSuggested" Stats Stats json:"stats" Measurement Measurement json:"measurement" Protocol string json:"protocol" SourceHEAD plumbing.ReferenceName json:"sourceHead,omitempty" }

func (r Result) Lines() []string { if r.Batching && len(r.TempRefs) > 0 { lines = append(lines, "batching: temp-refs="+strings.Join(r.TempRefs, ",")) } if r.SourceHEAD != "" { lines = append(lines, "source-head: "+r.SourceHEAD.String()) } return lines }

// ProbeResult holds the outcome of a probe operation. type ProbeResult struct { SourceURL string json:"sourceUrl" TargetURL string json:"targetUrl,omitempty" RequestedMode string json:"requestedMode" Protocol string json:"protocol" RefPrefixes []string json:"refPrefixes" Capabilities []string json:"sourceCapabilities" Refs []RefInfo json:"refs" Stats Stats json:"stats" Measurement Measurement json:"measurement" SourceHEAD plumbing.ReferenceName json:"sourceHead,omitempty" }

func (r ProbeResult) Lines() []string { if len(r.RefPrefixes) > 0 { lines = append(lines, "ref-prefixes: "+strings.Join(r.RefPrefixes, ", ")) } if r.SourceHEAD != "" { lines = append(lines, "source-head: "+r.SourceHEAD.String()) } if len(r.Capabilities) > 0 { lines = append(lines, "source-capabilities: "+strings.Join(r.Capabilities, ", ")) } }