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: · Entire

Home

Log in

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:

a "git sync" command that you call with a source repo and a target repo and it basically does a git fetch but from source (remote) to target (remote) by only using git primitives without a local repository and using low level git protocol calls ideally also just streaming blobs from a to b. so you can basically quickly mirror a repo from a to b and rerun it to keep it in sync

3mo ago·6.4s·15 calls

I’m going to inspect the wasm Go code in entire-io-worktree1 first to see what transport/protocol primitives already exist, then I’ll map that against what a remote-to-remote git sync would need.

The repo only has one Go/WASM entrypoint, so I’m narrowing to that path and the worker code that calls it. I want to see whether it already parses packfiles/commits or if it only hits GitHub’s HTTP APIs.

The Go side is already speaking smart HTTP upload-pack directly and parsing packfiles in memory. Next I’m checking the TypeScript wrapper to see how much of the transport loop is already there and where the gaps are for a remote-to-remote mirror.

The existing code proves the read side: it can build upload-pack requests, fetch packfiles over smart HTTP, and parse them entirely in memory. The missing question is the write side: whether there’s any equivalent receive-pack/push support or pack generation already present.

Yes. The code in api/wasm/git-graph.go and api/src/lib/git-graph.ts already proves the hard half: a repo can be spoken to over smart HTTP directly, with no .git checkout, by building upload-pack requests in WASM, fetching packfiles, and parsing them entirely in memory. It already does:

What it does not have is the push half. I found no receive-pack support, no push/update request builder, and no packfile encoder for writing objects back out. Right now it is a read-side transport/parser, not a mirror engine.

So: conceptually, a git sync <source> <target> built only from git protocol primitives is feasible. But the real design is not “fetch from A and forward bytes to B”; it is:

  1. ls-refs/advertise refs from target
  2. ls-refs/advertise refs from source
  3. Compute which refs should move
  4. Ask source for the object graph needed for those target updates
  5. Materialize those objects in memory
  6. Build a push pack for target
  7. Send receive-pack update commands + packfile to target
  8. Parse target status and retry/idempotently continue later

The main constraint is this: “just stream blobs from A to B” is too optimistic. Git push is object-graph based, not blob-forwarding based. In practice you need commits, trees, tags, and blobs, and the target cares about its own existing haves. The pack you get back from source upload-pack is also not directly the thing you POST to target receive-pack; you usually need to repackage or at least reframe it.

The code you have is a strong base for an MVP because it already has:

What you’d need to add:

My recommendation is an MVP with these limits:

That would already give you a rerunnable remote-to-remote mirror tool with no local repository checkout. If you want, I can sketch the exact protocol flow and a minimal package layout for implementing it in this git-sync repo.

ok, let's build the MVP in this repo

3mo ago·5.5s·42 calls

I’m going to inspect this repo’s current structure and then wire an MVP around the existing read-side protocol approach: source refs, source pack fetch, target receive-pack push, fast-forward branch updates only.

The repo is effectively empty, so I’m setting the MVP shape from scratch rather than integrating into existing code. Next I’m checking local tooling and then I’ll scaffold a Go CLI that can talk smart HTTP directly.

I have Go available and an empty repo. I’m checking whether go-git is already present in the module cache; if it is, I can build the MVP without a dependency download round-trip.

The module cache already has go-git and go-billy, so I can keep moving locally. I’m going to inspect the available push/transport APIs in that cached version before I write the CLI around them.

Plan:

  1. Scaffold a Go CLI module with a sync command and token/header auth support.
  2. Implement remote branch discovery, in-memory fetch from source, and fast-forward checks against target.
  3. Push changed branches to target over smart HTTP using the in-memory object store.
  4. Add basic tests for ref selection/fast-forward logic and write a minimal README with usage and constraints.

I’ve confirmed the simplest MVP path: use go-git’s transport and pack primitives end to end, with a repository backed only by memory.NewStorage(). That still avoids any local checkout, while letting the library handle receive-pack and pack writing correctly.

I’ve got the implementation shape. I’m creating a small Go module now with a real sync command, plus a separate planning layer so the fast-forward rules are testable without network access.

The first pass is in place. I’m compiling now to flush out API mismatches and trim anything I added that the compiler doesn’t actually need.

The code is structurally there; the current failure is just module metadata. I’m generating go.sum next, then I’ll rerun tests and fix any actual compile/runtime issues that surface.

The remaining blocker is dependency resolution and build cache location, not the code itself. go mod tidy needs writable module/build cache state and at least one dependency fetch, so I’m requesting that directly rather than trying to work around it.

Dependency resolution is done. I’m rerunning the test/build loop with cache directories redirected into writable space so I can get a clean compile signal.

I hit the expected second layer now: compile-level cleanup and a test command that should run fine once the code trims unused pieces. I’m fixing those imports and small logic rough edges before rerunning go test.

