bootstrap: push source HEAD's branch first · Entire
bootstrap: push source HEAD's branch first
e5dd3ba→main·
Soph·2mo ago·3 files·+133 added/-0 removed
Hosts that pick the default branch from the first push on a fresh repo (GitHub, GitLab among them) end up with whatever branch sorted first alphabetically in our bootstrap push order — often wrong. Reorder push commands in bootstrap.Execute so the source HEAD's branch (e.g. refs/heads/main) is sent first, regardless of alphabetical order with other branches in scope.
hoistSourceHeadCommand is a small helper that moves the matching command to position 0; no-op when source HEAD is empty (detached source), not in cmds (filtered out by --branch / --map), or already first.
Batched bootstrap already orders trunk-first via orderTrunkFirst in planBatches, so the batched path inherits this for free — the first checkpointed branch batch carries source-HEAD's branch.
Integration test asserts a sync with both refs/heads/alpha (sorts first alphabetically) and refs/heads/master (the HEAD target) pushes master as the first ref command. Unit test covers the helper's edge cases (already-first, empty head, missing, single-command).
Addresses #45 for the GitHub/GitLab common case.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Changes
3
internal
strategy/bootstrap
Mbootstrap.go+25
Mbootstrap_test.go+61
syncer
Mintegration_test.go+47
176 unmodified lines
177
178
179
180
181
182
183
13 unmodified lines
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
176 unmodified lines
p.OnPhase("pushing pack")
}
cmds := convert.PlansToPushCommands(plans)
cmds = hoistSourceHeadCommand(cmds, p.SourceHeadTarget)
pushErr := p.TargetPusher.PushPack(ctx, cmds, packReader)
_ = packReader.Close()
if pushErr != nil {
13 unmodified lines
return result, nil
}
// hoistSourceHeadCommand moves the push command whose ref matches the
// source's symref HEAD target to the front of cmds. Hosts that pick the
// default branch from the first push on a fresh repo (GitHub, GitLab) end
// up with the right default. No-op when sourceHEAD is empty or not in cmds.
func hoistSourceHeadCommand(cmds []gitproto.PushCommand, sourceHEAD plumbing.ReferenceName) []gitproto.PushCommand {
if sourceHEAD == "" || len(cmds) < 2 {
return cmds
}
for i, cmd := range cmds {
if cmd.Name != sourceHEAD {
continue
}
if i == 0 {
return cmds
}
out := make([]gitproto.PushCommand, 0, len(cmds))
out = append(out, cmd)
out = append(out, cmds[:i]...)
out = append(out, cmds[i+1:]...)
return out
}
return cmds
}
func adjustedBootstrapTargetRefs(
desiredRefs map[plumbing.ReferenceName]planner.DesiredRef,
targetRefs map[plumbing.ReferenceName]plumbing.Hash,
Minternal/strategy/bootstrap/bootstrap.go+25
21 unmodified lines
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
21 unmodified lines
"entire.io/entire/git-sync/internal/planner"
)
func TestHoistSourceHeadCommand(t *testing.T) {
main := plumbing.NewBranchReferenceName("main")
master := plumbing.NewBranchReferenceName("master")
alpha := plumbing.NewBranchReferenceName("alpha")
cmd := func(name plumbing.ReferenceName) gitproto.PushCommand {
return gitproto.PushCommand{Name: name}
}
tests := []struct {
name string
cmds []gitproto.PushCommand
head plumbing.ReferenceName
wanted []plumbing.ReferenceName
}{
{
name: "hoists matching command to front",
cmds: []gitproto.PushCommand{cmd(alpha), cmd(main), cmd(master)},
head: main,
wanted: []plumbing.ReferenceName{main, alpha, master},
},
{
name: "already first stays put",
cmds: []gitproto.PushCommand{cmd(main), cmd(alpha)},
head: main,
wanted: []plumbing.ReferenceName{main, alpha},
},
{
name: "empty source HEAD is a no-op",
cmds: []gitproto.PushCommand{cmd(alpha), cmd(main)},
head: "",
wanted: []plumbing.ReferenceName{alpha, main},
},
{
name: "no matching command is a no-op",
cmds: []gitproto.PushCommand{cmd(alpha), cmd(master)},
head: main,
wanted: []plumbing.ReferenceName{alpha, master},
},
{
name: "single command",
cmds: []gitproto.PushCommand{cmd(main)},
head: main,
wanted: []plumbing.ReferenceName{main},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := hoistSourceHeadCommand(tt.cmds, tt.head)
if len(got) != len(tt.wanted) {
t.Fatalf("length mismatch: got %d, want %d", len(got), len(tt.wanted))
}
for i, want := range tt.wanted {
if got[i].Name != want {
t.Errorf("position %d: got %q, want %q", i, got[i].Name, want)
}
}
})
}
}
func TestIsTargetBodyLimitError(t *testing.T) {
tests := []struct {
name string
Minternal/strategy/bootstrap/bootstrap_test.go+61
2810 unmodified lines
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2810 unmodified lines
}
// Bootstrap pushes the source HEAD's branch as the first ref command, so
// hosts that pick the default branch from the first push on a fresh repo
// (GitHub, GitLab) end up with the right default. Source has an alpha
// branch that sorts before master alphabetically; without the ordering
// fix the bootstrap pushes alpha first and master second.
func TestRun_IntegrationBootstrapPushesSourceHeadBranchFirst(t *testing.T) {
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 1)
head, err := sourceRepo.Reference(plumbing.NewBranchReferenceName(testBranch), true)
if err != nil {
t.Fatalf("resolve source head: %v", err)
}
if err := sourceRepo.Storer.SetReference(plumbing.NewHashReference(plumbing.NewBranchReferenceName("alpha"), head.Hash())); err != nil {
t.Fatalf("set alpha branch: %v", err)
}
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()
var firstCommandRef plumbing.ReferenceName
targetServer.receivePackHook = func(req *packp.UpdateRequests, _ bool) *packp.ReportStatus {
if firstCommandRef == "" && len(req.Commands) > 0 {
firstCommandRef = req.Commands[0].Name
}
return nil // delegate to default handler
}
if _, err := Run(context.Background(), Config{
Source: Endpoint{URL: sourceServer.RepoURL()},
Target: Endpoint{URL: targetServer.RepoURL()},
ProtocolMode: protocolModeAuto,
}); err != nil {
t.Fatalf("sync: %v", err)
}
want := plumbing.NewBranchReferenceName(testBranch)
if firstCommandRef != want {
t.Errorf("first receive-pack command = %q, want %q (source HEAD's branch should be pushed first)", firstCommandRef, want)
}
}
// Probe surfaces the source's symref HEAD target without performing a sync.
func TestProbe_IntegrationSurfacesSourceHEAD(t *testing.T) {
sourceRepo, sourceFS := newSourceRepo(t)