Auto-use bootstrap on empty targets · Entire
Auto-use bootstrap on empty targets
50f10f8→main·
Soph·3mo ago·5 files·+133 added/-32 removed
Sessions
8620d1269809View transcript
Changes
5
MREADME.md+2
cmd/git-sync
- Mmain_test.go+3
docs
Mbootstrap.md+5
internal/syncer
Mintegration_test.go+34
- Msyncer.go+89/-32
88 unmodified lines
89
90
91
92
93
94
95
96
88 unmodified lines
That is the intended way to compare the bootstrap relay path against the normal sync path on the same fixture or test repo.
When `sync` sees that all managed target refs are absent and the run is compatible with bootstrap semantics, it automatically uses the bootstrap relay path instead of the normal decode-and-repack sync path.
Sync specific branches:
```bash
117 unmodified lines
118
119
120
121
122
123
124
125
126
117 unmodified lines
if result["dry_run"] != true {
t.Fatalf("expected dry_run=true, got %#v", result["dry_run"])
}
if result["bootstrap_suggested"] != true {
t.Fatalf("expected bootstrap_suggested=true, got %#v", result["bootstrap_suggested"])
}
plans, ok := result["plans"].([]any)
if !ok || len(plans) == 0 {
t.Fatalf("expected plan entries, got %#v", result["plans"])
}
Mcmd/git-sync/main_test.go+3
150 unmodified lines
151
152
153
154
155
156
157
158
159
160
161
150 unmodified lines
- investigate whether target capability combinations require alternate pack handling
- measure source-to-target pack relay memory and CPU against current `sync`
Progress:
- `sync` now auto-selects the bootstrap relay path when all managed target refs are absent and the run matches bootstrap semantics
- dry-run `plan` surfaces a bootstrap suggestion for the same target shape
Phase 4:
- consider a more advanced incremental relay mode for non-empty targets
Mdocs/bootstrap.md+5
55 unmodified lines
56
57
58
59
60
61
62
63
64
11 unmodified lines
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
55 unmodified lines
if result.Pushed != 1 || result.Blocked != 0 {
t.Fatalf("unexpected result: %+v", result)
}
if !result.Relay {
t.Fatalf("expected sync to auto-switch to relay bootstrap on empty target")
}
assertHeadsMatch(t, sourceRepo, targetRepo, testBranch)
11 unmodified lines
}
}
func TestRun_IntegrationPlanSuggestsBootstrapOnEmptyTarget(t *testing.T) {
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 2)
targetRepo, err := git.Init(memory.NewStorage(), nil)
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()},
DryRun: true,
ProtocolMode: protocolModeAuto,
})
if err != nil {
t.Fatalf("plan failed: %v", err)
}
if !result.DryRun || !result.BootstrapSuggested {
t.Fatalf("expected bootstrap suggestion, got %+v", result)
}
if result.Relay {
t.Fatalf("dry-run plan should not execute relay")
}
}
func TestBootstrap_IntegrationInitialSyncToEmptyTarget(t *testing.T) {
sourceRepo, sourceFS := newSourceRepo(t)
makeCommits(t, sourceRepo, sourceFS, 4)
Minternal/syncer/integration_test.go+34
99 unmodified lines
100
101
102
103
104
105
106
107
128 unmodified lines
236
237
238
237
238
239
240
241
242
243
20 unmodified lines
264
265
266
267
268
269
270
271
272
132 unmodified lines
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
9 unmodified lines
438
439
440
441
442
443
444
96 unmodified lines
541
542
543
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
544
545
550
546
547
548
549
172 unmodified lines
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
99 unmodified lines
Blocked int `json:"blocked"`
Deleted int `json:"deleted"`
DryRun bool `json:"dry_run"`
Relay bool `json:"relay"`
BootstrapSuggested bool `json:"bootstrap_suggested"`
Stats Stats `json:"stats"`
Measurement Measurement `json:"measurement"`
Protocol string `json:"protocol"`
128 unmodified lines
}
summary := fmt.Sprintf(
"summary: pushed=%d deleted=%d skipped=%d blocked=%d protocol=%s",
r.Pushed, r.Deleted, r.Skipped, r.Blocked, r.Protocol,
"summary: pushed=%d deleted=%d skipped=%d blocked=%d protocol=%s relay=%t",
r.Pushed, r.Deleted, r.Skipped, r.Blocked, r.Protocol, r.Relay,
)
if r.DryRun {
summary += " dry-run=true"
}
return lines
}
func canBootstrapRelay(
cfg Config,
desired map[plumbing.ReferenceName]desiredRef,
targetRefs map[plumbing.ReferenceName]plumbing.Hash,
) bool {
if cfg.Force || cfg.Prune {
return false
}
if len(desired) == 0 {
return false
}
for targetRef := range desired {
if !targetRefs[targetRef].IsZero() {
return false
}
}
return true
}
func bootstrapWithInputs(
ctx context.Context,
cfg Config,
stats *statsCollector,
sourceConn *transportConn,
targetConn *transportConn,
sourceService *sourceRefService,
targetAdv *packp.AdvRefs,
desiredRefs map[plumbing.ReferenceName]desiredRef,
targetRefs map[plumbing.ReferenceName]plumbing.Hash,
) (Result, error) {
plans, err := buildBootstrapPlans(desiredRefs, targetRefs)
if err != nil {
return Result{}, err
}
result := Result{
Plans: plans,
Relay: true,
Stats: stats.snapshot(),
Protocol: sourceService.protocol,
}
packReader, err := sourceService.FetchPack(ctx, sourceConn, desiredRefs, nil)
if err != nil {
if errors.Is(err, git.NoErrAlreadyUpToDate) {
return result, nil
}
return result, fmt.Errorf("fetch source pack: %w", err)
}
defer packReader.Close()
packReader = limitPackReadCloser(packReader, cfg.MaxPackBytes)
if err := pushPackToTarget(ctx, targetConn, targetAdv, plans, packReader, cfg.Verbose); err != nil {
return result, fmt.Errorf("push target refs: %w", err)
}
result.Pushed = len(plans)
result.Stats = stats.snapshot()
return result, nil
}
func selectBranches(source map[string]plumbing.Hash, requested []string) map[string]plumbing.Hash {
if len(requested) == 0 {
return source
}