One test failure exposed a real issue in the fixture, not the planner: my synthetic commits were hashing identically because their contents were identical. I’m making those test commits unique and rerunning the suite.

The planner and unit tests are green now. I’m doing one final build pass and then I’ll summarize what the MVP can and cannot do yet.

Built the MVP in this repo. The CLI lives at cmd/git-sync/main.go, the sync engine is in internal/syncer/syncer.go, and usage/limits are documented in README.md.

The command is:

1

go run ./cmd/git-sync sync <source-url> <target-url>

with --branch, --dry-run, and separate source/target auth flags or env vars. The MVP does branch-only mirroring over smart HTTP, uses an in-memory repo only, creates missing target branches, fast-forwards existing ones, and blocks non-fast-forward/diverged targets.

Verification:

Two important limits:

If you want, next I can add an integration test path and a real dry-run/status output that shows source and target SHAs per branch.

can you explain what it does now

3mo ago·9.1s

Right now it is a small CLI that mirrors branch refs from one remote to another without creating a local checkout.

Flow:

  1. It creates an in-memory Git repository.
  2. It connects to the source remote and lists refs.
  3. It connects to the target remote and lists refs.
  4. It decides which branches to consider.
  5. It fetches the selected source branches into in-memory remote-tracking refs like refs/remotes/source/main.
  6. It fetches the selected target branches into in-memory remote-tracking refs like refs/remotes/target/main.
  7. For each branch, it compares source and target tips:
    • if target branch does not exist: plan create
    • if target tip equals source tip: plan skip
    • if target tip is an ancestor of source tip: plan update
    • otherwise: plan block
  8. If not --dry-run, it pushes only the create and update branches to the target.

The core logic is in internal/syncer/syncer.go. The CLI parsing is in cmd/git-sync/main.go.

What it does not do yet:

So this is already usable as a safe MVP for “mirror branches from A to B if B is empty, equal, or behind A”. It intentionally refuses diverged branches.

what is a git fetch doing in general?

3mo ago·8.3s

git fetch asks another repository for refs and objects, downloads anything you are missing, and updates your remote-tracking refs. It does not change your current branch or working tree.

In practice it usually does this:

  1. Contact the remote and read its advertised refs like refs/heads/main.
  2. Compare those refs with what your local repo already has.
  3. Negotiate which objects you already have and which commits/trees/blobs you need.
  4. Download a packfile containing the missing objects.
  5. Store those objects in .git/objects.
  6. Update refs like refs/remotes/origin/main.

What it does not do:

That is why git pull is basically:

  1. git fetch
  2. then merge or rebase onto what was fetched

So the important idea is: fetch is “synchronize object database + remote-tracking refs”, not “update my branch contents on disk.”

is there the opposite to git fetch?

3mo ago·4.7s

Yes: git push.

Roughly:

fetch downloads objects and updates your local view of remote refs. push uploads objects and asks the remote to move its refs.

They are not perfectly symmetric, though:

At the protocol level the pair is:

So for your tool, the “opposite of fetch” on the target side is effectively a push.

if I have a branch on the source and target, and the source adds a single commit on top, now I run the tool: how much data is transfered for that branch?

3mo ago·13.5s

In the ideal case: only the new commit and any objects reachable from it that the target does not already have.

For one extra commit on top of a shared branch tip, that usually means:

If the commit changes a tiny file, the transfer can be very small. If it adds a 50 MB blob, you transfer that blob.

For the current MVP specifically, the flow is less optimal than a hand-tuned protocol bridge:

  1. It fetches the selected branch from source into the in-memory repo.
  2. It also fetches the selected branch from target into the same in-memory repo.
  3. Then it pushes from memory to target.

So total network for that branch is roughly:

If source and target already share almost everything and source only added one small commit, then:

So the push side is incremental, but the current fetch side is not yet aggressively minimized. A more advanced version could reduce this further by negotiating directly against target’s advertised haves before fetching from source.

can we add that?

3mo ago·6.0s·5 calls

Yes. The simplest useful version is to fetch the target branches into memory first, then fetch the source branches into that same object store. That lets the source-side fetch negotiate against objects we already learned from the target, so for the “one new commit on top” case the source transfer can collapse much closer to just the delta.

I’m also updating the docs to reflect the new transfer behavior so the repo matches what the command now does.

This change is small but meaningful, so I’m rerunning formatting and tests now.

Yes, and I added the first practical version of it.

What changed:

Effect on your example:

What this improves:

What it does not do yet:

Docs were updated in README.md, and env GOCACHE=/tmp/go-build go test ./... still passes.

The next step, if you want to push this further, is to stop fetching full target branch objects when we only need target tip commits/ancestry checks. That would reduce target -> tool traffic too.

