Skip to content

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 city sync --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.

The mental model behind it all