Expand incremental relay to mapped branches · Entire

Expand incremental relay to mapped branches

08ac0e3→main·

Soph·3mo ago·4 files·+86 added/-9 removed

Sessions

78bcff081d69View 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-78bcff081d69/index.html)

Changes

4

90 unmodified lines

91
92
93
94
94
95
96
97

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 fast-forward branch updates when all planned updates are branch-only, there are no mappings, no tags, no prune/delete, no force, and the target does not advertise `no-thin`. More complex updates still fall back to the normal local decode-and-repack path.
`sync` also uses a narrow incremental relay path for fast-forward branch updates when all planned updates are branch-only, there are no tags, no prune/delete, no force, and the target does not advertise `no-thin`. This now includes multi-branch batches and branch-to-branch mappings. More complex updates still fall back to the normal local decode-and-repack path.

Sync specific branches:

MREADME.md+1/-1

164 unmodified lines

165
166
167
168
168
169

164 unmodified lines

- there is now a narrow incremental relay path in `sync`
- it now covers multi-branch fast-forward branch-only updates
- tags, deletes, force, prune, and mapped refs still use the normal path
- it now also covers branch-to-branch mappings
- tags, deletes, force, and prune still use the normal path

Mdocs/bootstrap.md+2/-1

219 unmodified lines

220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
101 unmodified lines

398
399
400
330
401
402
332
333
403
404
405
406
407
408
409
410
335
411
412
413

219 unmodified lines

assertGitRefEqual(t, sourceBare, targetBare, plumbing.NewBranchReferenceName("release"))
}

func TestRun_GitHTTPBackendSyncMappedBranchFastForward(t *testing.T) {
    if os.Getenv(gitHTTPBackendEnv) == "" {
        t.Skip("set GITSYNC_E2E_GIT_HTTP_BACKEND=1 to run git-http-backend integration test")
    }

if _, err := exec.LookPath("git"); err != nil {
        t.Skipf("git not available: %v", err)
    }

root := t.TempDir()
    sourceBare := filepath.Join(root, "source.git")
    targetBare := filepath.Join(root, "target.git")
    worktree := filepath.Join(root, "work")

runGit(t, root, "init", "--bare", sourceBare)
    runGit(t, root, "init", "--bare", targetBare)
    runGit(t, targetBare, "config", "http.receivepack", "true")
    runGit(t, root, "init", "-b", testBranch, worktree)
    runGit(t, worktree, "config", "user.name", "git-sync test")
    runGit(t, worktree, "config", "user.email", "git-sync@example.com")

writeFile(t, filepath.Join(worktree, "README.md"), "mapped\n")
    runGit(t, worktree, "add", "README.md")
    runGit(t, worktree, "commit", "-m", "initial")
    runGit(t, worktree, "remote", "add", "origin", sourceBare)
    runGit(t, worktree, "push", "origin", "HEAD:refs/heads/"+testBranch)

server := newGitHTTPBackendServer(t, root)
    defer server.Close()

sourceURL := server.RepoURL("source.git")
    targetURL := server.RepoURL("target.git")

result, err := Run(context.Background(), Config{
        Source:   Endpoint{URL: sourceURL},
        Target:   Endpoint{URL: targetURL},
        Mappings: []RefMapping{{Source: testBranch, Target: "stable"}},
    })
    if err != nil {
        t.Fatalf("initial mapped sync failed: %v", err)
    }
    if result.Pushed != 1 || result.Blocked != 0 {
        t.Fatalf("unexpected initial mapped result: %+v", result)
    }
    if !result.Relay || result.RelayMode != "bootstrap" {
        t.Fatalf("expected initial mapped sync to use bootstrap relay, got %+v", result)
    }

writeFile(t, filepath.Join(worktree, "README.md"), "mapped\nupdate\n")
    runGit(t, worktree, "add", "README.md")
    runGit(t, worktree, "commit", "-m", "mapped update")
    runGit(t, worktree, "push", "origin", "HEAD:refs/heads/"+testBranch)

result, err = Run(context.Background(), Config{
        Source:   Endpoint{URL: sourceURL},
        Target:   Endpoint{URL: targetURL},
        Mappings: []RefMapping{{Source: testBranch, Target: "stable"}},
    })
    if err != nil {
        t.Fatalf("mapped incremental sync failed: %v", err)
    }
    if result.Pushed != 1 || result.Blocked != 0 {
        t.Fatalf("unexpected mapped incremental result: %+v", result)
    }
    if !result.Relay || result.RelayMode != "incremental" {
        t.Fatalf("expected mapped incremental sync to use incremental relay, got %+v", result)
    }

assertGitRefEqual(t, sourceBare, targetBare, plumbing.NewBranchReferenceName(testBranch), plumbing.NewBranchReferenceName("stable"))
}

func TestBootstrap_GitHTTPBackendSync(t *testing.T) {
    if os.Getenv(gitHTTPBackendEnv) == "" {
        t.Skip("set GITSYNC_E2E_GIT_HTTP_BACKEND=1 to run git-http-backend integration test")
    }
}

func assertGitRefEqual(t *testing.T, sourceRepoPath, targetRepoPath string, ref plumbing.ReferenceName) {
func assertGitRefEqual(t *testing.T, sourceRepoPath, targetRepoPath string, refs ...plumbing.ReferenceName) {
    t.Helper()
    sourceHash := strings.TrimSpace(runGit(t, sourceRepoPath, "rev-parse", ref.String()))
    targetHash := strings.TrimSpace(runGit(t, targetRepoPath, "rev-parse", ref.String()))
    sourceRef := refs[0]
    targetRef := sourceRef
    if len(refs) > 1 {
        targetRef = refs[1]
    }
    sourceHash := strings.TrimSpace(runGit(t, sourceRepoPath, "rev-parse", sourceRef.String()))
    targetHash := strings.TrimSpace(runGit(t, targetRepoPath, "rev-parse", targetRef.String()))
    if sourceHash != targetHash {
        t.Fatalf("ref mismatch for %s: source=%s target=%s", ref, sourceHash, targetHash)
        t.Fatalf("ref mismatch for %s -> %s: source=%s target=%s", sourceRef, targetRef, sourceHash, targetHash)
    }
}