yes I want to continue, but before that I want to setup an integration tests which uses go-git as server for source and target and validates the right things are transfered

3mo ago·15.2s·48 calls

I’m going to inspect go-git’s server-side test helpers and transport packages, then I’ll build an integration harness around two in-process HTTP servers so we can assert both correctness and transfer size.

I found the go-git server primitives and the HTTP test suite. Next I’m reading their in-process HTTP server setup so I can reuse the same pattern instead of inventing a new protocol shim.

The built-in HTTP transport tests use git-http-backend, but go-git itself has server-side upload-pack/receive-pack sessions. I’m looking at the HTTP client expectations now so I can expose those sessions over httptest and keep the whole test stack inside Go.

I have enough of the server pieces now. The remaining bit is the smart-HTTP framing: info/refs responses and the two service POST endpoints. I’m checking whether go-git already exposes the exact encoding helpers; otherwise I’ll add a tiny test-only handler that wraps the server sessions correctly.

I’m wiring two integration cases now: initial sync to an empty target, and a resync after one extra source commit. I’m also fixing the target-fetch path to tolerate an empty target repo, because the tests should cover that baseline behavior too.

The integration harness is in. I’m compiling and running the new tests now; I expect a couple of small fixes around imports or smart-HTTP framing before the full loop passes.

The first failure is just the expected missing object import in the test helper. I’m fixing that and rerunning the integration cases.

The integration harness needs to open local loopback listeners via httptest, and the sandbox blocked that. I’m requesting permission to run the integration test process outside the sandbox so I can validate the HTTP server path end to end.

The first real protocol issue showed up: Remote.ListContext against an empty target returns ErrEmptyRemoteRepository. Since we don’t actually need target refs from ListContext for planning, I’m removing that failure path and relying on the target fetch phase instead.

The resync result is useful: it shows that “smaller pack bytes” is not a stable assertion against go-git’s server implementation, even when the negotiation path is correct. I’m switching the integration check to something more robust: verifying that the second source fetch actually sends have lines, which proves it is negotiating against objects learned from the target.

I replaced the brittle “pack must be smaller” assertion with a protocol-level check: the resync source fetch must include have lines. That directly tests the optimization we added.

The test exposed a real gap: fetching target first does not, by itself, make go-git send have lines on the subsequent source fetch. I’m inspecting go-git’s fetch negotiation now to see what it considers eligible local refs, because that determines whether we can make this optimization work or need a lower-level fetch path.

I found the issue with the assertion, and it’s in the test harness rather than the sync logic: packp.UploadPackRequest.Decode does not populate the have list. I’m switching the metric to count have lines directly from the raw pkt-line request body.

The integration tests are passing now. I’m doing one final full test pass across the repo so the new harness and the existing unit tests are green together.

Added end-to-end integration coverage in internal/syncer/integration_test.go and fixed empty-target handling in internal/syncer/syncer.go.

The integration harness now:

What the tests validate now:

Verification:

One important outcome from the integration work: the robust thing to assert is negotiation behavior (have lines), not “pack bytes must be smaller”. With go-git’s server implementation, the byte-size assertion was not stable enough.

If you want to continue, the next step is to reduce target-side transfer, since the current tool still fetches target history into memory on every run before it can negotiate source fetches.

let's do that

3mo ago·42.7s·14 calls

I’ve validated the current negotiation improvement. The next change is architectural: stop fetching target objects at all, and instead use target ref tips as haves in a direct source-side upload-pack request. Then we can do fast-forward checks from the fetched source commit graph without materializing the target history locally.

I’ve got the path: remove the target fetch entirely, list target refs via receive-pack, fetch source with explicit haves derived from target branch tips, and switch the fast-forward check to walk only the fetched source-side commit graph. Then the integration tests should show zero target upload-pack traffic.

I hit one expected compile cleanup from that refactor: the commit-walk helper needs the repository storer passed in explicitly because object.Commit doesn’t expose it. I’m fixing that plus any remaining import/API mismatches now.

The integration path is green with the new flow. I’m tightening the test expectations slightly so they explicitly prove target upload-pack traffic is gone, then I’ll run the full suite.

Changed the sync flow so it no longer fetches target objects at all.

What it does now:

So target-side transfer is now reduced to:

There is no target upload-pack POST anymore.

The main code change is in internal/syncer/syncer.go. The integration tests in internal/syncer/integration_test.go now explicitly assert:

Verification:

This gets much closer to the design you originally wanted: the tool now uses target refs as negotiation input, without materializing target history locally. The next step, if you want to keep pushing, is to stop using go-git’s high-level push path too and build the source-fetch/target-push bridge entirely from lower-level protocol calls.

yes, let's do that

3mo ago·15.5s·17 calls

