refactor(git-remotes): retrofit to the ADR-0020 context contract

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.
This commit is contained in:
2026-08-30 13:10:53 +00:00
parent 3dd5387671
commit 38eb0745b7
18 changed files with 440 additions and 356 deletions

View File

@@ -0,0 +1,67 @@
---
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`.