--- name: git-remotes description: > Manage git remote repositories — add/remove/configure remotes, push/pull with safety checks, handle fetch patterns and tracking branch updates, support multi-remote workflows. Use when automating remote operations, pushing with force-push safety, fetching with pruning, pulling with divergence resolution, or managing multi-remote tracking. Include indirect triggers: any git operation that touches a remote, even if the user doesn't explicitly name the remote. Do not use when working with local git history, commits, branches, or staging — use git-history or git-branches instead. metadata: category: git-workflow source_keys: - git-scm-remote-docs - git-scm-fetch-docs - git-scm-push-docs - git-scm-pull-docs - context7-git-htmldocs --- ## Gotchas - **Never force-push `main` or `master`, under any circumstances** — this is a hard refusal, not a `confirm: true` gate. If a force-push targets one of these branches, decline and explain why, regardless of how the request is confirmed. - **Force-push to any other branch requires explicit confirmation** — never execute `git push --force` or `git push -f` without user/agent approval. Always ask or require `confirm: true` flag first. - **`--force-with-lease` alone is not safe** — background processes (IDE plugins, cron jobs) that run `git fetch` silently defeat the protection. Always combine with `--force-if-includes` or use explicit SHA form `--force-with-lease=:`. - **Prune doesn't touch tags by default** — `git fetch --prune` leaves orphaned tags. Use `git fetch --prune --prune-tags` or configure `fetch.pruneTags true` globally. - **Pull with rebase rewrites history** — only safe for unpublished work. Rebasing already-pushed commits breaks everyone downstream. Check what's been pushed before rebasing. - **`git remote show` requires network access** — use `-n` flag for cached data if working offline. `git remote -v` lists URLs without network queries. - **Pull behavior defaults shift between Git versions** — older versions default to merge, newer versions to `--ff-only`. Always set `pull.ff only` explicitly for deterministic behavior. ## Operations ### Remote Management Use these to configure which remotes you push to and pull from: - **Add a remote**: `git remote add ` or `git remote add -f ` to fetch immediately - **Remove a remote**: `git remote remove ` (deletes remote + all tracking refs + config) - **Rename a remote**: `git remote rename ` - **Inspect remotes**: `git remote -v` (show URLs) or `git remote show ` (live tracking status, requires network) - **Set-url separately for fetch vs. push**: `git remote set-url --push ` changes only where pushes go — but 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. Full `set-url` variants (regex-targeted replace, `--add`, `--delete`): `references/remotes.md`. - **Remove a stale URL**: `git remote set-url --delete ` - **Inspect effective URLs**: `git remote get-url ` (shows URL after `insteadOf` rewrites) or `git remote get-url --push --all ` (all push URLs) - **Track only one branch**: `git remote add -t ` (repeatable), or suppress tag import entirely with `git remote add --no-tags ` - **Mirror a remote**: `git remote add --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 ` (add `--dry-run` to preview first) - **Set the remote's default branch pointer**: `git remote set-head -a` (auto-detect, requires a prior fetch), `git remote set-head ` (explicit), or `git remote set-head -d` (delete `refs/remotes//HEAD`) ### Fetch Operations Use these to update your tracking branches without touching your local branches: - **Fetch from one remote**: `git fetch ` — fetches all branches - **Fetch one branch only**: `git fetch ` — stores the result in `FETCH_HEAD`, not a tracking ref - **Fetch from all remotes**: `git fetch --all` with optional `--prune` to clean up stale tracking refs - **Prune properly**: Use `git fetch --all --prune --prune-tags` to clean both branches and tags - **Configure auto-prune**: Set `git config --global fetch.prune true` to auto-prune on every fetch across all remotes (or `remote..prune` to scope it to one remote) - **Shallow clones**: `--depth=` to deepen or create a shallow clone, `--unshallow` to convert to full history, `--update-shallow` to allow the shallow boundary to move. Details and the default fetch refspec: `references/remotes.md`. Fetch never modifies your local branches — it only updates remote-tracking branches (`refs/remotes/origin/*`). ### Push Operations Use these to send your commits upstream. Default: safe push to same-named branch on the remote. - **Basic push**: `git push ` — pushes to same-named remote branch - **Set upstream**: `git push -u ` — push and configure this branch to track the remote - **Multi-remote push**: `git push origin develop` and `git push staging develop` sequentially, or use `git remote set-url --add ` to push to multiple remotes with one command - **Force-push safety**: Always use `git push --force-with-lease --force-if-includes ` over bare `--force`. Require explicit confirmation first — and never for `main`/`master` (see Gotchas). `--force-if-includes` is a no-op without `--force-with-lease`. If background tools (IDE, cron) auto-fetch and could poison the lease check, use a dedicated push-only remote instead — see `references/remotes.md`. - **Server-side enforcement**: `receive.denyDeletes`, `receive.denyDeleteCurrent`, and `receive.denyNonFastForwards` are enforced on the remote regardless of local flags — a hardened server rejects the push even with `--force`. - **Delete remote branch**: `git push --delete ` (not `:` syntax; clearer and cleaner) - **Push everything**: `git push --all` (all local branches) or `git push --tags` (all tags) - **Push a single tag**: `git push origin ` - **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 `+`, e.g. `git push origin +main develop` forces `main` while safe-pushing `develop` Refspec syntax is `[+][:]`: | Pattern | Meaning | |---|---| | `` | Push to same-named remote branch | | `:` | Push `` local ref to `` remote ref | | `+:` | Force this refspec (non-fast-forward allowed) | | `:` | 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/` | ### Pull Operations Use these to fetch and integrate remote changes. Default strategy: `--ff-only` (fail if diverged, forcing a conscious choice). - **Pull with fast-forward only**: `git pull --ff-only` (recommended default — fails if you've diverged, forcing a rebase/merge decision) - **Pull with rebase**: `git pull --rebase` (replays your unpublished commits on top; linear history, but rewrites SHAs — only safe for unpublished work) - **Pull with merge**: `git pull --no-rebase` (three-way merge commit; preserves original commits, non-linear) - **Pull with rebase, preserving merges**: `git pull --rebase=merges` (like `--rebase`, but keeps intentional local merge commits during replay) - **Pull without integrating**: `git pull --squash` collapses incoming commits into staged changes without committing — you write the commit message - **Set pull strategy globally**: `git config pull.ff only` (or `pull.rebase true`; respects branch-specific overrides via `branch..rebase`). Full precedence order (CLI flag > `pull.rebase` > `branch..rebase` > `branch.autoSetupRebase`): `references/remotes.md`. - **Check before rebasing**: Always verify your commits haven't been pushed before using `--rebase`. Rebasing published commits breaks everyone downstream. - **Merge strategy default**: Git 2.34+ defaults to the `ort` merge strategy (`recursive` is now just an alias for it). Strategy options like `-X ours`, `-X theirs`, `-X ignore-space-change` still pass through unchanged. - **Submodules on pull**: `--recurse-submodules` only fetches submodules that are already checked out — newly added submodules are not initialized automatically. Use the `git-submodules` skill to initialize new ones. If pull diverges and you haven't set a strategy, the operation fails — this is good, forces a conscious choice. Never auto-merge diverged branches without asking. ### Return Format (for agents) Return structured output: ```json { "success": true, "operation": "push", "remote": "origin", "branch": "main", "output": "...", "warnings": ["force-with-lease not confirmed"], "recommendations": ["set pull.ff=only globally"] } ``` On failure, include `error` field with root cause and recovery suggestion.