I’m replacing the remaining high-level push path with direct receive-pack usage: build the update commands ourselves, compute the object closure from the in-memory store, encode a packfile, and send a ReferenceUpdateRequest straight to the target.

I’ve confirmed the direct push ingredients exist in go-git: low-level ReferenceUpdateRequest, revlist.Objects, and packfile.NewEncoder. I’m using those now so the tool no longer depends on Remote.PushContext.

I’m swapping out Remote.PushContext now. The new path will build packp.Commands directly from the plans, compute the object closure with revlist.Objects, pack those objects with packfile.NewEncoder, and send the request through a raw ReceivePackSession.

The direct push path is in. I’m running the suite now to catch any compile issues or protocol regressions from removing Remote.PushContext.

Two compile fixes surfaced: I still need config.RemoteConfig for the source remote, and the generic storer doesn’t expose Config(). I’m restoring the config import and using a fixed pack window for the direct encoder, matching go-git’s server default.

The tool now does both sides with lower-level protocol/session calls.

What changed in internal/syncer/syncer.go:

So the end-to-end flow is now:

  1. list source refs
  2. list target refs
  3. fetch source with want=source tips and have=target tips
  4. fast-forward check by walking fetched source commits
  5. build raw receive-pack update commands
  6. stream only the needed objects to target

The integration tests still pass, including the assertions that:

Verification:

At this point the main remaining high-level bit is source ref listing via Remote.ListContext. If you want, the next step is to replace even that with direct smart-HTTP ref advertisement handling so the whole tool is consistently low-level.

does that have benefits?

3mo ago·25.3s

Yes, but smaller benefits than the changes we just made.

What you already gained from the recent work:

Replacing source ref listing too would give:

What it would not materially improve:

So the answer is: yes, but mostly architectural benefits, not a big performance win.

If the goal is practical payoff, I would prioritize one of these next instead:

If the goal is purity and full control of the transport, then replacing ref listing is the next obvious step.

ok, let's do ref listing, and then continue with the 5 other open items

3mo ago·7.8s·53 calls

I’m going to replace source ref listing with direct session-based advertisement handling first, then I’ll layer in the next set of features in the least disruptive order: stats output, branch mapping, tags, force/prune controls, and protocol v2 detection/support where the transport exposes it.

I’m folding the remaining feature work into the same pass. Some of it is straightforward CLI/planning work; protocol v2 is the one likely to land as detection/reporting rather than full use, because go-git’s HTTP transport in this version doesn’t expose a v2 request path.

I’m doing a larger rewrite of the sync engine now. That’s the pragmatic way to land the remaining features coherently instead of layering more special cases onto the current file.

I’ve replaced the sync engine core. The next pass is compiler cleanup plus compatibility shims for the existing unit tests, then I’ll wire the new CLI flags and add focused tests for mapping/tags/prune/force.

I’m adding integration coverage for the new planning features now: mapping, tags, prune, and force. That will give us a clean signal before I update the docs.

I’m finishing the low-level ref listing work and wiring the remaining features through tests and docs. First step is to format the touched files and run the full suite so I can fix whatever broke in the combined change set.

gofmt hit README.md, so I’m re-running it on Go files only and waiting for the test failures to settle. After that I’ll fix compile or behavior regressions in one pass.

The first failure is in the new tag/prune path: we’re trying to build a push pack for a tag object we never fetched. I’m reading the sync planner and fetch set now, then I’ll patch the fetch closure so tag updates have the objects they need.

I found two logic gaps from the new feature batch. First, tag creates can point at objects the target already has, so the push path must not insist those objects exist locally. Second, prune currently never sees extra target tags, so it can’t delete them; I’m fixing both now, and I’ll clean up the stats double-count on info/refs in the same patch.

The fetch/push boundary is patched so ref-only retargets can avoid requiring local copies of already-present target objects, and prune now considers unmanaged target tags. I’m reformatting and rerunning the suite to see what the next failure is.

The receive-pack failure looks like a protocol capability issue rather than an object issue now. I’m checking the push request capabilities and command mix next, especially delete handling, because prune is the first path that sends deletions.

go-git’s helper only seeds agent and report-status; it does not actually enable delete-refs despite the comment. I’m making deletion explicit in the push request now, which should clear the prune path if that’s the server-side rejection.

I’ve enabled delete-refs explicitly on prune pushes. Running the targeted integration test again now to see whether that was the protocol mismatch or whether there’s another receive-pack edge behind it.

The in-process HTTP test server is slightly wrong here: go-git’s receive-pack can return a report plus a command error, and the smart-HTTP layer is supposed to return that report body with 200, not collapse it into 500. I’m fixing the harness so the failing command status comes back properly, which should tell us whether the remaining issue is real sync logic or just the test server behavior.

The harness now returns receive-pack status reports instead of masking them behind HTTP 500s. Re-running the targeted test should expose the real protocol or command-level rejection if one is still there.

