Description 582 -> 237 chars, body 1217 -> 290 words. The single remotes.md splits into config, fetch, push, and pull flow files. Restores the confirm: true token to the force-push gate -- it is the git plugin's cross-skill contract, gated on by git-orchestrate and git-branches. Moves push.md's worked example off main, which the skill's own Step 1 refuses, and restores the never-bare---force directive.
68 lines
3.5 KiB
Markdown
68 lines
3.5 KiB
Markdown
---
|
|
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 `git push --force-with-lease --force-if-includes <remote> <branch>`, after the SKILL.md Step 1 gate.
|
|
- **Basic**: `git push <remote> <branch>`
|
|
- **Set upstream**: `git push -u <remote> <branch>` — push and configure tracking
|
|
- **Multi-remote**: push sequentially (`git push origin develop`, `git push staging develop`), or add a second push URL with `git remote set-url --add <name> <url>` to reach both in one command
|
|
- **Delete a remote branch**: `git push <remote> --delete <branch>` — clearer than the `:<branch>` form
|
|
- **Bulk**: `git push --all` (all local branches), `git push --tags` (all tags), `git push origin <tag>` (one tag)
|
|
- **Delete remote branches with no local counterpart**: `git push --prune origin 'refs/heads/*:refs/heads/*'`
|
|
- **Force only part of a multi-ref push**: prefix the one refspec that needs it with `+` — `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 — `[+]<src>[:<dst>]`
|
|
|
|
| Pattern | Meaning |
|
|
|---|---|
|
|
| `<branch>` | Push to same-named remote branch |
|
|
| `<src>:<dst>` | Push `<src>` local ref to `<dst>` remote ref |
|
|
| `+<src>:<dst>` | Force this refspec (non-fast-forward allowed) — a force-push; passes the SKILL.md Step 1 gate |
|
|
| `:<branch>` | Delete remote `<branch>` |
|
|
| `refs/heads/*:refs/heads/*` | Glob: push all matching branches |
|
|
| `^refs/heads/dev-*` | Negative: exclude matching refs |
|
|
| `tag <name>` | Sugar for `refs/tags/<name>:refs/tags/<name>` |
|
|
|
|
## 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=<refname>` | Named ref only |
|
|
| `--force-with-lease=<refname>:<sha>` | 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.
|
|
git remote add origin-push $(git config remote.origin.url)
|
|
git push --force-with-lease origin-push
|
|
|
|
# Option 2 — explicit SHA via a local tag, unaffected by tracking-branch state
|
|
git fetch
|
|
git tag base master
|
|
git rebase -i master
|
|
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=<ref>:<sha>` form, which already pins an exact SHA.
|
|
|
|
Safest combination: `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`.
|