Forge-agnostic¶
git-city does its whole job with plain Git. There is no GitHub token to set, no GitLab API to authenticate against, no host-specific client to install. Every command operates on commits, branches, and (optionally) a remote — nothing more.
This is a deliberate stance. The daily branch-workflow loop — create a branch, stack work on it, keep it in sync, land it, undo a mistake — is fundamentally a Git operation. Tying it to a particular forge buys you pull-request automation at the cost of locking the entire workflow to one host. git-city keeps the workflow portable and treats the forge as an optional layer on top.
Where this sits today
Forge integration is not built yet. git-city works fully with a plain Git remote, or with no remote at all. The roadmap below covers what a future forge layer would add — but everything described elsewhere in these docs works without it.
What "forge-agnostic" means here¶
A forge is a hosting service that wraps Git with extra features: pull/merge requests, code review, CI triggers, issue links. GitHub, GitLab, Gitea, Bitbucket, and Forgejo are forges. Plain git push to an SSH host is not.
git-city never calls a forge API. The commands that touch a remote use only the Git protocol:
| Operation | What git-city runs | Forge needed? |
|---|---|---|
| Fetch updates | git fetch <remote> --prune |
No |
| Update a trunk | git branch -f <trunk> <remote>/<trunk> |
No |
| Push a feature | git push --force-with-lease=… <remote> <branch> |
No |
| Land a feature | fast-forward parent, push it (and, with --delete, remove the remote branch) |
No |
| Delete a branch | remove it locally and on the remote | No |
Because all of this is Git, it works against any host that speaks Git — and against a bare repository on a file server, or a clone with no remote configured at all.
→ How sync, land, and delete use the remote
What this enables¶
Trunk-based development. A trunk (your main, plus any configured perennials) fast-forwards from its remote and is never rebased. Features rebase onto it and land by fast-forward, keeping history linear. None of that needs a forge.
Patch-based and email workflows. Because git-city produces clean, linear, rebased branches, the output is exactly what git format-patch / git send-email expects. You can develop a stack with git-city and ship it as a patch series by hand.
Self-hosted and air-gapped setups. A plain Git remote over SSH, an internal mirror, or a bare repo on a shared drive are all first-class. There is no service to reach and no credentials beyond the ones Git already uses.
No-remote experiments. With no remote configured, the push steps simply have nothing to push to — the local workflow (branches, stacks, rebases, undo) still works end to end.
Safety without a forge
Every remote write is a --force-with-lease push, so git-city refuses to clobber commits on the remote that you have not seen. undo never deletes a remote branch git-city did not create. You get the safety guarantees without handing credentials to a third-party API.
A complete loop, no forge in sight¶
Here is what git city sync actually runs for a feature — pure Git, shown via --dry-run:
git fetch origin --prune
git branch -f main origin/main
git checkout add-login
git rebase --onto main <base> add-login
git push --force-with-lease=add-login:<sha> origin add-login
Nothing executed (--dry-run).
origin here is any Git remote. Swap it for a GitHub URL, a GitLab URL, or git@your-server:repo.git and the commands are identical.
What is deferred¶
The piece a forge would add is proposing your work for review — turning a landed-ready branch into a pull request, a merge request, or a patch series. git-city does not do this yet.
The roadmap reserves a single command for it:
| Future command | What it would do |
|---|---|
git city propose |
Open a PR/MR on the configured forge, or assemble a patch series, for the current branch's stack |
When it lands, propose will be the only forge-aware command. The rest of git-city — sync, land, delete, the reversible engine — stays on plain Git. The forge becomes an output target, not a dependency.
→ Roadmap
Contrast with git-town¶
git-town puts the forge at the center of its workflow: proposing, shipping, and reviewing changes assume a connected host, and you configure forge credentials as part of normal use.
git-city inverts that. The forge is optional and, for now, absent. The local Git workflow is the product; forge support is a planned add-on that will never become load-bearing for the everyday loop.
| git-town | git-city | |
|---|---|---|
| Forge | Central to the workflow | Optional, and not built yet |
| Works with no remote | Limited | Fully |
| Remote writes | Forge API + Git | Git only (--force-with-lease) |
| Proposing changes | Built in | Deferred (propose, planned) |
→ Full comparison with git-town
git-city's bet is that a forge-agnostic core ages well: the workflow you learn works the same on GitHub, on a self-hosted Forgejo, or on a bare repo on a USB stick — and a future propose command will meet your forge where it is, without ever holding the rest of the tool hostage.