The remaining failure is real protocol behavior: this server expects a pack stream whenever the request contains create or update commands, even if every referenced object already exists on the target. I’m changing the push path to send an explicit zero-object pack in that case instead of omitting the pack section.

The push path now distinguishes “no pack needed because this is delete-only” from “create/update commands with zero new objects,” and it will stream an explicit empty pack for the latter. I’m rerunning the tag/prune/force integration case to verify that edge is covered.

The next mismatch is behavioral: blocked non-fast-forward refs currently produce a plan entry but not an error, which makes --force less meaningful than it should be. I’m tightening that so a real sync run aborts on blocked refs before it pushes anything, while --dry-run can still report the block without mutating the target.

Blocked refs now stop a real sync run before any push happens, which matches the intended safety boundary for the new force mode. I’m rerunning the force/prune test first, then the full suite if that clears.

The force/prune path is green now. I’m running the full suite next, then I’ll bring the README in line with the new low-level ref listing, stats, mapping, tag, prune, and force behavior.

The code is green. I’m just bringing the README up to date now so it matches the current transport design and the new flags rather than the original branch-only MVP description.

The README is aligned with the current implementation now. I’m doing one quick status check so I can summarize the exact outcome and any remaining gap cleanly.

Implemented the low-level ref listing and finished the next feature batch. Source and target ref discovery now go through direct info/refs handling in internal/syncer/syncer.go, so the sync path is consistently low-level on list, fetch, and push.

The open items covered in this pass are in place:

The CLI wiring is in cmd/git-sync/main.go, the new integration coverage is in internal/syncer/integration_test.go, and the updated behavior/docs are in README.md.

One remaining gap is protocol v2 itself: the flag exists, but auto currently resolves to v1 and the README calls that out explicitly in README.md.

Verification: env GOCACHE=/tmp/go-build go test ./... passed.

4c34017initial version\ \ Entire-Checkpoint: 20848a8dd015+2003

ec5d9ffAdd protocol v2 packet primitives\ \ Entire-Checkpoint: e87357af16c9+260

71d6275Add source-side Git protocol v2 sync\ \ Entire-Checkpoint: 3759c319aa62+644/-50

74373e4Document Git protocol v2 support\ \ Entire-Checkpoint: 6705fddb3ef7+16/-1

9529088Explain why push remains on receive-pack\ \ Entire-Checkpoint: df43bc263284+11

acaab9cAdd read-only source probe command\ \ Entire-Checkpoint: 6b50977affcf+211/-6

3752981Add read-only source fetch smoke command\ \ Entire-Checkpoint: 01fbd070bdf2+210

0cff362Restore CLI and add plan command\ \ Entire-Checkpoint: a42e22b7f6bc+16

7a8fae0Add git-sync CLI entrypoint\ \ Entire-Checkpoint: 2fdb51a2ea3c+259

77295dbAdd optional target capability probe\ \ Entire-Checkpoint: 7aa99a6074b0+97/-6

379c575Test dry-run sync does not push\ \ Entire-Checkpoint: 683f05e0a6a6+41

2826440Fix gitignore and add JSON output mode\ \ Entire-Checkpoint: 9d262b2fb550+69/-11

44b243fSplit transport stats by protocol phase\ \ Entire-Checkpoint: 99a3ba48420b+23/-4

efd5740Test CLI plan mode end to end\ \ Entire-Checkpoint: 5424186859f4+350

e40e6ddStabilize JSON output schema\ \ Entire-Checkpoint: c7bfbc06f5ff+139/-56

e55eb4dDocument nested plan JSON fields\ \ Entire-Checkpoint: 448fb22e8f55+21

1dbdf39Update README plan wording\ \ Entire-Checkpoint: fb72ba1a2199+2/-2

c487e35Add git credential helper fallback\ \ Entire-Checkpoint: 939c30174771+219/-5

c05370dAdd git-http-backend integration test\ \ Entire-Checkpoint: 07ebffc93812+239/-8

1fb6bdcDocument bootstrap relay design\ \ Entire-Checkpoint: 2dcf2f1a3166+157

4f2b618Add bootstrap relay command\ \ Entire-Checkpoint: 0bdb781a9478+477/-1

c535fc6Document bootstrap and add backend test\ \ Entire-Checkpoint: f09ed59d22eb+59/-2

1743526Test bootstrap mappings and tags\ \ Entire-Checkpoint: 68e3d42cfb81+82

55f5f45Add bootstrap pack size limit\ \ Entire-Checkpoint: 72f07f367d81+75/-1

9435d85Add memory measurement output\ \ Entire-Checkpoint: 4805c08b89a8+142/-1

50f10f8Auto-use bootstrap on empty targets\ \ Entire-Checkpoint: 8620d1269809+133/-32

