okay lets add an FAQ with this information to the readme..of course open a PR · Entire
git pull from the main branch.. make sure there's nothing on this branch
2mo ago · 4.3s · 4 calls
I’ll check the current branch and worktree first so I don’t trample anything, then I’ll pull from main once I know the state.
You’re already on main, and it’s tracking origin/main. I do see several untracked config/tooling directories (.cursor/, .factory/, .gemini/, .github/hooks/, .opencode/), so I’ll leave those alone unless you want them removed.
2mo ago
anything that's like just laying around.. uncommitted.. we should remove
2mo ago · 4.3s · 4 calls
Got it. I’ll identify exactly what’s uncommitted or untracked after the pull, then remove only the loose/untracked stuff unless there are tracked edits that need a different kind of cleanup.
The only things laying around are untracked directories. I’m going to remove those now with git clean; no tracked file changes showed up.
The untracked directories are gone. I’ll do one final status check so we know the branch is clean.
Clean now. You’re on main, up to date with origin/main, and there are no uncommitted or untracked files left.
why did I even have those untracked config/tooling directories
2mo ago · 5.0s · 3 calls
I’ll peek at the repo ignore/setup files to see whether those directories were expected local-only tool state or just not covered by .gitignore.
Those were almost certainly created by local dev/AI/editor tools, not by the repo itself:
.cursor/from Cursor editor/workspace settings.gemini/from Gemini CLI or related agent tooling.opencode/from OpenCode tooling.factory/likely from Factory/agent tooling.github/hooks/likely local hook setup files, but under.githubinstead of Git’s normal.git/hooks
They showed up because this repo’s .gitignore does not ignore those directories. So Git saw them as “new files I don’t know about,” even though they were just local tooling state.
Nothing was committed from them, and they’re gone now. If they keep reappearing, we can add the local-tool dirs to .gitignore or to .git/info/exclude if you want the ignore rule to stay machine-local.
okay so for git sync.. we need to research how the repo works
2mo ago · 4.4s · 34 calls
I’ll map the repo from the outside in: README/docs first, then the module layout and command entrypoints, then I’ll trace the core flow so we have a practical mental model instead of just a file list.
The tree is a Go library plus two CLI binaries: git-sync and git-sync-bench. I’m going to read the architecture/docs and the public API next, then follow how the CLI wires into the internal planner/syncer.
The docs confirm the big idea: it tries to sync remote-to-remote by relaying Git packs directly, only falling back to in-memory object materialization for cases that aren’t relay-safe. Now I’m tracing the actual entrypoints and request structs so the summary matches the code, not just the docs.
I’ve got the main flow now. I’m doing a quick test/help pass to catch any mismatches between docs and executable behavior, then I’ll give you the “how this repo works” map with the parts that matter for future changes.
Small snag: this shell doesn’t currently have go on PATH, so I couldn’t execute the test suite directly. The repo is pinned to Go 1.26.2 and has mise.toml, so I’m checking whether the repo expects mise to provide the toolchain.
mise is installed and the repo declares its Go toolchain there, so I’ll use mise exec for verification instead of relying on shell PATH.
I researched the repo. Short version: git-sync is a Go CLI/library that mirrors Git refs from one HTTP remote to another without making a local checkout. Its main trick is relaying source packfiles directly into the target’s receive-pack when safe.
Mental Model
The flow is:
- CLI parses command/flags in cmd/git-sync/main.go.
- Public API lives in client.go, with stable methods:
Probe,Plan,Sync,Replicate. client.gobuilds an internal config throughinternalbridge.- The real orchestrator is internal/syncer/syncer.go.
syncerdiscovers source refs, target refs/capabilities, builds a plan, then chooses a transfer strategy.
Main Commandsgit-sync sync is the normal reconciler. It bootstraps empty targets, fast-forwards safe updates, and falls back to materialized in-memory push when needed.
git-sync replicate is source-authoritative and relay-only. It overwrites target refs to match source, but fails rather than doing materialized fallback.
git-sync plan does the same planning without pushing.
bootstrap, probe, and fetch exist too, but docs describe them as advanced/unstable CLI surfaces.
Strategy Split
The important strategy packages are:
- internal/strategy/bootstrap/bootstrap.go: empty-target relay, including batched bootstrap for large repos.
- internal/strategy/incremental/incremental.go: relay fast path for safe fast-forward branch updates and tag creates.
- internal/strategy/materialized/materialized.go: fallback path that fetches into an in-memory
go-gitstore, computes object closure, then pushes. - internal/strategy/replicate/replicate.go: relay-only overwrite behavior.
Planner
The planner decides what should happen per ref in internal/planner/planner.go. It builds desired refs from branches/mappings/tags, then emits actions: create, update, delete, skip, or block.
Fast-forward safety is checked with commit ancestry in internal/planner/planner.go. Tags are immutable unless --force is used. Deletes only happen with --prune.
Relay eligibility rules are centralized in internal/planner/relay.go.
Protocol Layer
The low-level Git HTTP implementation is in internal/gitproto:
- internal/gitproto/smarthttp.go:
info/refsand RPC POSTs. - internal/gitproto/fetch.go: source fetch, v1/v2 handling, pack streaming.
- internal/gitproto/push.go: target receive-pack commands, pack relay, materialized pack encoding.
It supports source-side protocol auto|v1|v2, but target push stays on receive-pack v1-style smart HTTP.
Key Design Constraint
The repo is built around avoiding persistent local repo storage. Relay paths are preferred because memory stays low. Materialized fallback is bounded by --materialized-max-objects, default 500000, to avoid huge in-memory pushes.
Verification
I ran the suite with the repo’s mise Go toolchain:
mise exec -- go test ./...
Everything passed.
Here are the concise answers:
- Does it shallow clone/sync or full historical data?
It syncs full Git history needed for the selected refs. It does not do shallow clone-style sync. For batched bootstrap planning it may fetch a filtered commit graph, but the actual target sync pushes complete objects.
- Is it just refs or objects as well?
Objects too. Refs are the plan/update surface, but the tool fetches/pushes the underlying commits, trees, blobs, and tags needed to make those refs valid on the target.
- Is it bidirectional?
No. It is one-way: source remote to target remote.
- Does it support all actions? (create, modify, delete)?
Mostly yes:
Create refs: yes
Update/modify refs: yes
Delete refs: yes, with
--pruneForce/non-fast-forward updates: yes in
sync --forcereplicatecan overwrite, but is relay-only and more restrictive.How does it scale? Does it stream through the in-memory store?
Best case, it streams pack data from source directly into target without materializing objects locally. That is the intended scalable path.
The in-memory go-git store is used for planning/ancestry checks and fallback materialized pushes. In fallback mode, it fetches objects into memory, computes the object closure, then encodes/pushes to the target. That path is bounded by --materialized-max-objects.
- How long does it take for a medium-sized repo?
There is no fixed answer in the repo. It depends on remote latency, pack size, provider speed, and whether relay is used. For a medium repo on the relay path, expect roughly “network transfer plus Git server pack generation time,” not local clone-plus-push time.
- How does it deal with partial successes? Is it atomic?
It plans everything before pushing and blocks unsafe refs before starting. A single receive-pack request is target-side atomic in the normal Git sense for that push. But across multiple batches or separate push requests, especially batched bootstrap, it is not globally atomic. It uses temp refs/resume behavior for batched bootstrap.
- Does it support SSH?
No. Smart HTTP/HTTPS only.
- Is this just one time or does it run in the background?
It is a one-shot CLI/library operation. It does not run as a daemon or background watcher. You would run it manually, from CI, cron, a worker, or another service.