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.

The user interrupted the previous turn on purpose. Any running unified exec processes may still be running in the background. If any tools/commands were aborted, they may have partially executed.

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:

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:

  1. CLI parses command/flags in cmd/git-sync/main.go.
  2. Public API lives in client.go, with stable methods: Probe, Plan, Sync, Replicate.
  3. client.go builds an internal config through internalbridge.
  4. The real orchestrator is internal/syncer/syncer.go.
  5. syncer discovers source refs, target refs/capabilities, builds a plan, then chooses a transfer strategy.

Main Commands
git-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:

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:

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:

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.

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.

No. It is one-way: source remote to target remote.

Mostly yes:

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.

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.

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.

No. Smart HTTP/HTTPS only.

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.