134bcccAdd incremental relay test harness\ \ Entire-Checkpoint: 4a7575f2583f+116/-2

520e91fAdd narrow incremental relay path\ \ Entire-Checkpoint: 894a895034bb+78/-10

b88dfb7Expand incremental relay to branch batches\ \ Entire-Checkpoint: 03bc9b185ab8+86/-13

08ac0e3Expand incremental relay to mapped branches\ \ Entire-Checkpoint: 78bcff081d69+86/-9

3becb72Expand incremental relay to tag creation\ \ Entire-Checkpoint: 90fce803a9ea+84/-13

d971ec0Explain relay selection decisions\ \ Entire-Checkpoint: 896f021c4767+68/-39

e368b12Replace linux fetch smoke with bootstrap smoke\ \ Entire-Checkpoint: 9425efd8edfc+66/-38

8ee4621add mise.toml\ \ Entire-Checkpoint: d005afe6724c+25

e4b9f67Improve live linux bootstrap smoke\ \ Entire-Checkpoint: d50053e4c868+28/-8

7027752Document bootstrap batching design\ \ Entire-Checkpoint: a2d74df554b4+264

2ed0a3fAdd Phase A bootstrap batching\ \ Entire-Checkpoint: aebdecbeb417+500/-3

9e1e66dDocument Phase A bootstrap batching\ \ Entire-Checkpoint: 5cc48c743bfd+51/-2

757018fResume batched bootstrap from temp refs\ \ Entire-Checkpoint: a859cf299e7d+127/-7

1a25922Report bootstrap batch progress\ \ Entire-Checkpoint: 4adfd6c1e723+22/-2

d426f33Document bootstrap batch resume\ \ Entire-Checkpoint: ffd71d2ccb2a+5/-3

8b741a1Add tag creation after batched bootstrap\ \ Entire-Checkpoint: 4733a258b7e6+109/-9

ba85a15Document batched bootstrap tag support\ \ Entire-Checkpoint: 6ef2787ebf99+7/-4

eee02e3Add batched linux bootstrap smoke\ \ Entire-Checkpoint: 21576324299f+26/-2

46857fcShow bootstrap progress in verbose mode\ \ Entire-Checkpoint: 6afc8032e5a1+56

a6721caUse two-stage bootstrap batch planning\ \ Entire-Checkpoint: 0670ea271363+156/-17

1235981Sample bootstrap batch checkpoints\ \ Entire-Checkpoint: 121669700ffc+85/-87

5806892Polish bootstrap batch guidance\ \ Entire-Checkpoint: 2fd4174c6988+28/-9

8686d70Add TLS skip-verify endpoint options\ \ Entire-Checkpoint: 67706ea6e332+113/-64

cd57c23Add Entire local public-repo smoke test\ \ Entire-Checkpoint: 3ad59c31a883+378/-4

c86a9b0todos\ \ Entire-Checkpoint: 32adf4e023ef+780

