--- topic: push source_keys: - git-scm-push-docs - context7-git-htmldocs --- # Pushing Default: safe push to the same-named branch on the remote. - **Force-push**: never bare `--force`. Use `rtk git push --force-with-lease --force-if-includes `, after the SKILL.md Step 1 gate. - **Basic**: `rtk git push ` - **Set upstream**: `rtk git push -u ` — push and configure tracking - **Multi-remote**: push sequentially (`rtk git push origin develop`, `rtk git push staging develop`), or add a second push URL with `rtk git remote set-url --add ` to reach both in one command - **Delete a remote branch**: `rtk git push --delete ` — clearer than the `:` form - **Bulk**: `rtk git push --all` (all local branches), `rtk git push --tags` (all tags), `rtk git push origin ` (one tag) - **Delete remote branches with no local counterpart**: `rtk git push --prune origin 'refs/heads/*:refs/heads/*'` - **Force only part of a multi-ref push**: prefix the one refspec that needs it with `+` — `rtk git push origin +release develop` forces `release` while safe-pushing `develop`. A `+` prefix is a force-push and passes the SKILL.md Step 1 gate like any other. ## Refspec syntax — `[+][:]` | Pattern | Meaning | |---|---| | `` | Push to same-named remote branch | | `:` | Push `` local ref to `` remote ref | | `+:` | Force this refspec (non-fast-forward allowed) — a force-push; passes the SKILL.md Step 1 gate | | `:` | Delete remote `` | | `refs/heads/*:refs/heads/*` | Glob: push all matching branches | | `^refs/heads/dev-*` | Negative: exclude matching refs | | `tag ` | Sugar for `refs/tags/:refs/tags/` | ## Force-push safety — full detail `--force-with-lease` rejects the push if the remote ref moved since your last fetch. Three forms: | Form | What it protects | |---|---| | `--force-with-lease` (bare) | All refs being pushed, checked against your remote-tracking branch | | `--force-with-lease=` | Named ref only | | `--force-with-lease=:` | Named ref must be at exact SHA — most stable | **Caveat with the bare form:** any background process that runs `git fetch` (IDE plugin, cron job, editor auto-fetch) updates your remote-tracking branch, which can make the lease check pass even though someone else pushed in between. The protection is silently defeated. Two mitigations: ```bash # Option 1 — dedicated push-only remote: background tools fetch `origin`, you push # through a separate remote that nothing else touches, so its tracking ref can't be # poisoned by an unrelated fetch. # The inner `git config` is bare: its stdout becomes a remote URL, so any # output rewriting would poison the remote silently. rtk git remote add origin-push $(git config remote.origin.url) # inner bare per ADR-0023 rtk git push --force-with-lease origin-push # Option 2 — explicit SHA via a local tag, unaffected by tracking-branch state rtk git fetch rtk git tag base master git rebase -i master # bare, not `rtk` (ADR-0023): interactive sequence editor rtk git push --force-with-lease=master:base master:master ``` `--force-if-includes` adds a second check on top of bare `--force-with-lease`: it verifies the remote-tracking tip actually appears in your local branch's reflog, i.e. you genuinely integrated it before rewriting. It is a no-op without `--force-with-lease`, and has no effect with the `--force-with-lease=:` form, which already pins an exact SHA. Safest combination: `rtk git push --force-with-lease --force-if-includes origin`. ## Server-side policy `receive.denyDeletes`, `receive.denyDeleteCurrent` and `receive.denyNonFastForwards` are enforced on the remote regardless of any local flag — a hardened server rejects the push even with `--force`.