Add narrow incremental relay path · Entire
Add narrow incremental relay path
520e91f→main·
Soph·3mo ago·4 files·+78 added/-10 removed
Sessions
894a895034bbView transcript
[?
Can you take a look at the go code (wasm) in /Users/soph/Work/entire/devenv/entire-io-worktree1 based a bit on that I wonder if something like this can be build:Codex·GPT-5.4·1 step](/content/gh/entireio/git-sync/session/019d6d29-8cf7-7fe3-adc9-8c3e4d9d5603#timeline-894a895034bb/index.html)
Changes
4
MREADME.md+2
docs
- Mbootstrap.md+6
internal/syncer
- Mgit_http_backend_test.go+2/-2
- Msyncer.go+68/-8
90 unmodified lines
91
92
93
94
95
96
97
98
90 unmodified lines
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` also uses a narrow incremental relay path for a single fast-forward branch update when the target already has the old tip and the target does not advertise `no-thin`. More complex updates still fall back to the normal local decode-and-repack path.
Sync specific branches:
```bash
MREADME.md+2
159 unmodified lines
160
161
162
163
164
165
166
167
168
159 unmodified lines
- consider a more advanced incremental relay mode for non-empty targets
- only pursue this if large migration workflows become important enough to justify the added protocol complexity
Progress:
- there is now a narrow incremental relay path in `sync`
- it is limited to a single fast-forward branch update
- tags, deletes, force, prune, mapped refs, and multi-ref updates still use the normal path
Mdocs/bootstrap.md+6
79 unmodified lines
80
81
82
83
84
83
84
85
86
87
79 unmodified lines
if result.Pushed != 1 || result.Blocked != 0 {
t.Fatalf("unexpected incremental result: %+v", result)
}
if result.Relay || result.RelayMode != "" {
t.Fatalf("expected incremental sync to stay on normal path for now, got %+v", result)
}
if !result.Relay || result.RelayMode != "incremental" {
t.Fatalf("expected incremental sync to use incremental relay, got %+v", result)
}
assertGitRefEqual(t, sourceBare, targetBare, plumbing.NewBranchReferenceName(testBranch))
Minternal/syncer/git_http_backend_test.go+2/-2
472 unmodified lines
473
474
475
476
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
264 unmodified lines
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
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
476 unmodified lines
1283
1284
1285
1229
1230
1231
1232
1286
1287
1234
1235
1236
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
472 unmodified lines
return result, fmt.Errorf("blocked %d ref update(s); rerun with --force where appropriate", result.Blocked)
}
if !cfg.DryRun && len(pushPlans) > 0 {
if !cfg.DryRun && canIncrementalRelay(cfg, pushPlans, targetAdv) {
relayPlans := append([]BranchPlan(nil), pushPlans...)
desiredRelay := desiredSubsetForPlans(desiredRefs, relayPlans)
packReader, err := sourceService.FetchPack(ctx, sourceConn, desiredRelay, targetRefMap)
if err != nil {
return result, fmt.Errorf("fetch source pack: %w", err)
}
defer packReader.Close()
packReader = limitPackReadCloser(packReader, cfg.MaxPackBytes)
if err := pushPackToTarget(ctx, targetConn, targetAdv, relayPlans, packReader, cfg.Verbose); err != nil {
return result, fmt.Errorf("push target refs: %w", err)
}
result.Relay = true
result.RelayMode = "incremental"
} else if !cfg.DryRun && len(pushPlans) > 0 {
if err := pushToTarget(ctx, repo, targetConn, targetAdv, pushPlans, targetRefMap, cfg.Verbose); err != nil {
return result, fmt.Errorf("push target refs: %w", err)
}
}
264 unmodified lines
return true
}
func canIncrementalRelay(cfg Config, plans []BranchPlan, targetAdv *packp.AdvRefs) bool {
if cfg.Force || cfg.Prune || cfg.DryRun || cfg.IncludeTags {
return false
}
if len(cfg.Mappings) > 0 {
return false
}
if len(plans) != 1 {
return false
}
if targetAdv == nil || targetAdv.Capabilities == nil {
return false
}
if targetAdv.Capabilities.Supports(capability.Capability("no-thin")) {
return false
}
plan := plans[0]
if plan.Kind != RefKindBranch {
return false
}
if plan.Action != ActionUpdate {
return false
}
if plan.TargetHash.IsZero() {
return false
}
return true
}
func desiredSubsetForPlans(
desired map[plumbing.ReferenceName]desiredRef,
plans []BranchPlan,
) map[plumbing.ReferenceName]desiredRef {
out := make(map[plumbing.ReferenceName]desiredRef, len(plans))
for _, plan := range plans {
if ref, ok := desired[plan.TargetRef]; ok {
out[plan.TargetRef] = ref
}
}
return out
}
func bootstrapWithInputs(
ctx context.Context,
cfg Config,
476 unmodified lines
commands := make([]*packp.Command, 0, len(plans))
for _, plan := range plans {
if plan.Action != ActionCreate {
return fmt.Errorf("bootstrap only supports create actions")
}
commands = append(commands, &packp.Command{
cmd := &packp.Command{
Name: plan.TargetRef,
Old: plumbing.ZeroHash,
New: plan.SourceHash,
}
Old: plan.TargetHash,
}
switch plan.Action {
case ActionCreate, ActionUpdate:
cmd.New = plan.SourceHash
default:
return fmt.Errorf("streamed pack push only supports create and update actions")
}
commands = append(commands, cmd)
}
req.Commands = commands
conn.stats.addCommands("target receive-pack", len(commands))