0520b7cRewrite git-sync into focused packages\ \ Break the monolithic syncer.go (3143 lines) into 7 focused packages:\ - internal/gitproto: pkt-line, smart HTTP, capability negotiation, v1/v2 fetch/push\ - internal/planner: mapping validation, planning, relay eligibility, checkpoints\ - internal/auth: credential resolution, Entire DB tokens, git credential helper\ - internal/strategy/bootstrap: one-shot + batched bootstrap, GitHub preflight\ - internal/strategy/incremental: incremental relay execution\ - internal/strategy/materialized: materialized fallback push with size guard\ - internal/syncer: slim orchestrator (734 lines), stats, measurement\ \ Addresses all 22 issues from docs/rewrite-issue-list.md:\ \ Correctness: tag ref creation independent of pack (#1), duplicate target\ mapping rejection (#2), cross-kind mapping rejection (#3), sideband-64k\ preference (#4), pack reader close discipline (#5), include-tag capability\ gating (#6), OAuth refresh error propagation (#7).\ \ Concurrency: mutex-protected stats (#8), bounded response reads (#9),\ flock-based file token store locking (#10).\ \ Architecture: package decomposition (#11), shared session setup (#12),\ explicit Params structs (#13).\ \ Performance: commit-count batch sizing heuristic (#14), materialized\ object count guard (#15), bounded ancestry checks with ErrAncestryDepthExceeded\ (#16), reusable pkt-line buffer (#17).\ \ Testing: 73 test functions, 7 benchmarks, coverage 41-58% on core packages.\ Protocol malformed-input tests (#18-20), behavioral edge cases (#21),\ benchmarks for planning/protocol hot paths (#22).\ \ Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com\ Entire-Checkpoint: 27633b8ca595+7196/-3981

cd8b183Add unit tests for strategy packages, auth token flow, and object push\ - strategy/incremental: test plansToPushPlans and toGP conversion (0% → 27.6%)\ - strategy/materialized: test plansToPushPlans and MaxMaterializedObjects (0% → 12.1%)\ - auth: test getTokenWithRefresh, ReadStoredToken/WriteStoredToken round-trip,\ encodeTokenWithExpiration, credentialService, lookupEntireDBToken (53% → 63.1%)\ - planner: test ObjectsToPush with in-memory repo, have-set exclusion (53% → 60.5%)\ \ Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com\ Entire-Checkpoint: baf824eebb82+531

025234eDeduplicate shared helpers and remove dead code\ - Extract convert.DesiredRefs and convert.PlansToPushPlans into\ internal/convert to eliminate 4 copies across strategy packages\ - Remove dead PlannerDesired struct from gitproto/convert.go\ - Remove unused statsCollector.addWantsHaves and addCommands\ - Fix SortedUniqueHashes to use map[Hash]struct{} for consistency\ \ Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com\ Entire-Checkpoint: 6a10067e2e43+64/-117

4b63c50Annotate rewrite tracker against current branch state\ \ Entire-Checkpoint: aac6eaf4698e+62/-31

4e9ecdaAdd cancellation coverage and tighten rewrite tracker\ \ Entire-Checkpoint: edee273e9e3b+80/-3

1a7aefcStrengthen in-flight cancellation coverage\ \ Entire-Checkpoint: bce3d316a633+78/-21

f9170fcAdd direct relay decision coverage\ \ Entire-Checkpoint: 1a1fb1d80eaf+103/-4

d11c597Replace bootstrap progress logging with slog\ \ Entire-Checkpoint: 75d8cdda2721+52/-31

e9b6ed4Add execution-path syncer benchmarks\ \ Entire-Checkpoint: 2a65ca22e9d1+221/-3

f266bffMake syncer benchmarks use testing.TB fixtures\ \ Entire-Checkpoint: ccee9163d5b1+24/-24

4b7a223Add batched bootstrap resume edge-case coverage\ \ Entire-Checkpoint: e391e5e1f4c9+124/-1

f5532bfCover lightweight tag creation in batched bootstrap\ \ Entire-Checkpoint: fbdb2b242248+48/-1

6f61ee7Move verbose logger into shared session setup\ \ Entire-Checkpoint: 8c8385150305+11/-11

709a9b8Reduce duplicate bootstrap batch probes\ \ Entire-Checkpoint: 28372368f4e8+7/-1

74213a2Add stream close coverage for pack push paths\ \ Entire-Checkpoint: 6bda93bfe6b6+120/-1

7407954Reuse successful bootstrap probe packs\ \ Entire-Checkpoint: 4ea409f02833+81/-34

b074cdeNarrow strategy dependencies behind source interfaces\ \ Entire-Checkpoint: e8501c11ba8a+37/-11

689eaddRoute strategies through target push abstractions\ \ Entire-Checkpoint: 00b8437a4e4a+336/-58

07ddaa2Centralize bootstrap capability checks\ \ Entire-Checkpoint: 865a5a114231+142/-40

7b5e963Cover batched bootstrap cleanup reruns\ \ Entire-Checkpoint: 1dc073d2c27c+53

eb766e1Harden relay strategy boundaries\ \ Entire-Checkpoint: bdbeba58c09f+178/-6

bf3b784Cover batched delete retry recovery\ \ Entire-Checkpoint: 7938df770894+101/-11

749b51fMigrate from go-git v5 to v6\ \ Switch to go-git/go-git/v6 v6.0.0-alpha.1 and go-billy/v6. The v6 API\ removes the transport session abstraction (UploadPackSession,\ ReceivePackSession) and changes Endpoint to embed url.URL. Rather than\ adapting to v6's new session model, the v1 fetch and push paths were\ rewritten to use direct HTTP protocol handling via PostRPCStream, which\ eliminates the dual-stack transport problem called out in the rewrite\ memo and aligns with the goal of owning the smart HTTP layer directly.\ \ Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com\ Entire-Checkpoint: b5b72cefafb3+642/-749

0ed62c5Restore streaming receive-pack pushes\ \ Entire-Checkpoint: 945161187c0f+160/-15

e2a5ee2Harden v1 upload-pack path coverage\ \ Entire-Checkpoint: 273279ad48cf+96

3d8ba82Tune bootstrap checkpoint probe spans\ \ Entire-Checkpoint: 8cefb463e4c2+89/-1

3f2d1cbProbe bootstrap tip optimistically\ \ Entire-Checkpoint: 021d8d15741b+86/-4

be94300Add bootstrap probe count visibility tests\ \ Entire-Checkpoint: 000e2d11c70b+133/-2

ddd4c7cExtract validation package\ \ Entire-Checkpoint: b5aa1d18a757+163/-47

9e0a8b5Extract benchmark repo fixtures\ \ Entire-Checkpoint: 87c9e7e44eb9+94/-55

353954dMove ref mappings into validation\ \ Entire-Checkpoint: 676e38397f34+13/-11

c567560Unify repo test fixtures\ \ Entire-Checkpoint: c980720ba4e1+85/-98

ebd2cf7Clarify validation mapping ownership\ \ Entire-Checkpoint: 1632dbb089c5+1/-1

f87ea42Move mapping validation into validation\ \ Entire-Checkpoint: f72b1b7c868f+96/-106

342fb90Add repeatable benchmark command\ \ Entire-Checkpoint: 69a3eb2efe0f+514

2b415acDocument benchmark command in README\ \ Entire-Checkpoint: 9d63d45b98e8+28

f82addbClean up README structure\ \ Entire-Checkpoint: be0975547aa9+51/-52

c44e996Split README details into docs\ \ Entire-Checkpoint: cca61b1c8965+183/-85

3138b97Explain product rationale in docs\ \ Entire-Checkpoint: ace64d1131b3+58

a05bee4Harden bootstrap pack close ownership\ \ Entire-Checkpoint: 1b15c3c23d6c+119

e933b9aSkip conservative tail probes in batching\ \ Entire-Checkpoint: 26d96c05aedf+62/-3

61e809eType target relay capability checks\ \ Entire-Checkpoint: e062e0d13a64+82/-30

68845c9Cover batched checkpoint retry recovery\ \ Entire-Checkpoint: e4aeef3a16bb+124/-2

1a9efa9Narrow planner relay policy input\ \ Entire-Checkpoint: 441e23e1e3f5+21/-12

b1842f4Expose materialized sync safety limit\ \ Entire-Checkpoint: cb50436aaefc+111/-58

d9d38abEncapsulate bootstrap checkpoint planning state\ \ Entire-Checkpoint: 9084d6c02935+111/-68

ecd3908Clarify materialized limit flag semantics\ \ Entire-Checkpoint: e32d96585b91+3/-3

6c10774Close pack streams on push preflight errors\ \ Entire-Checkpoint: a66d4355d81d+5

86ad049Keep usage text aligned with materialized limit flag\ \ Entire-Checkpoint: 5fa0aa17ecbf+1/-1

5ca5f37Cover v2 fetch body closure on decode errors\ \ Entire-Checkpoint: 8e6efde358b4+37

579d626Cover v2 fetch cancellation behavior\ \ Entire-Checkpoint: fea91c44dc0d+53/-1

3780862Cover push pack closure across cancellation paths\ \ Entire-Checkpoint: 61b6f9608f08+46

c6ecbcbCover v2 fetch-to-store cancellation behavior\ \ Entire-Checkpoint: 7fe8a1c82e41+53/-1

048f602Cover v2 fetch-to-store decode cleanup\ \ Entire-Checkpoint: b139564a8f9b+37

a62adb7Refresh remaining behavioral coverage gaps\ \ Entire-Checkpoint: c86204248e6c+4/-8

d0f0dfaCover mid-stream v2 fetch cancellation\ \ Entire-Checkpoint: 52544e8d948c+89/-1

f7ab510Encapsulate materialized execution stages\ \ Entire-Checkpoint: de77f2de9b81+50/-31

f3c0328Centralize target capability wiring in session setup\ \ Entire-Checkpoint: 6dcbf336156b+12/-7

5d1679aCover batched pack stream interruption cleanup\ \ Entire-Checkpoint: f5c817f253a9+78/-1

25d21feClose incremental pack streams on read errors\ \ Entire-Checkpoint: 4cea5c699ef8+100/-1

c676521Cover returned fetch-pack reader interruptions\ \ Entire-Checkpoint: 15bb3382b7eb+125/-1

e397687Narrow bootstrap execution handoff in syncer\ \ Entire-Checkpoint: e91c0933deb3+11/-19

5923020Narrow sync execution through session helpers\ \ Entire-Checkpoint: b6cb5c0bad67+33/-17

3d6606dBundle target state in sync session\ \ Entire-Checkpoint: 5683f5862e46+38/-27

76823bfTighten bootstrap checkpoint boundary search\ \ Entire-Checkpoint: 407e0ab6035f+223/-3

c3b7fa0Expose batch metrics in benchmark summaries\ \ Entire-Checkpoint: e2a6e30a7370+59/-3

b718e82Add batched planning backend sensitivity test\ \ Entire-Checkpoint: 671334abfa33+91

23cfb4aDocument batched planning backend test\ \ Entire-Checkpoint: d9d1a8661781+8/-135

8ba4146Cover malformed fetch packets after startup\ \ Entire-Checkpoint: 50ad337b37fc+121/-1