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

@@ -14,4 +14,7 @@ This directory contains provenance metadata and research sources for the `git-re
## Files
- `sources.md` — Extracted research sources and their contributing documents
- `remotes.md` — Full `set-url` variants, shallow-clone/fetch options, default fetch refspec, force-push mitigation detail, server-side deny policies, and pull config precedence
- `remote-config.md` — Remote add/remove/rename/inspect, tracking and mirror options, housekeeping, and the full `set-url` form
- `fetch.md` — Fetch and prune options, shallow and partial fetch, the default fetch refspec
- `push.md` — Push options, refspec syntax, force-push safety in full, server-side deny policies
- `pull.md` — Pull strategies, submodule caveat, the divergence rule, and pull config precedence

View File

@@ -0,0 +1,29 @@
---
topic: fetch
source_keys:
- git-scm-fetch-docs
- context7-git-htmldocs
---
# Fetching
Fetch updates remote-tracking branches (`refs/remotes/<name>/*`) and never modifies a local branch, so it is always safe to run.
- **One remote**: `git fetch <remote>` — all branches
- **One branch**: `git fetch <remote> <branch>` — the result lands in `FETCH_HEAD`, not a tracking ref
- **All remotes**: `git fetch --all`
- **Prune properly**: `git fetch --all --prune --prune-tags` cleans stale branches *and* tags
- **Auto-prune**: `git config --global fetch.prune true` (or `remote.<name>.prune` to scope it to one remote), and `fetch.pruneTags true` for tags
## Shallow and partial fetch
```bash
git fetch --depth=<n> # deepen history, or create a shallow clone
git fetch --unshallow # convert a shallow clone to full history
git fetch --update-shallow # allow the fetch to update the shallow boundary
git fetch --refmap='' <remote> <branch> # fetch without updating any tracking ref (FETCH_HEAD only)
```
## Default fetch refspec
The default is `+refs/heads/*:refs/remotes/<name>/*`. The leading `+` forces the update — remote-tracking branches always mirror the remote exactly and offer no protection for local history.

View File

@@ -0,0 +1,37 @@
---
topic: pull
source_keys:
- git-scm-pull-docs
- context7-git-htmldocs
---
# Pulling
Default strategy: `--ff-only`. It fails on divergence, which forces a conscious choice instead of an accidental merge commit.
- **Fast-forward only**: `git pull --ff-only` — the recommended default
- **Rebase**: `git pull --rebase` replays your commits on top for linear history, but rewrites SHAs. Verify nothing being replayed has been pushed: rebasing published commits breaks everyone downstream.
- **Merge**: `git pull --no-rebase` — three-way merge commit, preserves original commits, non-linear
- **Rebase preserving merges**: `git pull --rebase=merges` keeps intentional local merge commits during the replay
- **Stage without committing**: `git pull --squash` collapses incoming commits into staged changes; you write the message
- **Merge strategy**: Git 2.34+ defaults to `ort` (`recursive` is now an alias for it). Strategy options such as `-X ours`, `-X theirs`, `-X ignore-space-change` pass through unchanged.
- **Submodules**: `--recurse-submodules` only fetches submodules already checked out. Newly added ones are not initialized — use the `git-submodules` skill for those.
## On divergence
A pull that diverges with no strategy configured fails, and that failure is the useful outcome. Report the divergence and the three ways out — `--ff-only`, `--rebase`, `--no-rebase` — and let the caller choose. Auto-merging a diverged branch buries a decision that belongs to the human.
## Config precedence
Highest wins:
1. Command-line flag (`--ff-only` / `--rebase` / `--no-rebase`)
2. `pull.rebase` config (global or local)
3. `branch.<name>.rebase` (branch-specific override)
4. `branch.autoSetupRebase` (set automatically when the tracking branch was created)
```bash
git config pull.ff only # deterministic default across Git versions
git config --global pull.rebase true
git config branch.develop.rebase false # develop always merges, regardless of the global default
```

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`.

View File

@@ -0,0 +1,39 @@
---
topic: remote-config
source_keys:
- git-scm-remote-docs
- context7-git-htmldocs
---
# Remote configuration
Which remotes exist, where they point, and what they track.
`git remote show <name>` needs network access — use `-n` for cached data offline, or `git remote -v`, which lists URLs without querying.
## Add, remove, rename, inspect
- **Add**: `git remote add <name> <url>`, or `-f` to fetch immediately
- **Remove**: `git remote remove <name>` — deletes the remote, all its tracking refs, and its config
- **Rename**: `git remote rename <old> <new>`
- **Inspect**: `git remote -v` (URLs, offline) or `git remote show <name>` (live tracking status)
- **Effective URLs**: `git remote get-url <name>` shows the URL after `insteadOf` rewrites; `git remote get-url --push --all <name>` lists every push URL
## Tracking, mirroring, housekeeping
- **Track one branch**: `git remote add -t <branch> <name> <url>` (repeatable); `--no-tags` suppresses tag import entirely
- **Mirror**: `--mirror=fetch` mirrors all refs locally (bare repos only); `--mirror=push` makes every push behave like `--mirror`
- **Prune stale tracking refs without fetching**: `git remote prune <name>`, with `--dry-run` to preview
- **Default branch pointer**: `git remote set-head <name> -a` (auto-detect, needs a prior fetch), `... <branch>` (explicit), `... -d` (delete `refs/remotes/<name>/HEAD`)
## `set-url` — full form
```bash
git remote set-url <name> <newurl> # replace the first fetch URL
git remote set-url <name> <newurl> <oldurl-regex> # replace only the URL matching regex
git remote set-url --push <name> <url> # change push URL only (must point at same repo)
git remote set-url --add <name> <url> # add an extra push URL (push to multiple remotes)
git remote set-url --delete <name> <regex> # remove URLs matching regex
```
`--push` changes only where pushes go — fetch and push URLs must still reference the same repository. For genuine fetch-from-A / push-to-B workflows, use two separate named remotes instead; `--push` cannot do this.

View File

@@ -1,82 +0,0 @@
---
topic: remotes
source_keys:
- git-scm-remote-docs
- git-scm-fetch-docs
- git-scm-push-docs
- git-scm-pull-docs
---
## `set-url` — full form
```bash
git remote set-url <name> <newurl> # replace the first fetch URL
git remote set-url <name> <newurl> <oldurl-regex> # replace only the URL matching regex
git remote set-url --push <name> <url> # change push URL only (must point at same repo)
git remote set-url --add <name> <url> # add an extra push URL (push to multiple remotes)
git remote set-url --delete <name> <regex> # remove URLs matching regex
```
`--push` changes only where pushes go — fetch and push URLs must still reference the same repository. For genuine fetch-from-A / push-to-B workflows, use two separate named remotes instead.
## Shallow clones and partial fetch
```bash
git fetch <remote> <branch> # fetch one branch only, stored in FETCH_HEAD (not a local/tracking ref)
git fetch --depth=<n> # deepen history, or create a shallow clone
git fetch --unshallow # convert a shallow clone to full history
git fetch --update-shallow # allow the fetch to update the shallow boundary
git fetch --refmap='' <remote> <branch> # fetch without updating any tracking ref (FETCH_HEAD only)
```
## Default fetch refspec
The default fetch refspec is `+refs/heads/*:refs/remotes/<name>/*`. The leading `+` forces the update — remote-tracking branches always mirror the remote exactly and provide no protection for local history. Fetch never touches your local branches, only remote-tracking refs.
## 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 when the `--force-with-lease=<ref>:<sha>` form is used (that form already pins an exact SHA).
Safest combination: `git push --force-with-lease --force-if-includes origin`.
Remote-side policies (`receive.denyDeletes`, `receive.denyDeleteCurrent`, `receive.denyNonFastForwards`) are enforced server-side regardless of any local flag — a server configured this way rejects the push even with `--force`.
## Pull config precedence
Highest wins:
1. Command-line flag (`--ff-only` / `--rebase` / `--no-rebase`)
2. `pull.rebase` config (global or local)
3. `branch.<name>.rebase` (branch-specific override)
4. `branch.autoSetupRebase` (set automatically when the tracking branch was created)
```bash
git config --global pull.rebase true
git config branch.develop.rebase false # develop always merges, regardless of the global default
```

View File

@@ -12,8 +12,7 @@
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md → `## Remote Management (`git remote`)`
**Contributing files:**
- SKILL.md (Remote Management section)
- references/remotes.md (`set-url` full form)
- references/remote-config.md
---
@@ -26,8 +25,8 @@
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md → `## Fetching (`git fetch`)`
**Contributing files:**
- SKILL.md (Fetch Operations section, Gotchas)
- references/remotes.md (shallow clones, default fetch refspec)
- SKILL.md (Gotchas — prune does not touch tags)
- references/fetch.md
---
@@ -40,8 +39,8 @@
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md → `## Pushing (`git push`)`
**Contributing files:**
- SKILL.md (Push Operations section, Gotchas)
- references/remotes.md (force-push safety full detail, server-side deny policies)
- SKILL.md (Gotchas — `--force-with-lease` caveat; Step 1 force-push gate)
- references/push.md
---
@@ -54,8 +53,8 @@
- **Research doc:** plugins/git/docs/research/docs/git/remotes.md → `## Pulling (`git pull`)`
**Contributing files:**
- SKILL.md (Pull Operations section, Gotchas)
- references/remotes.md (pull config precedence)
- SKILL.md (Gotchas — pull default drift)
- references/pull.md (divergence rule; strategies; config precedence)
---
@@ -69,3 +68,7 @@
**Contributing files:**
- SKILL.md (all sections)
- references/remote-config.md
- references/fetch.md
- references/push.md
- references/pull.md