sync¶
git city sync brings a branch up to date with everything around it — the remote, its parent, and (with --all) the whole tree. It is the command you run most: after pulling new work into the trunk, after a teammate pushes to a shared feature, or any time a stack has drifted out of alignment.
git-city syncs by rebasing, not merging. A feature is rebased onto its parent so history stays linear and each branch reads as a clean diff against the one below it. This is the single most important thing to understand about the command.
| Argument / option | Meaning |
|---|---|
[branch] |
Branch to sync. Defaults to the current branch. |
--all |
Sync every feature in the repo, parents before children. |
--dry-run |
Print the exact git commands that would run; execute nothing. |
What sync does¶
The behavior depends on the branch type.
Syncing a feature¶
For a feature branch, sync performs a full, ordered update:
- Fetch from the remote (with
--prune). - Bring the parent up to date, recursively — a feature can only be rebased onto a parent that is itself current.
- Absorb the remote: if the feature's own remote tracking branch has commits you don't, those are taken in first so nothing is lost.
- Rebase the feature onto its (now updated) parent.
- Force-push the result with
--force-with-lease.
Step 5 uses --force-with-lease, never a bare --force: if the remote moved in a way git-city didn't see, the push is refused rather than clobbering someone else's work.
Merge strategy (for long-lived branches)¶
Rebase is ideal for short-lived branches synced often. For a long-lived branch against a busy parent, rebasing re-resolves the same conflicts on every sync (rerere helps, but only so much). For those, switch the branch to the merge strategy:
Now git city sync on that branch merges the parent in (git merge) instead of rebasing onto it, and pushes plainly — no force, because a merge only adds a commit, it never rewrites history. You resolve each conflict once; the resolution is a commit that sticks, so future syncs only surface genuinely new conflicts. The branch shows a merge tag in info/tree, and conflicts pause/continue/abort exactly as with rebase.
The default is rebase; set the key back (or git config --unset it) to return. This is a per-branch choice — most branches stay on rebase for linear history; flip only the heavy ones.
Syncing a trunk¶
A trunk (the configured trunk, plus any perennials) is never rebased onto anything — it is long-lived shared history. Syncing a trunk simply fetches and fast-forwards it to its remote (or rebases your local trunk commits onto the remote if it has diverged).
--all: syncing the whole tree¶
--all syncs every feature in the repository in topological order — each parent is brought up to date before any of its children, so a child is always rebased onto an already-synced parent. Each trunk involved is updated exactly once, no matter how many features sit on top of it.
This is the usual way to reconcile an entire stack (or several stacks) with the trunk in one command.
Previewing with --dry-run¶
Every mutating path through sync supports --dry-run, which prints the exact, ordered git commands it would run and then executes nothing:
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).
Look before you leap
--dry-run is the fastest way to understand what a sync will do to a particular branch — especially before a --all run across an unfamiliar tree. It is read-only and safe to run any time.
When a rebase conflicts¶
A rebase can stop on a conflict. When that happens, sync pauses rather than fails: the rebase is left in progress in your working tree, and the remaining steps are saved so the operation can be resumed.
git-city: sync add-login is paused.
stopped on: git rebase --onto main <base> add-login
resolve the conflict and `git add`, then:
git city continue resume the operation
git city abort undo everything and return to the start
git city info show this and the repo status
Resolve the conflicted files, git add them, then choose:
| Command | Effect |
|---|---|
git city continue |
Resume the paused sync from where it stopped. |
git city abort |
Undo everything and return to the exact starting state. |
git city info |
Re-print the paused-operation message above, then the dashboard. |
Note
A paused operation is sticky: while one is in progress, most other git-city commands (including undo) refuse to run until you continue or abort. This keeps the repository in a single, well-defined state.
→ Resolving conflicts · Recovery commands
Errors¶
Dirty working tree
Sync moves commits, so it refuses to run with uncommitted changes:
Commit or stash your changes, then run sync again.
Sync also fails cleanly if a branch has no recorded parent and is not a trunk — git-city has nothing to rebase it onto. Use new, reparent, or insert to give a branch a place in the tree.
All errors print as a single git-city: <message> line on stderr with exit code 1 — never a raw traceback.
After sync¶
Once a feature is synced and reviewed, fold it into its parent with land: land fast-forwards the parent to the feature's tip, re-homes any children, pushes, and keeps the branch (tagged landed; remove it later with prune, or land with --delete). land refuses if the feature is behind its parent — which is exactly the situation sync resolves, so the two commands pair naturally: sync, then land.
→ land