refactor(skills): retrofit the corpus to the ADR-0020 context contract (#129)
Retrofits all 39 skills to ADR-0020's description/body context contract, then fixes what six rounds of independent review found in that retrofit — including four ways the hot gate itself failed open. Closes #99, #107, #108, #110, #111, #114, #115, #120. ## The retrofit (waves 1-5) | | Start | Now | |---|---|---| | Description FAILs (>400 chars) | 26 | **0** | | Body FAILs (>900 words, body-only) | 9 | **0** | | Dangling routing targets | 2 | **0** | | `Kyberforge.CompositionNote` | 10 | **0** | | Preload tax | 21,005 chars | **~10,500** | Under the 12,000-char success criterion. Per-wave detail is on #99. ## The review fixes **The gate failed open four ways, three of them found after the retrofit shipped.** An unrecognised follower token made a dangling target vanish. A skill directory with no `SKILL.md` resolved as a valid target, so a commit could be green locally and red in a fresh clone — three existing fixtures were relying on that, one of which made the install-leak A/B pass vacuously. Then the free-standing `/name` sweep turned out to be gated on the sentence carrying a boundary marker, so route notation in any other sentence was invisible — not an ERROR, not a SUGGESTION, not an INFO — which left the documented "`/name` always blocks" promise false from a second direction. All four fixed and pinned. **Two checks were silently not running.** `validate-provenance.sh` checks 7-8 were dead across nine skills. Waking them exposed a deeper problem: they assume `Research doc:` names a source index, but 30 of 121 entries point at topic content documents, so every new check-7 INFO was a false positive and check 8 was saved from a false-FAIL flood only by an *unannounced* skip. Checks 7/8 are now scoped to source indexes and every skip announces itself (#121). **The retrofit's own anti-goal, four times.** ADR-0020 warns that a blunt gate gets satisfied by deleting content rather than relocating it. `diagnose` and `skill-audit` relocated prose and then read it unconditionally; `prototype` and `vale-config` deleted rules outright that survived nowhere. All four addressed. ## Verification - `bash tests/run-tests.sh --strict` — 24 suites, 0 skipped, 0 failed - `bash tests/run-bats.sh` — 325 tests, 0 failures - `pre-commit run --all-files` — 17/17 - `pre-commit run --hook-stage pre-push --all-files` — 16/16, with `apm marketplace check` and `apm pack --check-clean` run against the remote, not skipped - `scripts/skill-size-check.sh` over all 39 skills — rc 0, 0 ERROR/FAIL, SUGGESTION-only - Preload tax measured at **10,498 chars**, max description 390 — both inside budget - Every new test proven non-vacuous by a deliberate mutation of the behaviour it covers **Per-commit sync, stated accurately:** the ten commits from the latest review round each pass `check-plugin-content-sync` in isolation, verified by checking each out in a detached worktree with a clean between. The earlier gitea window (`dfacf05..bedbd1d`, nine commits) does **not** — its mirror was regenerated in one batch at `bbc7300`. An earlier revision of this description claimed the property held for every commit; it does not, and a bisect through that window lands on a red commit. **Squash-merge** to collapse it, or accept that this range is not bisectable. ## Version bump Six plugins and the catalog take a **patch**, not a minor. The branch is **89 commits — 40 `fix` / 30 `refactor` / 12 `docs` / 5 `chore` / 2 `test` — zero `feat`, zero `!`, zero `BREAKING CHANGE`** — and adds no skill, agent, command or hook. (Two earlier revisions of this section cited a stale histogram, most recently 78 commits; the figures above are measured at HEAD.) Both rules this repo ships (`forge/references/version-bump.md`, landing in this PR, and `git-commits/references/conventional-commits-spec.md`) make that a patch, and the catalog set is unchanged at 7 entries. Not settled by that: four published files were removed from the installed tree, three moved, and `caveman` gained `disable-model-invocation`, retiring its old triggers. Under a strict reading those are major-class and currently ship under `refactor:` with no marker. Whether the deployed skill surface is a public contract is written down nowhere — worth deciding, but it outlives this PR. ## Deliberately not in scope #112 (cherry-pick ownership, now resolved in favour of `git-commits`), #113 (`rtk git` normalisation), #116 (research fan-out), #101 (audit-skill merge), #122 (non-spec skill-root files), #123 (no PRD producer) stay open. #117 is the one worth reading: the contract's remedy is to move prose into `references/`, which is exactly where neither the size gate nor Vale looks — and the blind spot is wider than #117 currently records, since there is no root `.vale.ini` at all, so every ADR, `CONTEXT.md` and `README.md` is unlinted too. That blind spot let this branch carry two `level: error` `Kyberforge.SentenceOpenerThereIs` violations into `references/` files it created — `provider-adapter-author/references/provider-matrix.md:31` and `agent-audit/references/finding-criteria.md:95`. Both are reworded in `afadaae`, confirmed by routing each file through the audit's own `vale-wrap.sh` (1 error each before, 0 after). Five further occurrences sit in `references/` files already on `main`; those are the pre-existing corpus and stay with #117, which is the real fix. Also unfixed and not this PR's: `apm install` appends a duplicate `SessionStart` entry to `.claude/settings.json`, so a fresh clone cannot get pre-push green without an edit AGENTS.md warns against. Reproduces identically on `main`. Co-authored-by: Defame1297 <gitea@rkdr.net> Reviewed-on: https://git.dev.rkdr.net/Defame1297/holocron/pulls/129 Co-authored-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net> Co-committed-by: Claude Code AI - Gitea MCP <claude@noreply.git.dev.rkdr.net>
This commit was merged in pull request #129.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# git-branches
|
||||
|
||||
Manage the full lifecycle of git branches — create, switch, delete, rename, and track feature/hotfix/release branches under GitHub Flow or Gitflow.
|
||||
Manage the full lifecycle of git branches — create, switch, delete, rename, track, merge, and compare feature/hotfix/release branches under GitHub Flow or Gitflow.
|
||||
|
||||
## What it does
|
||||
|
||||
@@ -12,11 +12,23 @@ This skill handles branch operations within the git workflow suite. It creates b
|
||||
/git-branches
|
||||
```
|
||||
|
||||
Describe your branch task: create a feature/hotfix/release branch, switch, delete, rename, or track. The skill will determine the branching pattern (GitHub Flow or Gitflow) from config or repo state and handle safety checks for destructive operations.
|
||||
Describe your branch task: create a feature/hotfix/release branch, switch, delete, rename, track, merge, or compare two branches. The skill will determine the branching pattern (GitHub Flow or Gitflow) from config or repo state and handle safety checks for destructive operations.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `references/branch-patterns.md` | Loaded when a branch's base, name prefix, or merge rule depends on GitHub Flow vs. Gitflow |
|
||||
| `references/branch-operations.md` | Loaded when running a create/switch/delete/rename/track/list/stash action, or resolving `get-intent` |
|
||||
| `references/merging.md` | Loaded when merging one branch into another or resolving merge conflicts |
|
||||
| `references/comparing-branches.md` | Loaded when comparing two branches or finding where they diverged |
|
||||
| `references/orchestrator-contract.md` | Loaded when `git-orchestrate` or another calling agent supplies a structured request rather than prose |
|
||||
| `references/sources.md` | Research sources backing the branching/gitflow guidance |
|
||||
|
||||
## Composition
|
||||
|
||||
`git-orchestrate` calls this skill for the branch step of a multi-step workflow and parses its
|
||||
structured result. Revert is `git-history`'s; commit authoring, rebase, reset and cherry-pick are
|
||||
`git-commits`'; deleting a remote branch is `git-remotes`'; branch operations against a
|
||||
Gitea-hosted remote are `gitea-branches`'.
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
name: git-branches
|
||||
|
||||
description: >
|
||||
Use when managing the full lifecycle of git branches: create feature/hotfix/release branches
|
||||
(gitflow, GitHub Flow, or custom patterns from config), switch, delete, rename, and track branches,
|
||||
or retrieve branch intent metadata. Handles branch protection safety checks and returns structured
|
||||
results for agent composition. Use even if the user doesn't explicitly mention branch names — they
|
||||
may be asking about "fixing something" or "shipping a feature", which implicitly requires branch
|
||||
management. Do not use when the user needs only commit operations (use git-commits) or history
|
||||
inspection (use git-history).
|
||||
Use when creating, switching, deleting, renaming, tracking, merging, or comparing
|
||||
local git branches under GitHub Flow or Gitflow.
|
||||
Not writing or rewriting commits -> `git-commits`.
|
||||
Not history inspection -> `git-history`.
|
||||
Not a Gitea remote's branches -> `gitea-branches`.
|
||||
|
||||
metadata:
|
||||
category: git
|
||||
@@ -21,92 +19,57 @@ metadata:
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Branches are cheap; deletion is cheap but risky.** Deleting one requires checking if commits on it are reachable elsewhere; always confirm before deleting, as it may lose unmerged work.
|
||||
- **Uncommitted changes can block branch switches.** `git switch` aborts if local modifications conflict with the target branch. Offer to stash changes before switching when this happens, don't force a checkout.
|
||||
- **Tracking relationships matter for coordination.** Agents pushing on behalf of users should always set tracking (`-u origin <branch>`) so later pushes/pulls know the target. Without it, commands fail or target the wrong remote branch.
|
||||
- **Gitflow vs. GitHub Flow are not compatible.** Gitflow requires `develop` and `release/*` branches with `--no-ff` merges; GitHub Flow uses only `main` and feature branches with fast-forward. Read the repo's config or ask the orchestrator which pattern to use — don't guess.
|
||||
- **Naming collisions with tags.** A branch and tag can have the same name. Prefer `git switch` over `git checkout` for branch operations — verify which ref you're targeting with `git branch --list <name>` / `git tag --list <name>` if the name could be ambiguous, and disambiguate explicitly with `refs/heads/<name>` (branch) or `refs/tags/<name>` (tag) where a command accepts either.
|
||||
- **Never force-push `main` or `master`.** This is a hard refusal, not a confirmation gate — it applies even if the caller passes `confirm: true`. Deleting or renaming `main`/`master` in a way that would require a force-push to reconcile the remote (e.g. force-deleting and recreating it, or renaming it out from under in-flight work) must be rejected outright; explain why and suggest a non-destructive alternative (e.g. a new branch) instead of proceeding.
|
||||
- **Uncommitted changes abort a switch.** `git switch` refuses rather than clobbering conflicting local edits. Offer to stash and retry — forcing the checkout past it is how work disappears.
|
||||
- **A branch and a tag can carry the same name.** Detect it before acting — `git branch --list <name>` and `git tag --list <name>`; output from both means the name is ambiguous. Prefer `git switch` over `git checkout`, and where a command accepts either ref, disambiguate with `refs/heads/<name>` or `refs/tags/<name>`.
|
||||
- **`main` and `master` are a refusal, not a gate.** Force-pushing, force-deleting, or renaming them is rejected even when the caller passes `confirm: true` — no flag makes the remote's history recoverable. Offer a new branch instead.
|
||||
|
||||
## Branch Patterns
|
||||
## Step 1 — Determine the branching pattern
|
||||
|
||||
Default to **GitHub Flow** (simpler, modern, CI/CD-friendly). Fall back to **Gitflow** only if the repo's config specifies it or the branch structure shows it in use (presence of `develop` or release branches).
|
||||
Read `branching_pattern` from the git plugin config (`.claude/plugins/git/config.json`; the plugin root's `config.example.json` shows the shape). Default: `github-flow`. With no config, infer Gitflow from the presence of a `develop` or `release/*` branch, and GitHub Flow otherwise.
|
||||
|
||||
**GitHub Flow:**
|
||||
- Base: `main`
|
||||
- Feature branches: `feature/<feature-name>` or `fix/<bug-name>`
|
||||
- Merge: fast-forward when possible (preserves linear history)
|
||||
- Delete after merge
|
||||
The two patterns are not mixable, and the wrong merge rule silently damages history. If the action touches a base branch, a name prefix, or a merge rule, read `references/branch-patterns.md`.
|
||||
|
||||
**Gitflow:**
|
||||
- Base: `main` (production) + `develop` (integration)
|
||||
- Feature branches: `feature/<feature-name>` (from `develop`)
|
||||
- Release branches: `release/X.Y.Z` (from `develop`, merged to `main` + `develop`)
|
||||
- Hotfix branches: `hotfix/X.Y.Z` (from `main`, merged to `main` + `develop`)
|
||||
- Merge: always use `--no-ff` to preserve branch structure
|
||||
## Step 2 — Dispatch on the action
|
||||
|
||||
## Workflow
|
||||
| Action | Reference |
|
||||
|---|---|
|
||||
| create, switch, delete, rename, track, list, get-intent, stash | `references/branch-operations.md` |
|
||||
| merge a branch, resolve merge conflicts | `references/merging.md` |
|
||||
| compare two branches, find their divergence | `references/comparing-branches.md` |
|
||||
|
||||
- [ ] **Determine pattern:** Check git plugin config (`.claude/plugins/git/config.json`, if present — see `config.example.json` in the plugin root for the expected shape) for `branching_pattern` (default: `github-flow`). If not set, inspect repo for `develop` branch or `release/*` branches; if present, assume Gitflow.
|
||||
- [ ] **Create branch:** Use `git switch -c <branch> <base>`. Base defaults to config's `base_branch` (usually `main` or `develop`). Include intent metadata in branch name or return as structured result (e.g., `{ "branch": "feature/x", "intent": "implement feature X" }`).
|
||||
- [ ] **Track remote:** If pushing, always use `git push -u origin <branch>` to establish tracking.
|
||||
- [ ] **Safety checks before destructive ops:** Before delete/force-push/rebase with history loss, check: (1) Is this branch tracking a remote? Warn if yes. (2) Are there unpushed commits? Warn if yes. (3) Does the orchestrator call include `confirm: true`? Fail if not. For humans, prompt interactively.
|
||||
- [ ] **Return structured results:** Always return branch operations as JSON or structured text: `{ "action": "create", "branch": "feature/x", "base": "main", "tracking": "origin/feature/x", "intent": "implement feature X" }`. Agents need to parse this for subsequent operations.
|
||||
- [ ] **Retrieve intent (`get-intent`):** Git has no native field for free-text branch metadata — this skill doesn't persist it. On `create`, the `intent` value is only ever returned in the structured result; the caller (orchestrator or agent) is responsible for storing it if it needs to be looked up later. On `get-intent`, either parse it back out of the branch name convention (`feature/<intent-slug>`) or return `{ "intent": null }` if the caller never persisted the original create-time value — don't fabricate an intent.
|
||||
Load only the file the action needs. A destructive action still passes Step 3 first.
|
||||
|
||||
### Command mapping for each action
|
||||
## Step 3 — Gate destructive operations
|
||||
|
||||
- **delete:** `git branch -d <branch>` refuses if the branch has unmerged commits — prefer this by default. `git branch -D <branch>` forces deletion and discards unmerged work; only use it after the safety checks above pass and `confirm: true` is set. For a remote branch: `git push origin --delete <branch>`.
|
||||
- **rename:** `git branch -m <old> <new>`.
|
||||
- **list:** `git branch` (local only), `git branch -a` (all local + remote-tracking), `git branch -r` (remote-tracking only), `git branch --merged`/`--no-merged` (filter by merge status into current branch).
|
||||
- **get-intent:** No git command — see Workflow step "Retrieve intent" for how this is resolved.
|
||||
- **track (existing branch):** `git branch --set-upstream-to=origin/<branch>` sets tracking without a push; `git branch -vv` shows tracking state for all local branches.
|
||||
- **switch (existing branch):** `git switch <branch>` — switches to an existing local branch (aborts on conflicting local changes, see Gotchas). `git switch -` switches back to the previously checked-out branch.
|
||||
Before any delete or force-delete that loses history:
|
||||
|
||||
## Merging
|
||||
- [ ] Does the branch track a remote? Warn if so.
|
||||
- [ ] Are there unpushed commits on it? Warn if so.
|
||||
- [ ] Did the caller pass `confirm: true`? Fail if not — for a human caller, prompt interactively instead of failing.
|
||||
|
||||
Scope: fast-forward/merge-commit mechanics and conflict resolution only. Rebase, cherry-pick, and revert belong to `git-history`.
|
||||
These gates are passable. The `main`/`master` refusal in Gotchas is not.
|
||||
|
||||
- **Fast-forward:** `git merge <branch>` — advances the pointer with no merge commit if the target hasn't diverged.
|
||||
- **True merge:** `git merge --no-ff <branch>` — forces a merge commit even when fast-forward is possible; required by Gitflow on all supporting-branch merges.
|
||||
- **Squash merge:** `git merge --squash <branch>` stages the combined diff without committing; follow with a manual `git commit`.
|
||||
- **Octopus merge:** `git merge branch-a branch-b branch-c` merges more than two branches at once; fails outright on any conflict, so use sequential two-way merges if conflicts are expected.
|
||||
## Step 4 — Set tracking
|
||||
|
||||
**Conflict resolution:** when Git can't auto-merge, it inserts conflict markers and stops. Run `git status` to find conflicted files, edit them to resolve the markers, then `git add <file>` and `git merge --continue`. `git merge --abort` reverts to the pre-merge state. `git mergetool` opens the configured merge tool; `git diff --diff-filter=U` shows only conflicted files.
|
||||
A new branch's first push must be `git push -u origin <branch>`. The push itself is `git-remotes`' — every remote-side gate lives there, which is why Step 2's remote-delete row hands off the same way — but the upstream requirement originates here, so carry it in the handoff. Without an upstream, later pushes and pulls either fail or silently target the wrong remote branch, and the caller has no way to tell which happened.
|
||||
|
||||
## Comparing Branches
|
||||
## Step 5 — Return a structured result
|
||||
|
||||
- `git log main..feature` — commits in `feature` not in `main`.
|
||||
- `git log feature..main` — commits in `main` not in `feature` (reverse direction).
|
||||
- `git log --left-right main...feature` — both diverging sets (symmetric diff).
|
||||
- `git diff main...feature` — diff from the common ancestor to `feature`'s tip.
|
||||
- `git merge-base main feature` — print the common ancestor commit.
|
||||
Return every operation in this shape rather than prose, including failures — a calling agent chains its next operation on the result and cannot parse a sentence.
|
||||
|
||||
## Integration with Orchestrator
|
||||
|
||||
When invoked by `git-orchestrate`, accept requests in the form:
|
||||
```json
|
||||
{
|
||||
"action": "create|switch|delete|rename|track|list|get-intent",
|
||||
"branch": "<branch-name>",
|
||||
"base": "<base-branch (optional, defaults to config)>",
|
||||
"intent": "<human-readable intent (optional)>",
|
||||
"confirm": "<true for destructive ops, omit for read ops>"
|
||||
}
|
||||
```
|
||||
|
||||
Return results as:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"action": "create|switch|...",
|
||||
"action": "create|switch|delete|rename|track|list|get-intent",
|
||||
"branch": "<name>",
|
||||
"message": "descriptive message",
|
||||
"intent": "<intent if tracked>",
|
||||
"tracking": "origin/<branch (if set)>",
|
||||
"tracking": "origin/<branch, if set>",
|
||||
"error": "<error message if success=false>",
|
||||
"suggestion": "<recovery suggestion if applicable>"
|
||||
}
|
||||
```
|
||||
|
||||
If error is due to uncommitted changes, include `{ "suggestion": "stash changes and retry" }` so the orchestrator can offer automatic recovery.
|
||||
When the failure is uncommitted local changes, set `"suggestion": "stash changes and retry"` so the caller can offer recovery rather than surfacing a dead end.
|
||||
|
||||
When a calling agent supplies a structured request rather than prose, read `references/orchestrator-contract.md` for the request schema.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-git-htmldocs
|
||||
---
|
||||
|
||||
# Per-action command mapping
|
||||
|
||||
One command per action. Where two forms exist, the first is the default and the second the escape
|
||||
hatch.
|
||||
|
||||
- **create** — `git switch -c <branch> <base>`. Base comes from the config's `base_branch`
|
||||
(`main` under GitHub Flow, usually `develop` under Gitflow).
|
||||
- **switch** — `git switch <branch>` moves to an existing local branch; it aborts rather than
|
||||
clobbering conflicting local changes. `git switch -` returns to the previous branch.
|
||||
- **delete (local)** — `git branch -d <branch>` refuses when the branch holds unmerged commits,
|
||||
which is why it is the default. `git branch -D <branch>` forces the deletion and discards that
|
||||
work — only after the destructive-operation gates pass and `confirm: true` is set.
|
||||
- **delete (remote)** — not this skill's. Deleting a remote branch is a push, and every remote-side
|
||||
gate lives in `git-remotes`; hand it there rather than running the push from here. Its
|
||||
`references/push.md` carries the command and the refspec form.
|
||||
- **rename** — `git branch -m <old> <new>`.
|
||||
- **list** — `git branch` (local), `-a` (local plus remote-tracking), `-r` (remote-tracking only),
|
||||
`--merged` / `--no-merged` (filter by merge status into the current branch).
|
||||
- **track** — `git branch --set-upstream-to=origin/<branch>` sets an upstream without pushing.
|
||||
`git branch -vv` shows the tracking state of every local branch.
|
||||
|
||||
## get-intent
|
||||
|
||||
Git has no native field for free-text branch metadata, and this skill does not persist any. On
|
||||
`create`, the `intent` value is only returned in the structured result — the caller decides
|
||||
whether to store it.
|
||||
|
||||
On `get-intent`, either parse the intent back out of the branch-name convention
|
||||
(`feature/<intent-slug>`) or return `{ "intent": null }` when the caller never persisted the
|
||||
create-time value. Never fabricate an intent: a downstream commit message built on a guessed
|
||||
intent is worse than one built on none.
|
||||
|
||||
## Stashing work in progress
|
||||
|
||||
A switch aborts rather than clobbering conflicting local changes (see Gotchas). Stash is the way
|
||||
past it: it shelves the working tree and index so the branch pointer can move.
|
||||
|
||||
- **save** — `git stash push -m "<message>"`. Add `-u` to include untracked files; verified on Git
|
||||
2.39.5, a plain `push` leaves them in place, and a plain `push` with *only* untracked changes
|
||||
reports `No local changes to save` and stashes nothing. Bare `git stash` is `push` with no message.
|
||||
- **restore** — `git stash pop` applies the newest entry and deletes it. `git stash apply stash@{n}`
|
||||
applies without deleting, for replaying one shelf onto more than one branch.
|
||||
- **list** — `git stash list`; `git stash show -p stash@{n}` prints that entry's diff.
|
||||
- **drop** — `git stash drop stash@{n}` deletes one entry. `git stash clear` deletes all of them
|
||||
and nothing recovers them — confirm before running it.
|
||||
- **branch from a stash** — `git stash branch <branch> stash@{n}` creates a branch at the commit the
|
||||
stash was taken from and pops it there. Use it when the stash no longer applies to the current tip.
|
||||
|
||||
**A conflicting `pop` keeps the entry.** Verified on 2.39.5: it exits 1, writes conflict markers,
|
||||
prints "The stash entry is kept in case you need it again", and `git stash list` still shows it.
|
||||
Resolve, `git add`, then `git stash drop` the entry by hand — otherwise it silently accumulates.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
source_keys:
|
||||
- nvie-gitflow-post
|
||||
- atlassian-gitflow-tutorial
|
||||
- gitflow-cheatsheet
|
||||
---
|
||||
|
||||
# Branch patterns
|
||||
|
||||
Which pattern is in play decides the base branch, the branch name prefix, and whether merges are
|
||||
allowed to fast-forward. Default to GitHub Flow — simpler, and what CI/CD-oriented repos expect.
|
||||
Fall back to Gitflow only when the config says so or the repo already carries `develop` or
|
||||
`release/*` branches.
|
||||
|
||||
## GitHub Flow
|
||||
|
||||
- Base: `main`
|
||||
- Feature branches: `feature/<feature-name>` or `fix/<bug-name>`
|
||||
- Merge: fast-forward where possible, to keep history linear
|
||||
- Delete the branch after merge
|
||||
|
||||
## Gitflow
|
||||
|
||||
- Base: `main` (production) plus `develop` (integration)
|
||||
- Feature branches: `feature/<feature-name>`, cut from `develop`
|
||||
- Release branches: `release/X.Y.Z`, cut from `develop`, merged to both `main` and `develop`
|
||||
- Hotfix branches: `hotfix/X.Y.Z`, cut from `main`, merged to both `main` and `develop`
|
||||
- Merge: always `--no-ff`, so the branch structure survives in the history
|
||||
|
||||
The two are not mixable. A `--no-ff` merge into a GitHub Flow repo leaves merge commits nobody
|
||||
expects; a fast-forward merge of a Gitflow release branch erases the release boundary.
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-git-htmldocs
|
||||
---
|
||||
|
||||
# Comparing two branches
|
||||
|
||||
The two-dot and three-dot forms mean different things and are easy to swap by accident — check the
|
||||
direction before reporting a result.
|
||||
|
||||
- `git log main..feature` — commits on `feature` that are not on `main`.
|
||||
- `git log feature..main` — the reverse direction: commits on `main` not on `feature`.
|
||||
- `git log --left-right main...feature` — both diverging sets at once (symmetric difference).
|
||||
- `git diff main...feature` — the diff from the common ancestor to `feature`'s tip, which is what
|
||||
a reviewer sees, rather than the diff between the two tips.
|
||||
- `git merge-base main feature` — print the common ancestor commit.
|
||||
30
plugins/git/.apm/skills/git-branches/references/merging.md
Normal file
30
plugins/git/.apm/skills/git-branches/references/merging.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-git-htmldocs
|
||||
- atlassian-gitflow-tutorial
|
||||
---
|
||||
|
||||
# Merging one branch into another
|
||||
|
||||
Scope is fast-forward and merge-commit mechanics plus conflict resolution. Rebase and cherry-pick
|
||||
belong to `git-commits`; revert to `git-history`.
|
||||
|
||||
- **Fast-forward** — `git merge <branch>` advances the pointer with no merge commit when the
|
||||
target has not diverged.
|
||||
- **True merge** — `git merge --no-ff <branch>` forces a merge commit even when a fast-forward is
|
||||
possible. Gitflow requires it on every supporting-branch merge.
|
||||
- **Squash merge** — `git merge --squash <branch>` stages the combined diff without committing.
|
||||
Follow it with a `git commit`.
|
||||
- **Octopus merge** — `git merge branch-a branch-b branch-c` merges more than two branches at
|
||||
once, but fails outright on any conflict. Use sequential two-way merges when conflicts are
|
||||
likely.
|
||||
|
||||
## Conflict resolution
|
||||
|
||||
When Git cannot auto-merge it writes conflict markers and stops mid-merge. Run `git status` to
|
||||
list the conflicted files, edit each to resolve its markers, then `git add <file>` and
|
||||
`git merge --continue`.
|
||||
|
||||
- `git merge --abort` restores the pre-merge state.
|
||||
- `git mergetool` opens the configured merge tool.
|
||||
- `git diff --diff-filter=U` shows only the still-conflicted files.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
source_keys: []
|
||||
---
|
||||
|
||||
# Orchestrator request contract
|
||||
|
||||
`git-orchestrate` and other calling agents send this shape. The result shape they parse back is in
|
||||
`SKILL.md` Step 5, because every run emits one.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "create|switch|delete|rename|track|list|get-intent",
|
||||
"branch": "<branch-name>",
|
||||
"base": "<base branch, optional, defaults to config>",
|
||||
"intent": "<human-readable intent, optional>",
|
||||
"confirm": "<true for destructive ops, omit for read ops>"
|
||||
}
|
||||
```
|
||||
@@ -12,7 +12,8 @@
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/gitflow.md (whole-document reference)
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Branch Patterns — Gitflow vs. GitHub Flow structure and defaults)
|
||||
- SKILL.md (Gitflow vs. GitHub Flow inference and the not-mixable rule)
|
||||
- references/branch-patterns.md (Gitflow vs. GitHub Flow structure, defaults, and why the two are not mixable)
|
||||
|
||||
## atlassian-gitflow-tutorial
|
||||
|
||||
@@ -23,7 +24,9 @@
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/gitflow.md (whole-document reference)
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Branch Patterns — Gitflow branch types, base/merge targets, `--no-ff` requirement)
|
||||
- SKILL.md (Gitflow vs. GitHub Flow inference and the not-mixable rule)
|
||||
- references/branch-patterns.md (Gitflow branch types, base/merge targets, `--no-ff` requirement)
|
||||
- references/merging.md (`--no-ff` requirement on Gitflow supporting-branch merges)
|
||||
|
||||
## gitflow-cheatsheet
|
||||
|
||||
@@ -34,7 +37,7 @@
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/gitflow.md (whole-document reference)
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Branch Patterns — feature/release/hotfix naming conventions)
|
||||
- references/branch-patterns.md (feature/release/hotfix naming conventions)
|
||||
|
||||
## context7-git-htmldocs
|
||||
|
||||
@@ -45,4 +48,7 @@
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/branching-merging.md (whole-document reference)
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Command mapping, Merging, Comparing Branches — `git switch`/`git branch`/`git merge`/`git log`/`git diff`/`git merge-base` command vocabulary and flags)
|
||||
- SKILL.md (Gotchas — `git switch` abort-on-conflict behaviour, branch/tag name ambiguity)
|
||||
- references/branch-operations.md (`git switch`/`git branch` command vocabulary and flags)
|
||||
- references/merging.md (`git merge` strategies and conflict-resolution commands)
|
||||
- references/comparing-branches.md (`git log`/`git diff`/`git merge-base` range syntax)
|
||||
|
||||
@@ -16,9 +16,16 @@ Describe your commit task: create a new commit, amend, squash, or cherry-pick. T
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `references/conventional-commits-spec.md` | Full Conventional Commits specification |
|
||||
| `references/commit-template.md` | Why / Implementation Notes / Impact body structure and full trailer list |
|
||||
| File | Loaded when |
|
||||
|------|-------------|
|
||||
| `SKILL.md` | Always — gotchas, the flow dispatch table, the gates common to every flow, and the output shape |
|
||||
| `references/create-commit.md` | Composing a new commit from staged changes |
|
||||
| `references/rewrite-history.md` | Amending, squashing, or folding a `fixup!`/`squash!` commit into an earlier one |
|
||||
| `references/cherry-pick.md` | Replaying an existing commit onto the current branch |
|
||||
| `references/conventional-commits-spec.md` | A type, footer, or breaking-change edge case is not obvious — full spec, 11-type set, commitlint constraint table |
|
||||
| `references/commit-template.md` | Writing a body for a non-trivial commit — Why / Implementation Notes / Impact structure and the full trailer list |
|
||||
| `references/sources.md` | Research sources and provenance |
|
||||
|
||||
## Composition
|
||||
|
||||
Part of the git plugin's domain suite. This skill owns commit authoring and history-rewriting operations only; `git-history` inspects history, `git-branches` owns branch lifecycle, and `git-workflow` is the conversational entry point that routes between them.
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
name: git-commits
|
||||
|
||||
description: >
|
||||
Use when creating, amending, squashing, or cherry-picking commits.
|
||||
Generates well-formatted commit messages following Conventional Commits spec (type, scope, description, body, footers).
|
||||
Validates against commitlint config-conventional constraints (header max 100 chars, lowercase subject, no trailing periods, type must be one of 11 standard types).
|
||||
Communicates SemVer impact (MAJOR for breaking changes, MINOR for features, PATCH for fixes).
|
||||
Handles confirmation gates for history-altering operations (amend, rebase, squash).
|
||||
Provides interactive guidance for humans, structured JSON output for agents.
|
||||
Do not use for: inspecting git history, branch management, or repository state inspection — those are separate skills.
|
||||
Use when creating, amending, squashing, or cherry-picking commits, including
|
||||
writing and validating the Conventional Commits message.
|
||||
Not history inspection -> `git-history`.
|
||||
Not branch lifecycle -> `git-branches`.
|
||||
|
||||
metadata:
|
||||
version: "0.1.2"
|
||||
version: "0.1.3"
|
||||
category: git
|
||||
source_keys:
|
||||
- conventional-commits-spec
|
||||
@@ -24,92 +21,56 @@ allowed-tools: Bash
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Type must be one of 11 standard types** — `feat`, `fix`, `perf`, `revert`, `docs`, `style`, `refactor`, `test`, `build`, `ci`, `chore`. Non-standard types will fail commitlint validation. Note: the Conventional Commits spec itself only mandates `feat`/`fix` — the 11-type set is a commitlint/Angular convention this skill validates against, not a spec requirement.
|
||||
- **Scope is optional but should be used** — helps identify which part of the system changed. Examples: `api`, `db`, `cli`, `config`.
|
||||
- **Header max 100 characters** — type + scope + colon + description must fit. If longer, move detail to body.
|
||||
- **BREAKING CHANGE notation** — use `!` before the colon (`feat!: drop Node 6`) for visibility in `git log --oneline`. Footer notation (`BREAKING CHANGE: ...`) is machine-readable but hidden in log.
|
||||
- **SemVer mapping is not optional** — agents must communicate: `feat` → MINOR bump, `fix`/`perf`/`revert` → PATCH, any with breaking change → MAJOR.
|
||||
- **Confirmation gates are mandatory for destructive operations** — amend, rebase, squash require explicit user/agent approval before execution.
|
||||
- **Never skip hooks with `--no-verify`** — hooks are the automated QA gate; bypassing them breaks the pipeline. Do not add this flag to any commit command unless the user explicitly demands it, and warn them if they do.
|
||||
- **Never force-push `main`/`master`** — even after an amend or interactive rebase, refuse to force-push a protected branch (`main`, `master`) and explain why; force-push is only safe on branches no one else has based work on.
|
||||
- **Command examples use the `rtk git` wrapper** — this org's convention routes all git invocations through `rtk git <subcommand>` instead of bare `git <subcommand>`. Follow this prefix in any command you actually run.
|
||||
- **Never commit secrets, credentials, or environment-specific config** — if staged changes contain what looks like an API key, token, password, or connection string, stop and flag it before committing rather than committing it.
|
||||
- **Commits must be atomic and leave the repo working** — each commit should be one logical, independently reviewable and reversible change, and should leave the repository in a buildable/testable state. If staged changes bundle unrelated work, suggest splitting before committing.
|
||||
- **Commit messages explain why, not what** — the diff already shows what changed; the message's job is to capture context the diff can't (motivation, root cause, tradeoffs). See `references/commit-template.md` for the structure this maps to.
|
||||
- **Run git as `rtk git <subcommand>`, never bare `git`** — org convention, in `&&` chains too.
|
||||
- **Refuse to force-push `main`/`master`** — a rewrite leaves the branch diverged and the reflex is to force it back; safe only where nobody else has based work on it.
|
||||
- **`reset --hard` is a confirmation gate, not a default.** It overwrites the working tree, and uncommitted edits it discards were never in git, so no reflog recovers them. Name what will be lost and offer a stash first.
|
||||
- **Never add `--no-verify`** — using it when a hook fails bypasses the QA gate the pipeline depends on. Only on the user's explicit demand, with a warning.
|
||||
|
||||
## Workflow
|
||||
## Dispatch
|
||||
|
||||
### For creating a new commit:
|
||||
Read exactly one flow file. Each is self-contained.
|
||||
|
||||
1. **Gather context** — what changed and why? (from staged changes, PR description, issue context). Verify the staged diff is one logical, atomic change and that the repo would still build/test at this commit — if not, suggest splitting before proceeding.
|
||||
2. **Check for secrets** — scan the staged diff for anything that looks like a credential, API key, token, or environment-specific config. Stop and flag it rather than committing.
|
||||
3. **Determine type** — is this a feature (`feat`), bug fix (`fix`), or other? Default: check the change itself.
|
||||
4. **Determine scope** — which system/module? Use scope from plugin config if set, otherwise infer from files changed.
|
||||
5. **Write description** — imperative mood, no period. Neither source spec sets a length target below the 100-char header max, but convention favors keeping it to ~50 characters where possible for `git log --oneline` readability. Examples: "add user authentication", "fix race condition in cache".
|
||||
6. **Add body if needed** — explain why (not what). Blank line before body, wrap at 100 chars. For non-trivial changes, follow the Why / Implementation Notes / Impact structure in `references/commit-template.md`.
|
||||
7. **Add footers if needed** — `Fixes: #123`, `Refs: #123`, `ADR: 0012`, `RFC: 0003`, `Design: <link>`, `Reviewed-by: Name`, `Co-authored-by: Name <email>`, `Signed-off-by: Name <email>`, `BREAKING CHANGE: description`. See `references/commit-template.md` for the full trailer list.
|
||||
8. **Validate** — check header length, type correctness, no trailing periods, lowercase.
|
||||
9. **Confirm and execute** — for agents, require explicit approval; for humans, show preview and ask. Never add `--no-verify` to skip hooks.
|
||||
| Condition | Flow | Read |
|
||||
|---|---|---|
|
||||
| Composing a new commit from staged changes | create | `references/create-commit.md` |
|
||||
| Amending, squashing, folding a fixup, rebasing onto a new base, or resetting HEAD | rewrite | `references/rewrite-history.md` |
|
||||
| Replaying an existing commit onto the current branch | cherry-pick | `references/cherry-pick.md` |
|
||||
|
||||
### For amending a commit:
|
||||
## Gates on every flow
|
||||
|
||||
1. **Stage new changes** (or changes to undo)
|
||||
2. **Run amend operation** — executes `rtk git commit --amend [--no-edit]` based on user intent
|
||||
3. **Offer message edit** — if user wants to change commit message, show current message and prompt for new one
|
||||
4. **Confirm before force-push** — amending is only safe on non-shared branches; if the current branch is `main`/`master`, refuse to force-push and explain why rather than warning and proceeding
|
||||
1. **Confirmation.** No history rewrite executes without explicit approval from the user or the calling agent. Cherry-pick needs the destination branch confirmed first.
|
||||
2. **Atomicity.** The result must be one logical, independently reviewable and reversible change that leaves the repository buildable and testable. This binds an amend or a squashed result as much as a fresh commit — say so before writing it, not after.
|
||||
3. **Secrets.** Before any commit or amend, scan the staged diff for anything resembling an API key,
|
||||
token, password, connection string, or environment-specific config. Stop and flag it rather than
|
||||
committing it.
|
||||
4. **Validation.** Check the message against commitlint `config-conventional` before committing. If a type, footer, or breaking-change edge case is not obvious, read `references/conventional-commits-spec.md` — it carries the constraint table, the 11-type set, and the footer token rules.
|
||||
5. **SemVer impact.** Report the bump the commit implies: `feat` → MINOR, `fix`/`perf`/`revert` → PATCH, any breaking change → MAJOR, everything else → none. Callers decide releases from this, so never omit it.
|
||||
6. **Conflicts.** If a rebase or cherry-pick halts, offer resolution or an abort. Do not resolve automatically without confirmation.
|
||||
|
||||
### For squashing commits (interactive rebase):
|
||||
## Output
|
||||
|
||||
1. **Identify commits to squash** — typically the last N commits on current branch
|
||||
2. **Confirm operation** — squashing rewrites history; get explicit approval
|
||||
3. **Execute rebase** — `rtk git rebase -i HEAD~N`, mark older commits as `squash` or `fixup`
|
||||
4. **Handle merge conflicts** — if rebase halts, offer conflict resolution options or abort; do not resolve automatically without confirmation
|
||||
5. **Offer message composition** — if squashing interactive, allow message editing
|
||||
|
||||
### For squashing commits (autosquash — preferred when tagging at commit time):
|
||||
|
||||
Prefer this over manual interactive rebase when a commit is written to be folded into an earlier one, since it removes the manual "mark as squash/fixup" step and the risk of reordering the wrong line:
|
||||
|
||||
1. **Create the fixup/squash commit** — `rtk git commit --fixup=<commit>` (keeps target's message) or `rtk git commit --squash=<commit>` (lets you edit the combined message later). Both prefix the message with `fixup!`/`squash!` and target `<commit>`.
|
||||
2. **Confirm operation** — rewriting history still requires explicit approval before the rebase runs.
|
||||
3. **Execute** — `rtk git rebase --autosquash HEAD~N` (or `-i --autosquash` to review the plan first); git reorders and marks the `fixup!`/`squash!` commits against their targets automatically.
|
||||
4. **Handle merge conflicts** — same as manual rebase: offer resolution or abort, never resolve automatically without confirmation.
|
||||
|
||||
### For cherry-picking:
|
||||
|
||||
1. **Identify source commit(s)** — hash or branch reference
|
||||
2. **Confirm destination branch** — cherry-pick will replay commits on current branch
|
||||
3. **Execute cherry-pick** — `rtk git cherry-pick <commit-hash>`
|
||||
4. **Handle conflicts** — offer conflict resolution or abort
|
||||
5. **Report outcome** — successful replays, conflicts, or rejected commits
|
||||
|
||||
## Output format (for agent consumption)
|
||||
|
||||
Return structured JSON:
|
||||
For an agent caller, return:
|
||||
|
||||
```json
|
||||
{
|
||||
"operation": "create|amend|squash|cherry-pick",
|
||||
"status": "success|conflict|rejected",
|
||||
"message": "Commit message or error description",
|
||||
"message": "commit message or error description",
|
||||
"commit_hash": "abc1234",
|
||||
"semver_impact": "MAJOR|MINOR|PATCH|none",
|
||||
"breaking_change": true|false,
|
||||
"confirmation_required": true|false,
|
||||
"breaking_change": false,
|
||||
"confirmation_required": false,
|
||||
"details": {
|
||||
"type": "feat",
|
||||
"scope": "api",
|
||||
"description": "add user authentication",
|
||||
"body": "optional body text",
|
||||
"footers": ["Fixes: #123", "Refs: #456", "ADR: 0012", "Reviewed-by: Alice", "Co-authored-by: Bob <bob@example.com>", "Signed-off-by: Alice <alice@example.com>"]
|
||||
"body": "optional body text, or null",
|
||||
"footers": ["Fixes: #123", "Refs: #456", "Co-authored-by: Bob <bob@example.com>"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For interactive human use, format as readable prose with clear prompts and previews.
|
||||
`details.footers` is an array of the resolved trailer lines, empty when there are none — never a
|
||||
single joined string, and never omitted. Downstream agents index it.
|
||||
|
||||
## Reference
|
||||
|
||||
If a footer or type/scope edge case isn't covered above, read `references/conventional-commits-spec.md` for the full specification.
|
||||
|
||||
For the Why / Implementation Notes / Impact body structure and the full trailer list, read `references/commit-template.md`.
|
||||
For a human caller, show the same fields as a prose preview with a confirmation prompt.
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-git-htmldocs
|
||||
---
|
||||
|
||||
# Cherry-picking a commit
|
||||
|
||||
1. **Identify the source commit** — a hash or a branch reference.
|
||||
2. **Confirm the destination** — cherry-pick replays onto the branch currently checked out, so verify that is the intended branch before running anything.
|
||||
3. **Execute** — `rtk git cherry-pick <commit-hash>`. A range is `<a>..<b>` (exclusive of `<a>`) or `<a>^..<b>` (inclusive); `-n` stages without committing, for when the replay needs editing first.
|
||||
4. **Handle conflicts** — if the replay halts, offer resolution or `rtk git cherry-pick --abort`. Never resolve automatically without confirmation.
|
||||
5. **Report the outcome** — which commits replayed, which conflicted, and which were rejected.
|
||||
@@ -7,7 +7,7 @@ source_keys:
|
||||
|
||||
Use this structure for the body/footer of any non-trivial commit (skip sections that don't apply — do not leave placeholders in the actual commit).
|
||||
|
||||
```
|
||||
```text
|
||||
<type>(<scope>): <concise summary>
|
||||
```
|
||||
The header is required. Describe the intended outcome, not the implementation.
|
||||
@@ -53,7 +53,7 @@ Omit if there are no noteworthy impacts.
|
||||
|
||||
Structured metadata for traceability and tooling. Use only the trailers that apply:
|
||||
|
||||
```
|
||||
```text
|
||||
Fixes:
|
||||
Refs:
|
||||
ADR:
|
||||
|
||||
@@ -10,7 +10,7 @@ Conventional Commits is a lightweight convention on top of commit messages that
|
||||
|
||||
## Message Format
|
||||
|
||||
```
|
||||
```text
|
||||
<type>[optional scope]: <description>
|
||||
|
||||
[optional body]
|
||||
@@ -58,20 +58,20 @@ A `BREAKING CHANGE` footer or `!` on **any** type always triggers a MAJOR bump.
|
||||
Two equivalent notations:
|
||||
|
||||
**`!` in header** (preferred — visible in `git log --oneline`):
|
||||
```
|
||||
```text
|
||||
feat!: drop support for Node 6
|
||||
feat(api)!: remove deprecated endpoint
|
||||
```
|
||||
|
||||
**`BREAKING CHANGE` footer** (machine-readable body):
|
||||
```
|
||||
```text
|
||||
feat: allow config to extend other configs
|
||||
|
||||
BREAKING CHANGE: `extends` key now used for extending config files
|
||||
```
|
||||
|
||||
**Both together** (most explicit):
|
||||
```
|
||||
```text
|
||||
feat!: drop support for Node 6
|
||||
|
||||
BREAKING CHANGE: use JavaScript features not available in Node 6.
|
||||
@@ -85,7 +85,7 @@ Rules:
|
||||
|
||||
## Footer Token Rules
|
||||
|
||||
```
|
||||
```text
|
||||
<token>: <value>
|
||||
<token> #<value> # for issue references
|
||||
```
|
||||
@@ -96,7 +96,7 @@ Rules:
|
||||
- Blank line required before the footer block.
|
||||
|
||||
Valid footer examples:
|
||||
```
|
||||
```text
|
||||
Reviewed-by: Z
|
||||
Refs: #123
|
||||
Co-authored-by: Alice <alice@example.com>
|
||||
@@ -106,29 +106,29 @@ BREAKING CHANGE: the `--format` flag now requires a value
|
||||
## Examples
|
||||
|
||||
Minimal — no body, no footer:
|
||||
```
|
||||
```text
|
||||
docs: correct spelling of CHANGELOG
|
||||
```
|
||||
|
||||
With scope:
|
||||
```
|
||||
```text
|
||||
feat(lang): add Polish language
|
||||
```
|
||||
|
||||
Breaking change via `!`:
|
||||
```
|
||||
```text
|
||||
feat!: send an email to the customer when a product is shipped
|
||||
```
|
||||
|
||||
Breaking change via footer:
|
||||
```
|
||||
```text
|
||||
feat: allow provided config object to extend other configs
|
||||
|
||||
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
|
||||
```
|
||||
|
||||
Multi-paragraph body with multiple footers:
|
||||
```
|
||||
```text
|
||||
fix: prevent racing of requests
|
||||
|
||||
Introduce a request id and a reference to latest request. Dismiss
|
||||
@@ -142,7 +142,7 @@ Refs: #123
|
||||
```
|
||||
|
||||
Revert:
|
||||
```
|
||||
```text
|
||||
revert: let us never again speak of the noodle incident
|
||||
|
||||
Refs: 676104e, a215868
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source_keys:
|
||||
- conventional-commits-spec
|
||||
- commitlint-config-conventional
|
||||
- org-commit-conventions
|
||||
---
|
||||
|
||||
# Creating a new commit
|
||||
|
||||
1. **Gather context** — what changed and why, from the staged diff, the PR description, or the issue. Confirm the staged diff is one logical, independently reviewable and reversible change that leaves the repository buildable and testable. If it bundles unrelated work, suggest splitting it before going further.
|
||||
2. **Determine the type** — read it off the change itself: a new user-visible feature is `feat`, a bug fix is `fix`. For the full 11-type set and each type's SemVer impact, read `references/conventional-commits-spec.md`.
|
||||
3. **Determine the scope** — use the scope from plugin config where one is set, otherwise infer it from the files changed (`api`, `db`, `cli`, `config`). Scope is optional, but it identifies which part of the system moved and is worth setting.
|
||||
4. **Write the description** — imperative mood, no trailing period: "add user authentication", "fix race condition in cache". Neither source spec sets a target below the 100-character header maximum, but convention favours roughly 50 characters so `git log --oneline` stays readable.
|
||||
5. **Add a body when the change is non-trivial** — blank line first, wrapped at 100 characters. Explain *why*, not what: the diff already shows what changed, and the message's job is the context the diff cannot carry — motivation, root cause, tradeoffs. Follow the Why / Implementation Notes / Impact structure in `references/commit-template.md`.
|
||||
6. **Add footers where they apply** — `Fixes: #123`, `Refs: #123`, `ADR: 0012`, `Co-authored-by: Name <email>`, `BREAKING CHANGE: description`. For the full trailer list, read `references/commit-template.md`.
|
||||
7. **Signal a breaking change with `!` before the colon** — `feat!: drop Node 6` is visible in `git log --oneline`, where the `BREAKING CHANGE:` footer alone is machine-readable but hidden. Use both when the break needs describing.
|
||||
8. **Validate, confirm, execute** — check header length, type, lowercase subject and trailing period against commitlint, show the message, and commit only once the caller has approved. Never add `--no-verify`.
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
source_keys:
|
||||
- org-commit-conventions
|
||||
- context7-git-htmldocs
|
||||
---
|
||||
|
||||
# Rewriting existing commits
|
||||
|
||||
Every flow on this page rewrites history. None of them runs before the caller has explicitly approved it, and none is followed by a force-push to `main`/`master` — refuse that and explain why instead.
|
||||
|
||||
## Amend the last commit
|
||||
|
||||
1. Stage the new changes, or the changes that undo something.
|
||||
2. Run `rtk git commit --amend`, adding `--no-edit` when the message stays as it is.
|
||||
3. If the message should change, show the current one and prompt for the replacement.
|
||||
4. The branch has now diverged from its remote. Amending is safe only on a branch nobody else has based work on; on `main`/`master`, refuse the force-push and explain, rather than warning and proceeding.
|
||||
|
||||
## Fold a commit into an earlier one (autosquash — preferred)
|
||||
|
||||
Prefer this whenever a commit is written to be folded, because git does the marking:
|
||||
|
||||
1. `rtk git commit --fixup=<commit>` keeps the target's message; `rtk git commit --squash=<commit>` lets you edit the combined message later. Both prefix the message with `fixup!`/`squash!` and name the target commit.
|
||||
2. Get explicit approval — the rebase still rewrites history.
|
||||
3. Run `rtk git rebase -i --autosquash HEAD~N`. Git pre-fills the todo list with the tagged commits already reordered against their targets; save it unchanged to apply.
|
||||
|
||||
**`-i` is not optional here.** On Git 2.39.5, `rtk git rebase --autosquash HEAD~N` without `-i` prints `Successfully rebased and updated refs/heads/<branch>.` and exits 0 while leaving the `fixup!` commit in place at its original SHA — `--autosquash` is honoured only by the interactive machinery, and the false success is the trap: the fold is reported as done, and the surviving `fixup!` subject then fails the Conventional Commits `commit-msg` hook. Later Git versions taught the non-interactive rebase to honour the flag, but `-i --autosquash` is correct on every version, so always write that.
|
||||
|
||||
## Squash by hand (interactive rebase)
|
||||
|
||||
Use this when the commits were not tagged at commit time. **Interactive rebase has no undo once `rebase -i` starts — `git reflog` is the recovery path.**
|
||||
|
||||
1. Identify the commits to squash — typically the last N on the current branch.
|
||||
2. Get explicit approval.
|
||||
3. Run `rtk git rebase -i HEAD~N`, marking the older commits `squash` to keep their messages for editing, or `fixup` to discard them.
|
||||
4. Compose the combined message when the rebase stops to ask. For a non-trivial combined message, follow the structure in `references/commit-template.md`.
|
||||
|
||||
## When a rebase halts on a conflict
|
||||
|
||||
Offer conflict resolution or `rtk git rebase --abort`. Do not resolve conflicts automatically without confirmation.
|
||||
|
||||
## Rebase the branch onto a new base
|
||||
|
||||
Replays this branch's commits on top of another branch's tip — bringing a feature branch up to
|
||||
date without a merge commit.
|
||||
|
||||
1. Confirm nothing being replayed has been pushed anywhere someone else has based work on. A rebase
|
||||
gives every replayed commit a new SHA, which breaks any clone that already has the old ones.
|
||||
2. Get explicit approval — this rewrites history like every other flow on this page.
|
||||
3. `rtk git fetch origin` first, so `<newbase>` is the real tip rather than a stale local copy.
|
||||
4. `rtk git rebase <newbase>` — for example `rtk git rebase main`. Use
|
||||
`rtk git rebase --onto <newbase> <upstream> <branch>` to replay only the commits after
|
||||
`<upstream>`, which is how a branch started from the wrong base gets moved.
|
||||
5. The branch has now diverged from its remote. It needs
|
||||
`--force-with-lease --force-if-includes` to push, never a bare `--force`, and never on
|
||||
`main`/`master` — refuse that and explain.
|
||||
|
||||
## Move the branch pointer back (`git reset`)
|
||||
|
||||
`reset` moves the current branch to another commit. The mode decides what survives:
|
||||
|
||||
| Mode | Branch pointer | Index | Working tree |
|
||||
|---|---|---|---|
|
||||
| `--soft` | moves | untouched — the changes stay staged | untouched |
|
||||
| `--mixed` (default) | moves | reset — the changes become unstaged | untouched |
|
||||
| `--hard` | moves | reset | **overwritten; uncommitted work is destroyed** |
|
||||
|
||||
- "Undo my last commit but keep the changes" is `rtk git reset --soft HEAD~1`. That is the default
|
||||
answer to the request; reach for anything else only when the caller asked for it.
|
||||
- `rtk git reset --mixed HEAD~1` when the staging should be redone from scratch too.
|
||||
- `rtk git reset --hard <ref>` is gated like a force-push: state exactly which uncommitted changes
|
||||
will be discarded, get approval for that specific reset, and offer `rtk git stash push -u` first.
|
||||
The commits it drops stay reachable through `git reflog`; the uncommitted edits never entered git
|
||||
at all and nothing recovers them.
|
||||
|
||||
Reset does not rewrite the commits it leaves behind, so no force-push is needed unless the branch
|
||||
was already pushed at the newer commit.
|
||||
@@ -7,34 +7,34 @@ source_keys:
|
||||
- context7-git-htmldocs
|
||||
---
|
||||
|
||||
# Research Sources for git:commits Skill
|
||||
# Research Sources for git-commits Skill
|
||||
|
||||
Sources extracted from the git plugin research phase. Only sources that directly informed this skill are listed; sibling skills (git:branches, git:history, git:remotes, etc.) have their own sources.md.
|
||||
Sources extracted from the git plugin research phase. Only sources that directly informed this skill are listed; sibling skills (git-branches, git-history, git-remotes, etc.) have their own sources.md.
|
||||
|
||||
## conventional-commits-spec
|
||||
|
||||
- **Description:** Conventional Commits Specification (v1.0.0) — message format, types, breaking changes, footer rules
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/commits.md § "Conventional Commits Specification (v1.0.0)"
|
||||
- **Contributing files:** SKILL.md, references/conventional-commits-spec.md
|
||||
- **Contributing files:** SKILL.md, references/conventional-commits-spec.md, references/create-commit.md
|
||||
- **Status:** extracted
|
||||
|
||||
## commitlint-config-conventional
|
||||
|
||||
- **Description:** commitlint config-conventional preset — validation constraints (max 100 chars header, no trailing periods, lowercase type, 11-type set enforcement)
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/commits.md § "commitlint Constraints (`config-conventional`)"
|
||||
- **Contributing files:** SKILL.md, references/conventional-commits-spec.md
|
||||
- **Contributing files:** SKILL.md, references/conventional-commits-spec.md, references/create-commit.md
|
||||
- **Status:** extracted
|
||||
|
||||
## org-commit-conventions
|
||||
|
||||
- **Description:** Organization commit message body template and git conventions (atomic commits, no `--no-verify`, no force-push main/master, `rtk git` wrapper) — content fully embedded in this skill; the org's `core/instructions/git.md` and `core/instructions/commits.md` are provenance only and are not a live dependency
|
||||
- **Research doc:** core/instructions/commits.md, core/instructions/git.md (org convention, not part of the plugin's research corpus)
|
||||
- **Contributing files:** SKILL.md, references/commit-template.md
|
||||
- **Contributing files:** SKILL.md, references/commit-template.md, references/create-commit.md, references/rewrite-history.md
|
||||
- **Status:** extracted
|
||||
|
||||
## context7-git-htmldocs
|
||||
|
||||
- **Description:** Official Git HTML documentation — `git commit --squash`/`--fixup` and `git rebase --autosquash` flag semantics
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/cli-reference.md
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Description:** Official Git HTML documentation — `git commit --squash`/`--fixup`, `git rebase --autosquash`, and `git cherry-pick` range and abort semantics
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/cli-reference.md § "Committing", § "Rebasing", § "Cherry-picking"
|
||||
- **Contributing files:** SKILL.md, references/rewrite-history.md, references/cherry-pick.md
|
||||
- **Status:** extracted
|
||||
|
||||
@@ -6,6 +6,10 @@ Inspect git history — log queries, bisect, and locating problematic commits.
|
||||
|
||||
This skill handles history inspection within the git workflow suite. It queries logs with pickaxe/line-range/custom formats, runs bisect to find bug-introducing commits, and locates commits for downstream cherry-picking or reverting. It returns structured results for agent composition. Rebase, squash, fixup, and other history-rewriting operations are owned by git-commits, not this skill.
|
||||
|
||||
## Composition
|
||||
|
||||
`git-branches` delegates revert here (see `git-branches`'s `references/merging.md`), which is why this skill carries that operation rather than treating it as out of scope; it is general git knowledge, not drawn from the `history-inspection.md` research corpus. Cherry-pick is **not** this skill's: `git-commits` owns it, and this skill's job ends at locating the SHA to hand over. Server-side commit history on a Gitea-hosted repository belongs to `gitea-branches`; this skill reads the local working copy.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
@@ -19,6 +23,7 @@ Describe your history task: search logs, bisect for a regression, or locate a sp
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `references/git-log-format.md` | Full log format placeholders, diff-filter letters, `-L` syntax, ancestry filters, diff output-control flags |
|
||||
| `references/bisect.md` | Loaded when the entry procedure is bisect: manual and automated flows, exit codes, skip, replay, narrowing, custom terms |
|
||||
| `references/git-log-format.md` | Loaded when a log or diff flag needs looking up: format placeholders, presets, diff-filter letters, `-L` syntax, ancestry filters, pickaxe binary-file behaviour, diff output-control flags |
|
||||
| `references/sources.md` | Research sources and provenance |
|
||||
| `references/README.md` | Index of the references directory |
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
name: git-history
|
||||
|
||||
description: >
|
||||
Inspect git history: query logs with pickaxe, line-range, or custom formats; find bug origins via bisect; locate problematic commits for cherry-picking or reverting. Use when investigating history, tracing when a change happened, or finding the commit that broke something. Return structured results for downstream agents. Do not use for authoring or formatting commit messages, or executing rebase/squash/fixup operations — use git-commits for that.
|
||||
Use when investigating git history — pickaxe (`-S`/`-G`) or `-L` line-range log
|
||||
queries, tracing when a change landed, bisecting what broke something, or
|
||||
locating a commit to revert or backport. Not authoring or rebasing commits ->
|
||||
`git-commits`. Not a Gitea server's history -> `gitea-branches`.
|
||||
|
||||
metadata:
|
||||
category: git
|
||||
@@ -16,81 +19,47 @@ allowed-tools: Bash
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Pickaxe searches (`-S` vs `-G`)**: `-S"string"` finds commits where string count changed; `-G"regex"` finds any line matching regex in diffs. They're not equivalent: a line replaced (one removal + one addition) matches `-G` but not `-S` if count is unchanged.
|
||||
- **`--follow` only works for single files**: it traces renames but fails with multiple paths or directory globs. Use `git log -- <single-file>` or query without `--follow`.
|
||||
- **Bisect with skips**: if bisect cannot pinpoint a commit because the culprit is adjacent to skipped commits, it reports "cannot find exact culprit" and lists candidates. This is not a failure — it's as precise as the skip range allows.
|
||||
- **Interactive rebase is non-recoverable on mistake**: there's no undo once `rebase -i` starts. Suggest `git reflog` to recover if the user realizes mid-way they selected the wrong commits.
|
||||
- **`-L` (line-range history) requires exact line numbers or regex patterns**: off-by-one errors omit the target range. Test the range with `git log -L` before offering it to users.
|
||||
- `-S"string"` matches only where the string's *count* changed, so a line edited in place matches `-G"regex"` and not `-S`. Reach for `-G` whenever the string may have moved rather than appeared.
|
||||
- `--follow` traces renames for exactly one path. Given several paths or a glob it fails instead of degrading, so run it once per file.
|
||||
- Under `git bisect run`, exit `128` or above **aborts the session** rather than marking the commit bad, so a crashing test script ends the search silently.
|
||||
- Bisect answering "cannot find exact culprit" beside skipped commits is a complete result: it is as precise as the skip range allows.
|
||||
|
||||
## Query Logs and Locate Commits
|
||||
## Step 1 — Pick the entry procedure
|
||||
|
||||
Default to `git log --oneline` for quick inspection. For deeper queries:
|
||||
| What is known | Procedure |
|
||||
|---|---|
|
||||
| Content, a file, or a line range to search for | Query the log — Step 2 |
|
||||
| Nothing to search for — only that the behaviour changed between two points | Bisect — read `references/bisect.md` |
|
||||
| The commit itself, already identified | Step 3 |
|
||||
|
||||
- **Find when a string appeared or disappeared**: Use `git log -S"string"` (count-sensitive, finds adds/removes). If you need any mention of the string in diffs, use `git log -G"regex"` instead. Add `--pickaxe-regex` to treat the `-S` string as a POSIX ERE, and `--pickaxe-all` to show every changed file in a matching changeset, not just the matching ones. Binary files are searched by `-S`; `-G` ignores them unless `--text` is also supplied.
|
||||
- **Trace changes to a specific line or function**: Use `git log -L <start>,<end>:<file>` or `git log -L :<function>:<file>` (requires function name heuristic). This shows the evolution of that range across all commits.
|
||||
- **Filter by change type**: Use `git log --diff-filter=<type>` (A=added, M=modified, D=deleted, R=renamed) to narrow to specific file operations.
|
||||
- **Mainline-only history through merges**: Use `--first-parent` to follow only the integration branch and skip merged-in side-branch commits; combine with `--merges`/`--no-merges` or `--ancestry-path`/`--min-parents`/`--max-parents` for other ancestry-graph filtering — see `references/git-log-format.md` for the full set.
|
||||
- **Custom format for structured output**: Construct format string with `%h` (hash), `%s` (subject), `%an` (author), `%ar` (relative date), `%b` (body). Example: `git log --format="%h | %s | %an (%ar)"`.
|
||||
- **File-specific history with renames**: Use `git log --follow -- <file>` (single file only). Without `--follow`, log stops at the rename boundary.
|
||||
## Step 2 — Query the log
|
||||
|
||||
## Bisect to Find Blame Commit
|
||||
Default to `git log --oneline`, then narrow by whatever is known:
|
||||
|
||||
Use bisect when hunting for the commit that introduced a bug or behaviour change. Binary search reduces iterations from O(N) to O(log N).
|
||||
- **Content**: `git log -S"string"`, or `-G"regex"` to match any diff line. `--pickaxe-regex` makes the `-S` argument a POSIX ERE; `--pickaxe-all` shows every file in a matching changeset.
|
||||
- **A line or function**: `git log -L <start>,<end>:<file>` or `git log -L :<function>:<file>`. Confirm the range resolves before reporting on it — an off-by-one silently omits the target.
|
||||
- **A file across renames**: `git log --follow -- <file>`. Without `--follow` the history stops at the rename boundary.
|
||||
- **Mainline only**: `--first-parent` follows the integration branch and skips commits merged in from side branches.
|
||||
- **Structured output**: `git log --format="%h | %s | %an (%ar)"`.
|
||||
|
||||
**Basic manual flow:**
|
||||
```bash
|
||||
git bisect start
|
||||
git bisect bad [HEAD] # mark current (or specified) as broken
|
||||
git bisect good <commit> # mark known-good baseline
|
||||
# Git checks out midpoint; test it manually
|
||||
git bisect good # if test passes
|
||||
git bisect bad # if test fails
|
||||
# Repeat until git reports "X is the first bad commit"
|
||||
git bisect reset # return to original HEAD
|
||||
If you need the placeholder catalogue, format presets, `--diff-filter` letters, full `-L` syntax, ancestry filters, pickaxe binary-file behaviour, or `git diff` output-control flags such as `--stat`, `--word-diff` and the whitespace options, read `references/git-log-format.md`.
|
||||
|
||||
## Step 3 — Act on a located commit
|
||||
|
||||
Offer the operation and its consequence; run it only once the user has chosen.
|
||||
|
||||
- Backporting the commit to another branch is a cherry-pick, and cherry-pick is `git-commits`' — it owns the destination-branch check, the `rtk git` wrapper and the `--abort` path. Hand it the SHA; do not run `git cherry-pick` from here.
|
||||
- `git revert <commit>` adds a new commit undoing it — for un-applying merged work without rewriting history.
|
||||
- `git blame <file>` attributes each line to the commit that last touched it, when the question is which commit introduced one specific line.
|
||||
|
||||
For diff output control on the located commit, read `references/git-log-format.md`.
|
||||
|
||||
## Step 4 — Return the result
|
||||
|
||||
Report each located commit in this shape, so a calling agent can act on it without reparsing raw log output:
|
||||
|
||||
```text
|
||||
<sha> — <subject>
|
||||
<author> (<relative date>)
|
||||
Recommendation: <action or "none">
|
||||
```
|
||||
|
||||
**Automated with `git bisect run`:** if a test command exists, use `git bisect run <cmd>`. Git interprets the exit code: `0`=good, `1-124`=bad, `125`=skip (build broken), `126-127`=POSIX shell errors treated as bad, `128+`=**aborts the bisect session entirely** (not treated as bad — a crashed test script can silently end the search).
|
||||
|
||||
**With skip:** if a commit is untestable (broken build), use `git bisect skip` to exclude it without manually deciding good/bad. If the first-bad is adjacent to skips, bisect reports it cannot pinpoint but lists candidates.
|
||||
|
||||
**Undoing a wrong good/bad call:** `git bisect log` prints the session's decision history; save it (`git bisect log > bisect.log`), edit out the mistaken entry, then `git bisect reset && git bisect replay bisect.log` to resume from the corrected log instead of restarting the whole search.
|
||||
|
||||
**Narrowing and speeding up the search:** `git bisect start HEAD v1.2 -- src/` limits bisection to a path, cutting the number of trials. `--no-checkout` updates the `BISECT_HEAD` ref instead of checking out a working tree (useful for tests that don't need one; automatic in bare repos). `--first-parent` follows only first parents at merges, finding the integration commit that introduced a regression while ignoring broken side branches.
|
||||
|
||||
**Inspecting remaining candidates visually:** `git bisect visualize` (alias `view`) opens the suspects in gitk; add `--stat` or `-p` to show diffstat or full patches instead. Falls back to `git log` when no graphical display is detected.
|
||||
|
||||
**For non-regression hunts:** use `git bisect start --term-new <new> --term-old <old>` to search for a property change instead of a bug (e.g., performance regression). Then use the custom terms instead of `good`/`bad`.
|
||||
|
||||
For rebase execution (interactive rebase, squash/fixup/reword, conflict handling) see git-commits — it owns history-rewriting operations. This skill only locates commits and reports on history; it does not execute rebases.
|
||||
|
||||
## Find and Manipulate Problematic Commits
|
||||
|
||||
Once a commit is identified (via log query or bisect), offer cherry-pick or revert. This section is general git knowledge, not sourced from `history-inspection.md` — `git-branches`'s SKILL.md explicitly delegates cherry-pick/revert here (see its Merging section), which is why this skill carries them rather than treating them as out of scope:
|
||||
|
||||
- **Cherry-pick**: `git cherry-pick <commit>` copies a commit's changes onto current HEAD. Use when backporting fixes to other branches.
|
||||
- **Revert**: `git revert <commit>` creates a new commit that undoes the changes. Use when un-applying a merged commit without rewriting history.
|
||||
- **Blame for context**: `git blame <file>` shows which commit last changed each line. Use to trace a specific line back to its introducing commit.
|
||||
|
||||
## Inspect Diffs
|
||||
|
||||
Diff-output tuning is in scope too: `--stat` for a diffstat summary, `--word-diff` for word-level (not line-level) changes, and whitespace flags (`-w`, `--ignore-blank-lines`) to suppress noise from reformatting. See `references/git-log-format.md` for the full flag set.
|
||||
|
||||
## Return Results Structured
|
||||
|
||||
For agent consumption, return:
|
||||
- **Commit SHA** (full or abbreviated as appropriate)
|
||||
- **Subject line** (from `%s`)
|
||||
- **Author and date** (from `%an` and `%ar`)
|
||||
- **Action taken or recommended** (e.g., "Found via bisect", "Offer cherry-pick to main", "Rebase conflicts detected")
|
||||
|
||||
Example for agent:
|
||||
```
|
||||
Found first bad commit: abc1234
|
||||
Subject: fix null pointer in parser
|
||||
Author: Alice (2 weeks ago)
|
||||
Recommendation: Backport to release branch via cherry-pick
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
For the full log format placeholder catalogue, named format presets, `--diff-filter` letters, `-L` range syntax, ancestry filters, and `git diff` output-control flags, read `references/git-log-format.md`.
|
||||
|
||||
@@ -12,4 +12,5 @@ This directory contains provenance metadata and research sources for the `git-hi
|
||||
## Files
|
||||
|
||||
- `sources.md` — Extracted research sources and their contributing documents
|
||||
- `git-log-format.md` — Full `git log` format placeholder catalogue, named format presets, `--diff-filter` letters, `-L` line-range syntax, ancestry filters, and `git diff` output-control flags
|
||||
- `bisect.md` — The full `git bisect` procedure: manual and automated flows, exit-code semantics, skip and replay, narrowing options, and custom good/bad terms
|
||||
- `git-log-format.md` — Full `git log` format placeholder catalogue, named format presets, `--diff-filter` letters, `-L` line-range syntax, ancestry filters, pickaxe binary-file behaviour, and `git diff` output-control flags
|
||||
|
||||
68
plugins/git/.apm/skills/git-history/references/bisect.md
Normal file
68
plugins/git/.apm/skills/git-history/references/bisect.md
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
topic: bisect
|
||||
source_keys:
|
||||
- git-scm-bisect-docs
|
||||
---
|
||||
|
||||
# Finding a commit with `git bisect`
|
||||
|
||||
Read this when the question is *which commit changed the behaviour* and there is no string, file,
|
||||
or line range to search the log for. Binary search reduces the trials from O(N) to O(log N).
|
||||
|
||||
## Manual flow
|
||||
|
||||
```bash
|
||||
git bisect start
|
||||
git bisect bad [HEAD] # mark current (or specified) as broken
|
||||
git bisect good <commit> # mark known-good baseline
|
||||
# Git checks out the midpoint; test it
|
||||
git bisect good # test passes
|
||||
git bisect bad # test fails
|
||||
# Repeat until git reports "X is the first bad commit"
|
||||
git bisect reset # return to the original HEAD
|
||||
```
|
||||
|
||||
## Automated
|
||||
|
||||
With a test command available, use `git bisect run <cmd>`. Git reads the exit code: `0` good,
|
||||
`1`–`124` bad, `125` skip (build broken), `126`–`127` POSIX shell errors, treated as bad, and
|
||||
`128` or above aborts the session outright rather than marking the commit bad.
|
||||
|
||||
## Untestable commits
|
||||
|
||||
`git bisect skip` excludes a commit that cannot be built or tested without deciding good or bad
|
||||
for it. When the first bad commit is adjacent to a skipped range, bisect reports that it cannot
|
||||
pinpoint the culprit and lists the candidates — that is the precise answer the skip range allows,
|
||||
not a failure.
|
||||
|
||||
## Undoing a wrong good/bad call
|
||||
|
||||
`git bisect log` prints the session's decision history. Save it, edit out the mistaken entry, and
|
||||
resume from the corrected log rather than restarting the search:
|
||||
|
||||
```bash
|
||||
git bisect log > bisect.log
|
||||
# edit bisect.log, removing the wrong decision
|
||||
git bisect reset && git bisect replay bisect.log
|
||||
```
|
||||
|
||||
## Narrowing and speeding up
|
||||
|
||||
- `git bisect start HEAD v1.2 -- src/` restricts bisection to a path, cutting the trial count.
|
||||
- `--no-checkout` updates the `BISECT_HEAD` ref instead of checking out a working tree — useful
|
||||
for tests that do not need one, and automatic in bare repos.
|
||||
- `--first-parent` follows only first parents at merges, finding the integration commit that
|
||||
introduced a regression while ignoring broken side branches.
|
||||
|
||||
## Inspecting the remaining candidates
|
||||
|
||||
`git bisect visualize` (alias `view`) opens the suspects in gitk, falling back to `git log` when
|
||||
no graphical display is detected. Add `--stat` or `-p` for a diffstat or full patches.
|
||||
|
||||
## Hunting a non-bug property change
|
||||
|
||||
`git bisect start --term-new <new> --term-old <old>` searches for any property change — a
|
||||
performance regression, say — instead of a bug. Use the custom terms in place of `good` and `bad`
|
||||
for the rest of the session.
|
||||
|
||||
Once the first bad commit is identified, return to Step 3 to act on it and Step 4 to report it.
|
||||
@@ -88,7 +88,7 @@ source_keys:
|
||||
| `%GK` | signing key ID |
|
||||
|
||||
**Trailers:**
|
||||
```
|
||||
```text
|
||||
%(trailers[:key=<k>][,only][,separator=<s>][,unfold][,keyonly][,valueonly])
|
||||
```
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Git bisect documentation covering binary search through commit history to find t
|
||||
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/history-inspection.md
|
||||
- **Doc heading:** `## git bisect`
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Contributing files:** SKILL.md, references/bisect.md
|
||||
|
||||
## git-scm-log-docs
|
||||
|
||||
@@ -28,4 +28,4 @@ Git diff documentation covering output control (--stat, --name-only, --name-stat
|
||||
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/history-inspection.md
|
||||
- **Doc heading:** `## git diff — Output Control`
|
||||
- **Contributing files:** references/git-log-format.md
|
||||
- **Contributing files:** SKILL.md, references/git-log-format.md
|
||||
|
||||
@@ -18,7 +18,17 @@ Describe your remote operation: add a remote, push, pull, fetch, or configure tr
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `SKILL.md` | Skill instructions for agents — force-push gate, dispatch table, return format |
|
||||
| `references/README.md` | Describes the references directory contents |
|
||||
| `references/remotes.md` | Full `set-url` variants, shallow-clone/fetch options, force-push mitigation detail, and pull config precedence |
|
||||
| `references/remote-config.md` | Read when adding, removing, renaming, inspecting or re-pointing a remote, or configuring tracking, mirroring, or `set-url` |
|
||||
| `references/fetch.md` | Read when fetching or pruning remote-tracking refs, or doing a shallow or partial fetch |
|
||||
| `references/push.md` | Read when pushing branches or tags, writing refspecs, or force-pushing |
|
||||
| `references/pull.md` | Read when integrating remote changes into the current branch, including the divergence rule |
|
||||
| `references/sources.md` | Research sources and provenance |
|
||||
|
||||
## Composition
|
||||
|
||||
Callers that need submodule initialization after a `--recurse-submodules` pull hand off to
|
||||
`git-submodules`; local-only work (commits, branches, history) belongs to `git-commits`,
|
||||
`git-branches`, and `git-history`. The `git-workflow` skill routes humans here for any
|
||||
remote-touching request.
|
||||
|
||||
@@ -2,112 +2,48 @@
|
||||
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.
|
||||
Use when a git operation — remote config, fetch, push, or pull — touches a
|
||||
remote, even when the user does not name it.
|
||||
Not local commits -> `git-commits`.
|
||||
Not local branches -> `git-branches`.
|
||||
Not log or bisect queries -> `git-history`.
|
||||
Not submodule pointers -> `git-submodules`.
|
||||
|
||||
metadata:
|
||||
category: git-workflow
|
||||
category: git
|
||||
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=<ref>:<sha>`.
|
||||
- **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.
|
||||
- **`--force-with-lease` alone is not safe** — background processes (IDE plugins, cron jobs) running `git fetch` silently defeat the protection. Combine it with `--force-if-includes`, or pin the explicit `--force-with-lease=<ref>:<sha>` form.
|
||||
- **Prune does not touch tags by default** — `git fetch --prune` leaves orphaned tags behind. Use `--prune --prune-tags`, or set `fetch.pruneTags true`.
|
||||
- **Set `pull.ff only` explicitly** — do not trust the installed default.
|
||||
|
||||
## Operations
|
||||
## Step 1 — Clear the force-push gate
|
||||
|
||||
### Remote Management
|
||||
`main` and `master` are a hard refusal: decline a force-push targeting either, whatever confirmation accompanies it, because no local approval can restore what the remote loses. On any other branch, `git push --force` and `-f` run only after the caller passes `confirm: true` for that specific push — for a human caller, prompt instead of failing.
|
||||
|
||||
Use these to configure which remotes you push to and pull from:
|
||||
## Step 2 — Dispatch
|
||||
|
||||
- **Add a remote**: `git remote add <name> <url>` or `git remote add -f <name> <url>` to fetch immediately
|
||||
- **Remove a remote**: `git remote remove <name>` (deletes remote + all tracking refs + config)
|
||||
- **Rename a remote**: `git remote rename <old> <new>`
|
||||
- **Inspect remotes**: `git remote -v` (show URLs) or `git remote show <name>` (live tracking status, requires network)
|
||||
- **Set-url separately for fetch vs. push**: `git remote set-url --push <name> <url>` 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 <name> <regex>`
|
||||
- **Inspect effective URLs**: `git remote get-url <name>` (shows URL after `insteadOf` rewrites) or `git remote get-url --push --all <name>` (all push URLs)
|
||||
- **Track only one branch**: `git remote add -t <branch> <name> <url>` (repeatable), or suppress tag import entirely with `git remote add --no-tags <name> <url>`
|
||||
- **Mirror a remote**: `git remote add --mirror=fetch <name> <url>` 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>` (add `--dry-run` to preview first)
|
||||
- **Set the remote's default branch pointer**: `git remote set-head <name> -a` (auto-detect, requires a prior fetch), `git remote set-head <name> <branch>` (explicit), or `git remote set-head <name> -d` (delete `refs/remotes/<name>/HEAD`)
|
||||
Read the row matching the operation, and only that row — each file is self-contained. A task spanning two operations reads both.
|
||||
|
||||
### Fetch Operations
|
||||
|
||||
Use these to update your tracking branches without touching your local branches:
|
||||
|
||||
- **Fetch from one remote**: `git fetch <remote>` — fetches all branches
|
||||
- **Fetch one branch only**: `git fetch <remote> <branch>` — 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.<name>.prune` to scope it to one remote)
|
||||
- **Shallow clones**: `--depth=<n>` 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 <remote> <branch>` — pushes to same-named remote branch
|
||||
- **Set upstream**: `git push -u <remote> <branch>` — 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 <name> <url>` to push to multiple remotes with one command
|
||||
- **Force-push safety**: Always use `git push --force-with-lease --force-if-includes <remote> <branch>` 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 <remote> --delete <branch>` (not `:<branch>` 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 <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 `+`, e.g. `git push origin +main develop` forces `main` while safe-pushing `develop`
|
||||
|
||||
Refspec syntax is `[+]<src>[:<dst>]`:
|
||||
|
||||
| Pattern | Meaning |
|
||||
| Operation | Read |
|
||||
|---|---|
|
||||
| `<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) |
|
||||
| `:<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>` |
|
||||
| Add, remove, rename, inspect, or re-point a remote; tracking, mirror, and `set-url` config | `references/remote-config.md` |
|
||||
| Fetch or prune remote-tracking refs; shallow or partial fetch | `references/fetch.md` |
|
||||
| Push branches or tags; refspecs; force-push | `references/push.md` |
|
||||
| Pull — integrate remote changes into the current branch | `references/pull.md` |
|
||||
|
||||
### Pull Operations
|
||||
## Step 3 — Return format
|
||||
|
||||
Use these to fetch and integrate remote changes. Default strategy: `--ff-only` (fail if diverged, forcing a conscious choice).
|
||||
For agent callers, return:
|
||||
|
||||
- **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.<name>.rebase`). Full precedence order (CLI flag > `pull.rebase` > `branch.<name>.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,
|
||||
@@ -116,8 +52,8 @@ Return structured output:
|
||||
"branch": "main",
|
||||
"output": "...",
|
||||
"warnings": ["force-with-lease not confirmed"],
|
||||
"recommendations": ["set pull.ff=only globally"]
|
||||
"recommendations": ["set `pull.ff only` so the default does not vary by Git version"]
|
||||
}
|
||||
```
|
||||
|
||||
On failure, include `error` field with root cause and recovery suggestion.
|
||||
On failure, set `success: false` and add an `error` field holding the root cause and a recovery suggestion.
|
||||
|
||||
@@ -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
|
||||
|
||||
31
plugins/git/.apm/skills/git-remotes/references/fetch.md
Normal file
31
plugins/git/.apm/skills/git-remotes/references/fetch.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
topic: fetch
|
||||
source_keys:
|
||||
- git-scm-fetch-docs
|
||||
- context7-git-htmldocs
|
||||
---
|
||||
|
||||
# Fetching
|
||||
|
||||
Fetch **with no refspec** updates remote-tracking branches (`refs/remotes/<name>/*`) and leaves every local branch alone.
|
||||
|
||||
That safety comes from the default refspec, not from `fetch` itself. Give it an explicit one and it writes to local branches: verified on Git 2.39.5, `git fetch origin main:probe` fast-forwarded the local `probe` branch, and a `+` prefix force-updates the destination, discarding whatever commits it held. Treat any `fetch` carrying a `<src>:<dst>` refspec as a branch update, not a read.
|
||||
|
||||
- **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.
|
||||
43
plugins/git/.apm/skills/git-remotes/references/pull.md
Normal file
43
plugins/git/.apm/skills/git-remotes/references/pull.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
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
|
||||
|
||||
`--ff-only` is not Git's default on an unset config, and never has been. Older versions silently
|
||||
merged on divergence; current ones refuse outright — verified on Git 2.39.5, a divergent pull with
|
||||
nothing configured prints the reconciliation hint and exits 128 with
|
||||
`fatal: Need to specify how to reconcile divergent branches.` The behaviour therefore still varies
|
||||
by installed version, and neither variant is the one you want. Set it explicitly.
|
||||
|
||||
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
|
||||
```
|
||||
67
plugins/git/.apm/skills/git-remotes/references/push.md
Normal file
67
plugins/git/.apm/skills/git-remotes/references/push.md
Normal 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`.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
# git-submodules
|
||||
|
||||
Initialize, clone, update, and manage git submodules for multi-repository projects.
|
||||
Add, initialize, update, pin, inspect, and remove git submodules in multi-repository projects.
|
||||
|
||||
## What it does
|
||||
|
||||
This skill handles submodule operations within the git workflow suite. It initializes submodules, clones repositories with nested submodule dependencies, updates submodule pinning, and manages version control across multi-repo projects. The skill provides clean workflows for projects with complex dependency structures and returns structured results suitable for agent composition.
|
||||
This skill handles submodule operations within the git workflow suite: cloning a superproject with
|
||||
its nested repositories, adding a dependency as a submodule, initializing and updating with
|
||||
pinning or branch tracking, parallel and recursive traversal, rebinding URLs and tracked branches,
|
||||
and the full removal sequence including the `.git/modules/` cleanup git leaves behind. It returns
|
||||
structured results suitable for agent composition.
|
||||
|
||||
It sits alongside the other git skills rather than duplicating them: `git-worktrees` covers
|
||||
multiple checkouts of a single repository, and `git-remotes` covers the superproject's own remotes.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -12,13 +19,17 @@ This skill handles submodule operations within the git workflow suite. It initia
|
||||
/git-submodules
|
||||
```
|
||||
|
||||
Describe your submodule task: initialize, clone, update, or manage versions. The skill will handle the operation and return structured results (operation, status, per-submodule details, conflicts, and a recovery `next_step` when applicable) suitable for agent composition.
|
||||
Describe the submodule task. The skill applies the shared working rules, dispatches to the
|
||||
reference for that task, and returns structured results (operation, status, per-submodule details,
|
||||
conflicts, and a recovery `next_step` when applicable).
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `SKILL.md` | Skill instructions for agents — gotchas, shared working rules, and the task dispatch table |
|
||||
| `references/README.md` | Describes contents of references/ |
|
||||
| `references/submodules.md` | Deep-dive reference: full flag tables, workflow patterns, safe-removal sequence, `absorbgitdirs`, `foreach` variables |
|
||||
| `references/setup-and-update.md` | Loaded when cloning a superproject, adding a submodule, initializing, updating, or re-pinning one, or running a command across all of them — includes the full `add` and `update` flag tables, the pinning workflows, and the `foreach` shell-variable table |
|
||||
| `references/urls-and-config.md` | Loaded when changing where a submodule points or how it is configured — `.gitmodules` vs `.git/config` anatomy, both key tables, `sync`/`set-url`/`set-branch`, local mirror overrides, relative URLs, the custom-`update` security gate, and `absorbgitdirs` |
|
||||
| `references/removal.md` | Loaded when removing or deinitializing a submodule — why `deinit` is not removal, and the four-step removal sequence |
|
||||
| `references/sources.md` | Research sources and provenance |
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
name: git-submodules
|
||||
|
||||
description: >
|
||||
Use when managing Git submodules: add dependencies as submodules, initialize and update nested repositories, sync URLs, inspect status (including detached HEAD and divergence), and safely remove submodules. Handles multi-repo projects with pinning, parallel operations, and recursive traversal. Use for both initial setup and ongoing maintenance workflows, even if the user doesn't explicitly say "submodule". Do not use for general git operations outside of submodule management.
|
||||
Use when managing Git submodules — the full lifecycle of a nested
|
||||
repository inside a superproject — including phrasings that never say the
|
||||
word, such as "add a dependency repo" or "vendor this repo inside ours".
|
||||
Not multiple checkouts of one repo -> `git-worktrees`.
|
||||
Not the superproject's own remotes -> `git-remotes`.
|
||||
|
||||
metadata:
|
||||
category: git
|
||||
@@ -10,82 +14,52 @@ metadata:
|
||||
- git-scm-submodule-docs
|
||||
---
|
||||
|
||||
## Concept
|
||||
|
||||
A submodule is a full Git repository embedded as a subdirectory inside a parent repository (the superproject). The superproject doesn't store the submodule's files — it stores a pointer to a specific commit SHA in the submodule's own history, and the two repos keep fully independent commit histories.
|
||||
|
||||
Two files govern a submodule, and they serve different audiences:
|
||||
|
||||
- **`.gitmodules`** — version-controlled, shared with collaborators. Defines each submodule's name, path, and canonical URL.
|
||||
- **`.git/config`** — local only, populated by `git submodule init`. This is where local URL overrides live (e.g. a private mirror) — they never propagate to other clones.
|
||||
|
||||
The submodule's own `.git` directory lives at `.git/modules/<name>/` in the superproject, linked to the submodule's working tree via a `.git` pointer file. After `git submodule update`, the working tree normally ends up in **detached HEAD state** — see Gotchas.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Detached HEAD by default.** `git submodule update` checks out a specific commit, not a branch. Work on a branch first, then update the pointer in the superproject. Commits made in detached state are invisible until pinned.
|
||||
- **Two pushes required, in order.** Always commit and push the submodule first, then update and push the superproject's pointer. The superproject only stores a commit SHA — if that SHA isn't reachable on the submodule's remote yet, `git submodule update` fails for anyone who pulls the superproject before the submodule push lands.
|
||||
- **`--recursive` is not default.** Most commands operate one level deep. Pass `--recursive` explicitly for nested submodules.
|
||||
- **`.git/modules/` persists after `git rm`.** Manual cleanup is needed: `rm -rf .git/modules/<name>/`.
|
||||
- **Detached HEAD detection.** Status prefix `+` means the checked-out commit differs from the superproject's recorded commit — normal after `update --remote`, but should be re-pinned before committing.
|
||||
- **Relative URLs resolve against the remote, not the filesystem.** A `../foo.git` entry in `.gitmodules` is relative to the superproject's default remote URL.
|
||||
- **Custom `update` commands are security-gated.** A `.gitmodules` entry of `update = !some-command` is never copied to `.git/config` by `git submodule init` — this stops a clone from silently executing arbitrary code.
|
||||
- **`update` leaves the submodule in detached HEAD.** Branch inside the submodule before editing, or the work is unreachable once the pointer moves.
|
||||
- **Push the submodule before the superproject.** The superproject stores only a SHA, and one missing from the submodule's remote breaks every collaborator's `update`.
|
||||
- **`--recursive` is never the default.** Subcommands stop one level deep, so nested submodules go stale silently.
|
||||
- **`git rm` leaves `.git/modules/<name>/` behind.** Nothing cleans it up, and it blocks re-adding a submodule there.
|
||||
|
||||
## Conventions
|
||||
## Working rules
|
||||
|
||||
- **Use `rtk git` for parent-repo operations.** Drop into the submodule directory only for submodule-specific git commands (committing/pushing inside the submodule itself) — mixing the two from the wrong working directory targets the wrong repo's history.
|
||||
- **Check for a dirty submodule before committing the parent pointer.** After adding or updating a submodule, run `git status` in both the parent and the submodule. A `-dirty` suffix means the submodule has uncommitted local changes; committing the parent pointer now would pin a state no one else can reproduce, since those changes exist only in the local working tree.
|
||||
Run `rtk git` from the superproject root. Enter the submodule directory only for commits and pushes
|
||||
that belong to the submodule's own history — the two repositories have independent histories, and
|
||||
the same command from the wrong directory writes to the wrong one.
|
||||
|
||||
## Operations
|
||||
Before committing a superproject pointer, run `rtk git submodule status --recursive`. Prefixes: `-`
|
||||
not initialized, `+` working tree differs from the recorded commit, `U` merge conflict. Add
|
||||
`--cached` to read the SHAs the superproject index will record rather than the working-tree state.
|
||||
A `-dirty` suffix means uncommitted changes inside the submodule, and committing the pointer over
|
||||
them pins a state nobody else can reproduce.
|
||||
|
||||
- **Clone a repo that has submodules**: `rtk git clone --recurse-submodules <url>` (one step, Git 2.13+) or `rtk git clone <url>` followed by `rtk git submodule update --init --recursive`.
|
||||
- **Add a submodule**: `rtk git submodule add <url> <path>` (`-b <branch>` to track a branch instead of a pinned commit, `--depth 1` for a shallow clone, `-f` to force past a gitignored path or name conflict, `--name <name>` when the logical name should differ from the path). Stages a `.gitmodules` entry and a gitlink — a commit is still required.
|
||||
- **Initialize**: `rtk git submodule init [<path>...]` copies submodule URLs from `.gitmodules` to `.git/config`. This is the point at which local URL overrides can be edited before fetching. Does not clone — use `update` (or `update --init` to run both in one step).
|
||||
- **Update (clone + checkout)**: `rtk git submodule update --init --recursive` is the common case — checks out the recorded commit in detached HEAD. Add `--remote --merge` (or `--remote --rebase`) to track the branch tip instead, `--jobs <n>` for parallel clones, `-f` to discard local changes. Full flag table: `references/submodules.md`.
|
||||
- **Inspect status**: `rtk git submodule status --recursive` (add `--cached` to show SHAs in the superproject index instead of the working tree). Status prefixes: `-` not initialized, `+` diverged from the superproject's recorded commit, `U` merge conflict.
|
||||
- **Sync and rebind URLs**: `rtk git submodule sync --recursive` after an upstream URL rename propagates `.gitmodules` changes into `.git/config`. `rtk git submodule set-url <path> <url>` changes a URL directly; `rtk git submodule set-branch -b <branch> <path>` sets the tracking branch used by `update --remote`.
|
||||
- **Override a submodule URL locally (private mirror)**: local-only, doesn't propagate to collaborators, and gets overwritten by the next `sync`. Full steps: `references/submodules.md`.
|
||||
- **Run a command across all submodules**: `rtk git submodule foreach --recursive '<command>'`. Shell variables available inside `<command>` (`$name`, `$sm_path`, `$displaypath`, `$sha1`, `$toplevel`): `references/submodules.md`.
|
||||
- **Deinit (unregister without removing)**: `rtk git submodule deinit <path>` (`--all` for every submodule, `-f` if local modifications are present) clears the `.git/config` section and empties the working tree. **`deinit` is not removal** — the `.gitmodules` entry and the gitlink in the superproject's index are untouched.
|
||||
- **Safe removal** (destructive; confirm before executing) — full three-step sequence including the manual `.git/modules/` cleanup: `references/submodules.md`.
|
||||
- **Move an embedded `.git` into `.git/modules/`**: `rtk git submodule absorbgitdirs [<path>...]` — needed when a submodule was created or copied without going through `git submodule add`. Details: `references/submodules.md`.
|
||||
To run one command across every submodule: `rtk git submodule foreach --recursive '<cmd>'`. Inside
|
||||
`<cmd>`, Git sets `$name`, `$sm_path`, `$displaypath`, `$sha1` and `$toplevel`; append `|| :` to
|
||||
continue past a failure instead of aborting the traversal. `$sm_path` and `$displaypath` name the
|
||||
same directory from different vantage points — if which one you want is not obvious, read the
|
||||
variable table in `references/setup-and-update.md` before writing the command.
|
||||
|
||||
## Configuration
|
||||
## Dispatch
|
||||
|
||||
`.gitmodules` (version-controlled, shared with collaborators):
|
||||
Read only the row that matches the request.
|
||||
|
||||
| Key | Purpose |
|
||||
| Task | Reference |
|
||||
|---|---|
|
||||
| `submodule.<name>.path` | Working tree path |
|
||||
| `submodule.<name>.url` | Remote URL |
|
||||
| `submodule.<name>.branch` | Branch used by `update --remote` |
|
||||
| `submodule.<name>.update` | Default update procedure |
|
||||
| `submodule.<name>.shallow` | Recommend shallow clone |
|
||||
| Clone a superproject with submodules; add, initialize, update or re-pin one; run a command across all of them with `foreach` | `references/setup-and-update.md` |
|
||||
| Change where a submodule points — `sync`, `set-url`, `set-branch`, a local mirror override, `absorbgitdirs`, or any `.gitmodules` / `.git/config` key | `references/urls-and-config.md` |
|
||||
| Remove a submodule, or `deinit` one without removing it | `references/removal.md` |
|
||||
|
||||
`.git/config` (local only, populated by `init`):
|
||||
Removal and `deinit` are destructive: state what will be deleted and get confirmation before
|
||||
executing.
|
||||
|
||||
| Key | Purpose |
|
||||
|---|---|
|
||||
| `submodule.<name>.url` | Local URL override |
|
||||
| `submodule.<name>.update` | Local procedure override |
|
||||
| `submodule.fetchJobs` | Default parallelism for `update --jobs` |
|
||||
| `submodule.recurse` | Auto-recurse submodule updates on `pull`/`push`/etc. |
|
||||
## Output format
|
||||
|
||||
```bash
|
||||
rtk git config submodule.recurse true # keep submodules pinned automatically after every pull
|
||||
```
|
||||
|
||||
## Agent output format
|
||||
|
||||
Return results as structured data:
|
||||
```
|
||||
operation: <clone|add|init|update|sync|set-url|set-branch|status|summary|absorbgitdirs|remove>
|
||||
```yaml
|
||||
operation: <clone|add|init|update|status|sync|set-url|set-branch|absorbgitdirs|deinit|remove>
|
||||
status: <success|error|partial>
|
||||
message: <human-readable summary>
|
||||
message: <one line; include git's own output on error>
|
||||
details:
|
||||
- <submodule-path>: <state>
|
||||
conflicts: [<submodule-path>, ...] # if any
|
||||
next_step: <recovery action if applicable>
|
||||
conflicts: [<submodule-path>, ...]
|
||||
next_step: <recovery action, when status is error or partial>
|
||||
```
|
||||
|
||||
For errors, include the git command output and recommend recovery (e.g., `git submodule deinit`, force-update, or URL override).
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
---
|
||||
metadata:
|
||||
source_keys:
|
||||
- git-scm-submodule-docs
|
||||
source_keys:
|
||||
- git-scm-submodule-docs
|
||||
---
|
||||
|
||||
# References
|
||||
|
||||
## submodules.md
|
||||
One file per task branch in SKILL.md's dispatch table. Load only the one that matches the request.
|
||||
|
||||
Deep-dive reference: full `update` flag table, workflow patterns (clone, add, keep-pinned, update-to-latest, override URL), the complete safe-removal sequence, `absorbgitdirs`, and `foreach` shell variables. Load when SKILL.md's condensed Operations list isn't enough detail.
|
||||
## setup-and-update.md
|
||||
|
||||
Cloning a superproject that has submodules, adding a dependency as a submodule, initializing
|
||||
without cloning, updating or re-pinning, and running one command across every submodule. Carries
|
||||
the `add` and `update` flag tables, the keep-pinned and move-the-pin-forward workflows, and the
|
||||
`foreach` shell-variable table (`$name`, `$sm_path`, `$displaypath`, `$sha1`, `$toplevel`).
|
||||
|
||||
## urls-and-config.md
|
||||
|
||||
Where a submodule points and how it is configured: the `.gitmodules` vs `.git/config` split, both
|
||||
key tables, `sync` / `set-url` / `set-branch`, local mirror overrides, relative URL resolution, the
|
||||
security gate on custom `update` commands, and `absorbgitdirs`.
|
||||
|
||||
## removal.md
|
||||
|
||||
Removing a submodule, and why `deinit` alone does not remove one. Carries the full four-step
|
||||
removal sequence including the manual `.git/modules/<name>/` cleanup.
|
||||
|
||||
## sources.md
|
||||
|
||||
Research sources that informed this skill — provenance chain for git-scm-submodule-docs reference material.
|
||||
Research sources that informed this skill — provenance chain for git-scm-submodule-docs reference
|
||||
material.
|
||||
|
||||
32
plugins/git/.apm/skills/git-submodules/references/removal.md
Normal file
32
plugins/git/.apm/skills/git-submodules/references/removal.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
topic: submodules
|
||||
source_keys:
|
||||
- git-scm-submodule-docs
|
||||
---
|
||||
|
||||
# Removing and deinitializing a submodule
|
||||
|
||||
Both operations are destructive. Confirm with the user before executing either.
|
||||
|
||||
## `deinit` is not removal
|
||||
|
||||
```bash
|
||||
rtk git submodule deinit <path> # --all for every submodule, -f if locally modified
|
||||
```
|
||||
|
||||
`deinit` clears the submodule's section from `.git/config` and empties its working tree. The
|
||||
`.gitmodules` entry and the gitlink in the superproject's index are untouched, so the submodule is
|
||||
still registered and a later `update --init` brings it straight back. Use it to reclaim disk space
|
||||
or to reset a broken checkout, not to delete a dependency.
|
||||
|
||||
## Full removal, in order
|
||||
|
||||
```bash
|
||||
rtk git submodule deinit -f <path> # unregister from .git/config
|
||||
rtk git rm <path> # drop the .gitmodules entry and the gitlink from the index
|
||||
rm -rf .git/modules/<name>/ # stale git dir: not tracked, not cleaned up by git
|
||||
rtk git commit -m "chore: remove <name> submodule"
|
||||
```
|
||||
|
||||
The third step is the one that gets skipped. `.git/modules/<name>/` survives `rtk git rm`, and while it
|
||||
is present Git refuses to add a submodule at the same path again.
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
topic: submodules
|
||||
source_keys:
|
||||
- git-scm-submodule-docs
|
||||
---
|
||||
|
||||
# Adding, initializing, updating and pinning submodules
|
||||
|
||||
## Clone a superproject that already has submodules
|
||||
|
||||
```bash
|
||||
rtk git clone --recurse-submodules <url> # Git 2.13+, one step
|
||||
# or, against an existing clone
|
||||
rtk git submodule update --init --recursive
|
||||
```
|
||||
|
||||
## Add a dependency as a submodule
|
||||
|
||||
```bash
|
||||
rtk git submodule add <url> <path>
|
||||
rtk git commit -m "chore: add <name> as submodule"
|
||||
```
|
||||
|
||||
`add` stages a `.gitmodules` entry and a gitlink — the commit is still required. Flags:
|
||||
|
||||
| Flag | Meaning |
|
||||
|---|---|
|
||||
| `-b <branch>` | Track a branch (`submodule.<name>.branch`) instead of only a pinned commit |
|
||||
| `--depth <n>` | Shallow clone |
|
||||
| `-f` | Force past a gitignored path or a name conflict |
|
||||
| `--name <name>` | Logical name differing from the path |
|
||||
|
||||
## Initialize without cloning
|
||||
|
||||
`rtk git submodule init [<path>...]` copies submodule URLs from `.gitmodules` into `.git/config` and
|
||||
does nothing else. This is the point at which a local URL override can be edited before any fetch
|
||||
happens. If a local mirror override is wanted, read `references/urls-and-config.md` before running
|
||||
`update`. Use `update --init` to run both steps at once.
|
||||
|
||||
## Update
|
||||
|
||||
`rtk git submodule update --init --recursive` is the common case: it clones what is missing and checks
|
||||
out the commit the superproject recorded, in detached HEAD.
|
||||
|
||||
| Flag | Meaning |
|
||||
|---|---|
|
||||
| `--init` | Run `init` first, avoiding a separate step |
|
||||
| `--remote` | Use the submodule's remote branch tip instead of the superproject's recorded commit |
|
||||
| `--checkout` | Detached HEAD at the recorded commit (default) |
|
||||
| `--rebase` | Rebase the current branch onto the recorded commit |
|
||||
| `--merge` | Merge the recorded commit into the current branch |
|
||||
| `--recursive` | Operate on nested submodules |
|
||||
| `--jobs <n>` | Parallel clone (defaults to `submodule.fetchJobs`) |
|
||||
| `-N` / `--no-fetch` | Skip the remote fetch |
|
||||
| `-f` | Discard local changes in the submodule working tree |
|
||||
| `--depth <n>` | Shallow clone |
|
||||
| `--filter <spec>` | Partial clone filter |
|
||||
|
||||
## Keep submodules pinned to the recorded commit
|
||||
|
||||
```bash
|
||||
rtk git submodule update --recursive # after every rtk git pull
|
||||
rtk git config submodule.recurse true # or do it automatically on pull/push/checkout
|
||||
```
|
||||
|
||||
## Move the pin forward to the tracked branch tip
|
||||
|
||||
```bash
|
||||
rtk git submodule update --remote --merge --recursive
|
||||
rtk git commit -am "chore: update submodules to latest"
|
||||
```
|
||||
|
||||
`--remote` uses `submodule.<name>.branch` when it is set; without it Git falls back to the remote's
|
||||
default branch. Commit the superproject afterwards or the new pin is lost on the next `update`.
|
||||
|
||||
## Run one command across every submodule
|
||||
|
||||
```bash
|
||||
rtk git submodule foreach --recursive '<command>'
|
||||
rtk git submodule foreach 'git pull origin main || :' # || : continues past a failure
|
||||
```
|
||||
|
||||
`<command>` runs inside each submodule's own working tree, so the git calls in it are the
|
||||
submodule's own — that is the one place a bare `git` is correct. Append `|| :` to keep the
|
||||
traversal going instead of aborting at the first failure.
|
||||
|
||||
Git exports five shell variables into `<command>`. `$sm_path` and `$displaypath` name the same
|
||||
directory from different vantage points and are not interchangeable:
|
||||
|
||||
| Variable | Meaning |
|
||||
|---|---|
|
||||
| `$name` | Logical submodule name (the `.gitmodules` section name, which need not match the path) |
|
||||
| `$sm_path` | Path relative to the superproject root |
|
||||
| `$displaypath` | Path relative to the current working directory |
|
||||
| `$sha1` | Commit SHA the superproject has recorded for this submodule |
|
||||
| `$toplevel` | Absolute path of the superproject's root |
|
||||
@@ -14,16 +14,18 @@ source_keys:
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (all sections)
|
||||
- references/submodules.md (all sections)
|
||||
- references/setup-and-update.md (all sections)
|
||||
- references/urls-and-config.md (all sections)
|
||||
- references/removal.md (all sections)
|
||||
|
||||
---
|
||||
|
||||
Other source keys extracted during the git plugin research phase inform sibling skills in the git workflow suite, not this one:
|
||||
|
||||
- `context7-git-htmldocs` — git:branches, git:history, git:remotes
|
||||
- `git-scm-docs` — git:configuration
|
||||
- `git-scm-worktree-docs` — git:worktrees
|
||||
- `nvie-gitflow-post`, `atlassian-gitflow-tutorial`, `gitflow-cheatsheet` — git:branches
|
||||
- `conventional-commits-spec`, `commitlint-config-conventional` — git:commits
|
||||
- `git-scm-push-docs`, `git-scm-fetch-docs`, `git-scm-pull-docs`, `git-scm-remote-docs` — git:remotes
|
||||
- `git-scm-bisect-docs`, `git-scm-log-docs`, `git-scm-diff-docs` — git:history
|
||||
- `context7-git-htmldocs` — git-branches, git-history, git-remotes
|
||||
- `git-scm-docs` — no current skill; it backed a git-configuration skill that no longer exists and survives here as provenance only
|
||||
- `git-scm-worktree-docs` — git-worktrees
|
||||
- `nvie-gitflow-post`, `atlassian-gitflow-tutorial`, `gitflow-cheatsheet` — git-branches
|
||||
- `conventional-commits-spec`, `commitlint-config-conventional` — git-commits
|
||||
- `git-scm-push-docs`, `git-scm-fetch-docs`, `git-scm-pull-docs`, `git-scm-remote-docs` — git-remotes
|
||||
- `git-scm-bisect-docs`, `git-scm-log-docs`, `git-scm-diff-docs` — git-history
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
---
|
||||
topic: submodules
|
||||
source_keys:
|
||||
- git-scm-submodule-docs
|
||||
---
|
||||
|
||||
# Submodules — Deep Reference
|
||||
|
||||
## Update flag reference
|
||||
|
||||
| Flag | Meaning |
|
||||
|---|---|
|
||||
| `--init` | Run init first (avoids a separate step) |
|
||||
| `--remote` | Use the submodule's remote branch tip instead of the superproject's recorded commit |
|
||||
| `--checkout` | Detached HEAD at recorded commit (default) |
|
||||
| `--rebase` | Rebase current branch onto recorded commit |
|
||||
| `--merge` | Merge recorded commit into current branch |
|
||||
| `--recursive` | Operate on nested submodules |
|
||||
| `--jobs <n>` | Parallel clone (defaults to `submodule.fetchJobs`) |
|
||||
| `-N` / `--no-fetch` | Skip remote fetch |
|
||||
| `--depth <n>` | Shallow clone |
|
||||
| `--filter <spec>` | Partial clone filter |
|
||||
|
||||
## Workflow patterns
|
||||
|
||||
### Clone a repo with submodules
|
||||
```bash
|
||||
git clone --recurse-submodules <url> # Git 2.13+, one step
|
||||
# or
|
||||
git clone <url>
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
### Add a dependency as a submodule
|
||||
```bash
|
||||
git submodule add https://github.com/org/lib.git libs/lib
|
||||
git commit -m "chore: add lib as submodule"
|
||||
```
|
||||
|
||||
### Keep submodules pinned to the superproject's recorded commit
|
||||
```bash
|
||||
git submodule update --recursive # after every git pull
|
||||
git config submodule.recurse true # do this automatically on pull
|
||||
```
|
||||
|
||||
### Update submodules to the latest commit on their tracked branch
|
||||
```bash
|
||||
git submodule update --remote --merge --recursive
|
||||
git commit -am "chore: update submodules to latest"
|
||||
```
|
||||
|
||||
### Override a submodule URL locally (private mirror)
|
||||
```bash
|
||||
git submodule init
|
||||
# edit .git/config: submodule.<name>.url = <mirror-url>
|
||||
git submodule update
|
||||
```
|
||||
Local-only override (`.git/config`, not `.gitmodules`) — doesn't propagate to collaborators. Re-running `sync` overwrites it with the `.gitmodules` URL.
|
||||
|
||||
## Removal, in full
|
||||
|
||||
`deinit` alone does not remove a submodule — it only clears `.git/config` and empties the working tree. To fully remove:
|
||||
```bash
|
||||
git submodule deinit -f <path> # unregister from .git/config
|
||||
git rm <path> # remove .gitmodules entry + gitlink from index
|
||||
rm -rf .git/modules/<name>/ # stale git dir; not tracked by git, not auto-cleaned
|
||||
git commit -m "chore: remove <name> submodule"
|
||||
```
|
||||
`.git/modules/<name>/` persisting after `git rm` will block re-adding the same path until manually deleted.
|
||||
|
||||
## Relocate an embedded `.git` directory
|
||||
|
||||
```bash
|
||||
git submodule absorbgitdirs [<path>...]
|
||||
```
|
||||
Moves a submodule's own `.git` directory into the superproject's `.git/modules/<name>/`, linking it back with a `.git` pointer file. Needed when a submodule was created or copied without going through `git submodule add` (e.g. converting a plain nested repo into a proper submodule).
|
||||
|
||||
## `foreach` shell variables
|
||||
|
||||
Available inside the `<command>` argument to `git submodule foreach`:
|
||||
|
||||
| Variable | Meaning |
|
||||
|---|---|
|
||||
| `$name` | Logical submodule name |
|
||||
| `$sm_path` | Path relative to superproject root |
|
||||
| `$displaypath` | Path relative to current working directory |
|
||||
| `$sha1` | Recorded commit SHA |
|
||||
| `$toplevel` | Superproject's root path |
|
||||
|
||||
```bash
|
||||
git submodule foreach --recursive '<command>'
|
||||
git submodule foreach 'git pull origin main || :' # || : continues past failures
|
||||
```
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
topic: submodules
|
||||
source_keys:
|
||||
- git-scm-submodule-docs
|
||||
---
|
||||
|
||||
# Where a submodule points, and how it is configured
|
||||
|
||||
## Two files, two audiences
|
||||
|
||||
- **`.gitmodules`** — version-controlled, shared with collaborators. Defines each submodule's
|
||||
logical name, path, and canonical URL.
|
||||
- **`.git/config`** — local only, populated by `rtk git submodule init`. Local URL overrides live here
|
||||
and never propagate to another clone.
|
||||
|
||||
The submodule's own `.git` directory lives at `.git/modules/<name>/` in the superproject and is
|
||||
linked to the submodule's working tree by a `.git` pointer file.
|
||||
|
||||
## `.gitmodules` keys
|
||||
|
||||
| Key | Purpose |
|
||||
|---|---|
|
||||
| `submodule.<name>.path` | Working tree path |
|
||||
| `submodule.<name>.url` | Remote URL |
|
||||
| `submodule.<name>.branch` | Branch used by `update --remote` |
|
||||
| `submodule.<name>.update` | Default update procedure |
|
||||
| `submodule.<name>.shallow` | Recommend a shallow clone |
|
||||
|
||||
## `.git/config` keys
|
||||
|
||||
| Key | Purpose |
|
||||
|---|---|
|
||||
| `submodule.<name>.url` | Local URL override |
|
||||
| `submodule.<name>.update` | Local procedure override |
|
||||
| `submodule.fetchJobs` | Default parallelism for `update --jobs` |
|
||||
| `submodule.recurse` | Auto-recurse submodule updates on `pull`/`push`/etc. |
|
||||
|
||||
## Rebind a URL or branch
|
||||
|
||||
```bash
|
||||
rtk git submodule sync --recursive # push .gitmodules URLs into .git/config
|
||||
rtk git submodule set-url <path> <url> # change the canonical URL
|
||||
rtk git submodule set-branch -b <branch> <path> # set the branch used by update --remote
|
||||
```
|
||||
|
||||
Run `sync` after an upstream rename: existing clones keep the stale URL in `.git/config` until
|
||||
they do.
|
||||
|
||||
## Override a URL locally (private mirror)
|
||||
|
||||
```bash
|
||||
rtk git submodule init
|
||||
# edit .git/config: submodule.<name>.url = <mirror-url>
|
||||
rtk git submodule update
|
||||
```
|
||||
|
||||
Local-only, invisible to collaborators, and overwritten by the next `sync`.
|
||||
|
||||
## Relative URLs
|
||||
|
||||
A `../foo.git` entry in `.gitmodules` resolves against the superproject's default remote URL, not
|
||||
against the filesystem. It is portable across hosts that mirror the same layout and broken
|
||||
everywhere else.
|
||||
|
||||
## Custom `update` commands are security-gated
|
||||
|
||||
A `.gitmodules` entry of `update = !some-command` is never copied into `.git/config` by
|
||||
`rtk git submodule init`. That is deliberate: it stops a hostile clone from silently executing
|
||||
arbitrary code. Setting it locally in `.git/config` is the only way to enable it.
|
||||
|
||||
## Relocate an embedded `.git` directory
|
||||
|
||||
```bash
|
||||
rtk git submodule absorbgitdirs [<path>...]
|
||||
```
|
||||
|
||||
Moves a submodule's own `.git` directory into `.git/modules/<name>/` and leaves a `.git` pointer
|
||||
file behind. Needed when a nested repository was created or copied in without going through
|
||||
`rtk git submodule add`.
|
||||
@@ -4,7 +4,7 @@ Human-friendly interface for interactive git workflows with conversational promp
|
||||
|
||||
## What it does
|
||||
|
||||
This skill wraps the `git-orchestrate` agent to provide an interactive, educational interface for humans performing git workflows. It handles commits, branch management, history inspection, submodules, worktrees, and remotes. The skill parses user intent, gathers session context, invokes the orchestrator, and presents results in plain language with inline help, progress updates, and explanations of what's happening. It enforces confirmation gates for destructive operations (force-push, branch deletion, rebasing with history loss, force-checkout) and provides best-practices guidance throughout.
|
||||
This skill wraps the `git-orchestrate` agent to provide an interactive, educational interface for humans performing git workflows. It is the router for the six local-git domain skills — `git-commits`, `git-branches`, `git-history`, `git-remotes`, `git-submodules` and `git-worktrees` — and `SKILL.md` carries a table mapping each of them to the requests it owns, so an ambiguous request resolves to exactly one domain before anything runs. The skill parses user intent, gathers session context, invokes the orchestrator, and presents results in plain language with inline help, progress updates, and explanations of what's happening. It enforces confirmation gates for destructive operations (force-push, branch deletion, rebasing with history loss, force-checkout) and provides best-practices guidance throughout. The org's non-negotiable git rules live in `references/hard-rules.md` and are loaded only when a request could conflict with one.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -18,7 +18,8 @@ Describe your git workflow: commit, create a branch, rebase, inspect history, ma
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `SKILL.md` | Skill instructions for agents — the six-domain routing table, the workflow steps, and the interaction style |
|
||||
| `README.md` | This file |
|
||||
| `references/hard-rules.md` | The org's non-negotiable git rules; read when a request creates, amends, or rewrites a commit, pushes, or touches hooks, config, or credentials |
|
||||
| `references/README.md` | Describes the references directory contents |
|
||||
| `references/sources.md` | Research sources and provenance |
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
name: git-workflow
|
||||
|
||||
description: >
|
||||
Use when a human user wants to perform git workflows interactively — commits, branch management,
|
||||
history inspection, submodules, worktrees, or remotes. Provides a friendly, conversational
|
||||
interface with clarification prompts ("Which branch base?"), progress updates, inline help,
|
||||
best practices guidance, and confirmation dialogs for destructive operations. Guides users
|
||||
through complex git patterns even if they don't mention every detail. Do not use when the
|
||||
caller is an agent—agents should invoke git-orchestrate directly for deterministic, composable execution.
|
||||
Use when a human's local git request is general or ambiguous — it routes to the owning
|
||||
domain skill: `git-commits`, `git-branches`, `git-history`, `git-remotes`, `git-submodules`
|
||||
or `git-worktrees`. An unambiguous request goes straight to its domain skill instead. Not an
|
||||
agent caller -> `git-orchestrate`. Not Gitea -> `gitea-workflow`.
|
||||
|
||||
metadata:
|
||||
category: git
|
||||
@@ -21,45 +19,61 @@ metadata:
|
||||
|
||||
## Gotchas
|
||||
|
||||
- This skill is specifically for **human interaction**. If the caller is an agent, invoke `git-orchestrate` directly instead—this skill adds UI overhead agents don't need.
|
||||
- Session context from previous git operations (branch names, commit strategy) persists during a single multi-step user request, then clears. Users don't need to re-provide decisions within one workflow.
|
||||
- Destructive operations require explicit confirmation: force-push, branch deletion, rebase with history loss, force-checkout. Users must confirm interactively; the skill never proceeds without their approval on destructive ops.
|
||||
- Run git commands through `rtk git <command>` rather than bare `git <command>` for parent-repo operations — this is a mandated org wrapper, not an optional style choice. Drop into a submodule's own directory for submodule-specific commands (see `git-submodules`).
|
||||
- Session context built during one multi-step request — branch names, the chosen base, the commit
|
||||
strategy — persists for that request and then clears. Do not re-ask the user for a decision they
|
||||
already gave you earlier in the same workflow.
|
||||
- Run parent-repo commands through `rtk git <command>`, never bare `git <command>`. This is a
|
||||
mandated org wrapper, not a style preference. Submodule-specific commands run from inside the
|
||||
submodule's own directory instead.
|
||||
|
||||
### Hard rules
|
||||
## Domains
|
||||
|
||||
These are non-negotiable regardless of what the user asks for — surface them proactively rather than waiting for the user to hit them (`org-git-conventions`; sub-skills invoked directly by humans, like this one, carry their own local copy of these rules for readers who won't chain through `git-orchestrate`, so state them plainly rather than assuming the user already knows them):
|
||||
Route to the domain that owns the operation, and in sequence when a request spans two — a rebase
|
||||
that ends in a force-push is `git-commits`, then `git-remotes`. An unambiguous request should have
|
||||
gone straight to the domain skill; this one exists for the ones that did not.
|
||||
|
||||
- Never skip hooks with `--no-verify` — hooks are the automated QA gate, and bypassing them breaks the pipeline for everyone downstream.
|
||||
- Never force-push `main` or `master`.
|
||||
- Keep commits atomic — each commit should represent one logical, independently reviewable and reversible change.
|
||||
- Every commit must leave the repository in a working state (buildable/testable where practical).
|
||||
- Commit messages explain **why**, not **what** — the diff already documents what changed.
|
||||
- Never commit secrets, credentials, or environment-specific config.
|
||||
- Use Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`, etc.).
|
||||
- Reference related issues, ADRs, or design documents using Git trailers when applicable.
|
||||
| The request is about | Domain |
|
||||
|---|---|
|
||||
| Writing, amending, squashing, or cherry-picking a commit, and its message | `git-commits` |
|
||||
| Rebasing onto a new base, or undoing a commit with `reset` | `git-commits` |
|
||||
| Creating, switching, deleting, renaming, tracking, or merging a local branch | `git-branches` |
|
||||
| Stashing work in progress to move between branches | `git-branches` |
|
||||
| When a change landed, which commit broke something, what to revert or backport | `git-history` |
|
||||
| Anything touching a remote — remote config, fetch, push, pull — even unnamed | `git-remotes` |
|
||||
| A nested repository pinned inside this one by a recorded commit | `git-submodules` |
|
||||
| Several branches checked out at once, in separate directories, without stashing | `git-worktrees` |
|
||||
|
||||
If a user's request conflicts with a hard rule (e.g. "force-push main to fix this"), explain the rule and propose a safe alternative instead of complying.
|
||||
`git-orchestrate` executes whatever this resolves to (step 5); the table only decides which domain
|
||||
owns the request.
|
||||
|
||||
## Workflow
|
||||
|
||||
When a user wants to perform git workflows:
|
||||
|
||||
1. **Parse the user's intent** — extract the high-level task (commit, create branch, rebase, inspect history, etc.) and any explicit options they mentioned.
|
||||
2. **Build session context** — gather repo state, current branch, any prior decisions in this workflow (branch intent for commit messages, base branch for rebasing, etc.).
|
||||
3. **Invoke git-orchestrate agent** — call it with:
|
||||
- `operation`: the git operation (e.g., "commit", "create-branch", "rebase")
|
||||
- `parameters`: user-provided or inferred options
|
||||
- `context`: decisions and repo state from prior steps in this workflow
|
||||
- `confirm`: `true` if a destructive op and the user confirmed, otherwise omit
|
||||
4. **Handle the response** — if orchestrator succeeds, present results in plain language with progress updates and explanations. If it fails, show the error reason and suggest recovery actions.
|
||||
5. **Clarification prompts** — if the orchestrator needs more information (e.g., "Which branch should this be based on?"), prompt the user conversationally and loop back with the user's input.
|
||||
6. **Confirmation gates** — before executing any destructive op (force-push, branch deletion, rebase, force-checkout), show what will happen and ask "Proceed?" If the user declines, cancel gracefully.
|
||||
1. **Parse intent** — extract the operation and, from the table above, the domain that owns it,
|
||||
plus any options the user named.
|
||||
2. **Check the hard rules** — if the request creates, amends, or rewrites a commit, pushes, or
|
||||
touches hooks, config, or credentials, read `references/hard-rules.md`. Raise the relevant rule
|
||||
before acting, not after.
|
||||
3. **Read the repo** — current branch, working-tree state, and which branching model the repo
|
||||
follows (the orchestrator reads `branching_pattern` from plugin config; infer from branch names
|
||||
if absent); the last of those decides which tips are worth offering.
|
||||
4. **Gate destructive operations** — before force-push, branch deletion, rebase, or
|
||||
force-checkout, show what will happen and ask "Proceed?". Cancel gracefully if the user
|
||||
declines. Never supply the confirmation on the user's behalf. Some operations are refusals, not
|
||||
confirmations: never offer "Proceed?" for a force-push of `main` or `master`.
|
||||
5. **Invoke the `git-orchestrate` agent** with `operation`, `parameters` (user-provided or
|
||||
inferred), `context` (step 3 plus the session context), and `confirm: true` only for a
|
||||
destructive op the user approved in step 4.
|
||||
6. **Clarify when the orchestrator asks for more** — put its question to the user in plain
|
||||
language ("Which branch should this be based on?") and loop back to step 5 with the answer.
|
||||
7. **Report the outcome** — on success, the result and what changed, in plain language; on
|
||||
failure, the error reason and a recovery action.
|
||||
|
||||
## Interaction style
|
||||
|
||||
- **Conversational**: Use natural language, not technical jargon. "Let me rebase your changes onto main" not "Running git rebase --interactive main".
|
||||
- **Pedagogical**: Explain what each step does and why. "I'm squashing your last 3 commits into one clean commit" not just "Squashing commits".
|
||||
- **Guided**: Offer inline help. When users mention ambiguous steps, suggest best practices. Match the tip to the repo's branching model: for Gitflow-style repos, "Tip: Feature branches branch off `develop`, not `main` — `main` only tracks released code." For trunk-based/GitHub Flow repos, "Tip: Short-lived feature branches off `main` keep merges small and reviewable."
|
||||
- **Transparent**: Show progress. "Creating branch feature/user-auth..." then "✓ Branch created. Ready to commit." Humans benefit from seeing workflow state.
|
||||
- **Safe**: Always confirm before destructive ops. Never silently rewrite history or force-push without explicit user approval.
|
||||
The caller is a human, so the interaction is the point. Explain each step and why it happens ("I'm
|
||||
squashing your last 3 commits into one clean commit" beats "Squashing commits"), show progress as
|
||||
you go, and prefer natural language to raw command lines.
|
||||
|
||||
Match tips to the repo's branching model rather than offering generic advice: on a Gitflow repo,
|
||||
feature branches come off `develop` and `main` tracks only released code; on a trunk-based or
|
||||
GitHub Flow repo, short-lived branches off `main` keep merges small and reviewable.
|
||||
|
||||
@@ -9,8 +9,11 @@ source_keys:
|
||||
|
||||
# References
|
||||
|
||||
This directory contains provenance metadata and research sources for the `git-workflow` skill.
|
||||
This directory contains the org git rules and the provenance metadata for the `git-workflow`
|
||||
skill.
|
||||
|
||||
## Files
|
||||
|
||||
- `hard-rules.md` — The org's non-negotiable git rules, loaded when a request creates, amends, or
|
||||
rewrites a commit, pushes, or touches hooks, config, or credentials
|
||||
- `sources.md` — Extracted research sources and their contributing documents
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
source_keys:
|
||||
- org-git-conventions
|
||||
---
|
||||
|
||||
# Org git hard rules
|
||||
|
||||
Non-negotiable regardless of what the user asks for. Surface the relevant one proactively rather
|
||||
than waiting for the user to hit it — a human invoking this skill directly never sees the
|
||||
orchestrator's copy of these rules, so raise them here.
|
||||
|
||||
- Never skip hooks with `--no-verify` — hooks are the automated QA gate, and bypassing them breaks
|
||||
the pipeline for everyone downstream.
|
||||
- Never force-push `main` or `master`.
|
||||
- Keep commits atomic — each commit should represent one logical, independently reviewable and
|
||||
reversible change.
|
||||
- Every commit must leave the repository in a working state (buildable/testable where practical).
|
||||
- Commit messages explain **why**, not **what** — the diff already documents what changed.
|
||||
- Never commit secrets, credentials, or environment-specific config.
|
||||
- Use Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`, etc.).
|
||||
- Reference related issues, ADRs, or design documents using Git trailers when applicable.
|
||||
|
||||
If a user's request conflicts with one of these (e.g. "force-push main to fix this"), explain the
|
||||
rule and propose a safe alternative instead of complying. Do not comply and note the rule
|
||||
afterwards.
|
||||
@@ -56,4 +56,5 @@
|
||||
- **Research doc:** none — org convention, not part of the plugin's research corpus (no `plugins/git/docs/research/` topic file backs this entry)
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Gotchas — Hard rules subsection, rtk git note)
|
||||
- references/hard-rules.md (whole file — the eight hard rules and the conflict-handling rule)
|
||||
- SKILL.md (Gotchas — `rtk git` wrapper note)
|
||||
|
||||
@@ -4,7 +4,7 @@ Manage git worktrees to enable multi-branch parallel development across isolated
|
||||
|
||||
## What it does
|
||||
|
||||
This skill handles worktree operations within the git workflow suite. It creates, lists, locks/unlocks, moves, removes, prunes, and repairs worktrees — letting an agent work on multiple branches simultaneously without stashing. It returns structured results (paths, branches, lock status) suitable for agent composition.
|
||||
This skill handles worktree operations within the git workflow suite. It creates, lists, locks/unlocks, moves, removes, prunes, and repairs worktrees — letting an agent work on multiple branches simultaneously without stashing. It returns structured results (paths, branches, lock status) suitable for agent composition. For multi-step flows spanning branch strategy plus worktree setup, `git-workflow` handles the broader orchestration and delegates the worktree mechanics here.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -18,7 +18,7 @@ Describe your worktree task: create a worktree for a branch, list existing workt
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `SKILL.md` | Dispatch table, per-operation gates, and the report format |
|
||||
| `references/README.md` | Describes the references directory contents |
|
||||
| `references/worktrees.md` | Full `add` flag table, sparse-checkout, removable-media locking, remote disambiguation, configuration |
|
||||
| `references/worktrees.md` | Read when an operation needs more than the dispatch table: shared vs. per-worktree state, the `add` command forms, the full `add` flag table, orphan branches, sparse-checkout, removable-media locking, remote disambiguation, where to run `repair` from, config keys, and the emergency-fix and PR-review patterns |
|
||||
| `references/sources.md` | Research sources and provenance |
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
name: git-worktrees
|
||||
|
||||
description: >
|
||||
Manage Git worktrees to enable multi-branch parallel development across isolated directories.
|
||||
Use when the user needs to work on multiple branches simultaneously without stashing, switch between feature/hotfix/experimental work, or coordinate code reviews alongside ongoing development.
|
||||
Handles creation, listing, locking, moving, removal, pruning, and repair of worktrees.
|
||||
Provides structured results (paths, branches, lock status) for agent composition in git orchestration workflows.
|
||||
Do not use when only inspecting a single branch or when the user needs standard checkout/stash workflows.
|
||||
Use when working on several branches at once without stashing —
|
||||
manages the full lifecycle of a git worktree.
|
||||
Not ordinary branch switching or checkout -> `git-branches`.
|
||||
Not interactive multi-step git guidance -> `git-workflow`.
|
||||
|
||||
metadata:
|
||||
category: git
|
||||
@@ -14,101 +13,45 @@ metadata:
|
||||
- git-scm-worktree-docs
|
||||
---
|
||||
|
||||
## Concept
|
||||
|
||||
A worktree lets you check out multiple branches simultaneously from one repository, each in its own directory. All worktrees share the same objects, config, and most refs (`refs/`). Each worktree has its own `HEAD`, index, and per-worktree metadata (`ORIG_HEAD`, `MERGE_HEAD`, `refs/bisect/`, `refs/worktree/`, `refs/rewritten/`) stored at `$GIT_DIR/worktrees/<name>/`. The **main worktree** (from `git init`/`git clone`) is exactly one per repo and cannot be removed; **linked worktrees** are the additional ones created via `git worktree add`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **A branch can only be checked out in one worktree at a time.** Attempting `git worktree add` for an already-checked-out branch fails unless you pass `--force`. Use `--force` only when intentional.
|
||||
- **Submodules are unsupported and block operations.** Repos with submodules have incomplete worktree support. Worktrees containing submodules cannot be moved and require `--force` to remove.
|
||||
- **Never manually `rm -rf` a worktree directory.** This leaves stale metadata in `$GIT_DIR/worktrees/`. Always use `git worktree remove`. If already deleted, run `git worktree prune` to clean up.
|
||||
- **Manual moves break bidirectional pointers.** If a worktree directory is moved outside of `git worktree move`, run `git worktree repair` to fix connections.
|
||||
- **Force-flag escalation with locks.** Removing or moving a locked worktree requires `-ff` (two flags), not just `-f`.
|
||||
- **Worktree identification is by full path, unique basename, or unique partial path.** Ambiguous names error. Use `git worktree list` to see available identifiers.
|
||||
- **`--lock` on `add` is atomic; create-then-lock has a race window.** Use `--lock` directly on `git worktree add` when consistency matters.
|
||||
- **`extensions.worktreeConfig = true` is a one-way door.** It enables per-worktree config (`git config --worktree ...`) but makes the repo refuse to open in older Git versions. Once set, `core.bare`/`core.worktree` must live in `config.worktree`, not `config`. Don't enable it unless per-worktree config is actually needed.
|
||||
- **A branch can be checked out in only one worktree at a time.** `git worktree add` on an already-checked-out branch fails; `--force` is the only override, so use it only deliberately.
|
||||
- **Never `rm -rf` a worktree directory.** That strands metadata in `$GIT_DIR/worktrees/`. Use `git worktree remove`, or `git worktree prune` afterwards.
|
||||
- **Submodules break worktree support.** A worktree containing submodules cannot be moved at all, and needs `--force` to remove.
|
||||
- **`extensions.worktreeConfig = true` is a one-way door.** Without it, `git config --worktree` errors; with it, that flag writes to the worktree's own `config.worktree` file, and `core.bare`/`core.worktree` are forced there too. It also breaks older Git. Leave it off unless per-worktree config is needed.
|
||||
|
||||
## Common Operations
|
||||
## Step 1 — Dispatch
|
||||
|
||||
**Create and switch to a new worktree** — default approach:
|
||||
```bash
|
||||
git worktree add -b <new-branch> <path>
|
||||
cd <path>
|
||||
```
|
||||
This creates a new branch and checks it out in a new directory. Other branches cannot be checked out elsewhere simultaneously.
|
||||
| Operation | Run |
|
||||
|---|---|
|
||||
| Create on a branch that already exists locally | `git worktree add <path> <branch>` |
|
||||
| Create on a new branch | `git worktree add -b <branch> <path>` |
|
||||
| Create on the branch named after the path basename | `git worktree add <path>` — checks that branch out if it exists, else creates it from HEAD |
|
||||
| Create and reset an existing branch to HEAD — discards its commits | `git worktree add -B <branch> <path>` |
|
||||
| Create a local branch tracking a remote one | `git worktree add --track -b <branch> <path> <remote>/<branch>` — always correct. `git worktree add <path> <branch>` expands to exactly this, but **only** under the conditions in `references/worktrees.md` |
|
||||
| Throwaway experiment, no branch | `git worktree add -d <path>` — detached HEAD |
|
||||
| **Never** `git worktree add <path> <remote>/<branch>` | That ref resolves, so the shortcut never fires and you get **a detached HEAD, no branch, no upstream**. Commits there go unreachable once HEAD moves, and `git push` needs an explicit refspec. Use the tracking row above |
|
||||
| List | `git worktree list -v`, or `--porcelain -z` to parse |
|
||||
| Lock or unlock | `git worktree lock [--reason <str>] <path>` / `git worktree unlock <path>` |
|
||||
| Move | `git worktree move <from> <to>` |
|
||||
| Remove | `git worktree remove <path>` |
|
||||
| Prune stale metadata | `git worktree prune --dry-run`, then without the flag |
|
||||
| Repair after a manual move | `git worktree repair` — in the main worktree if *it* moved, or inside a linked worktree that moved. `git worktree repair <path>...` — from any worktree, naming each moved linked worktree's new path |
|
||||
|
||||
**Create-or-reset a branch**: `git worktree add -B <branch> <path>` — like `-b` but resets the branch to HEAD if it already exists.
|
||||
If the operation needs anything the table does not carry — the full `add` flag
|
||||
table, orphan branches, sparse-checkout, locking for removable media, remote
|
||||
disambiguation across several remotes, how to name a worktree unambiguously,
|
||||
worktree config keys, or the worked emergency-fix and PR-review patterns — read
|
||||
`references/worktrees.md`.
|
||||
|
||||
**Create a worktree for an existing remote branch**:
|
||||
```bash
|
||||
git worktree add <path> <remote>/<branch>
|
||||
```
|
||||
For ambiguous names across remotes, disambiguate via `checkout.defaultRemote` config or `--guess-remote`. Full flag table and detail: `references/worktrees.md`.
|
||||
Gates:
|
||||
|
||||
**Throwaway experiment in detached HEAD**:
|
||||
```bash
|
||||
git worktree add -d ../experiment # or --detach
|
||||
# experiment freely, no branch created
|
||||
git worktree remove ../experiment
|
||||
```
|
||||
- **`move`, `remove` — the main worktree cannot be moved or removed.** Only linked worktrees, the ones `git worktree add` created, are candidates.
|
||||
- **`add`, `move`, `remove` — escalate force flags one step at a time.** `-f` overrides a safeguard such as an unclean tree; `move` and `remove` need `-ff` on top of that when the worktree is locked. Confirm with the user before either — both discard state.
|
||||
- **`add` — lock at creation, not after.** `git worktree add --lock` is atomic, where add-then-`lock` leaves a window in which the worktree is unprotected.
|
||||
|
||||
**List all worktrees with state**:
|
||||
```bash
|
||||
git worktree list -v # human-readable with lock/prune reasons
|
||||
git worktree list --porcelain -z # machine-readable, NUL-terminated
|
||||
```
|
||||
## Step 2 — Report
|
||||
|
||||
**Move a worktree to a new path**:
|
||||
```bash
|
||||
git worktree move <current-path> <new-path>
|
||||
# Cannot move: main worktree, worktrees with submodules
|
||||
# To override safeguards: -f; to override locked state too: -ff
|
||||
```
|
||||
|
||||
**Remove a worktree**:
|
||||
```bash
|
||||
git worktree remove <path> # only if clean
|
||||
git worktree remove -f <path> # force-remove unclean
|
||||
git worktree remove -ff <path> # force-remove even if locked
|
||||
```
|
||||
|
||||
**Prune stale metadata**:
|
||||
```bash
|
||||
git worktree prune --dry-run # preview what would be removed
|
||||
git worktree prune # clean up orphaned metadata
|
||||
```
|
||||
Also triggered by `git gc`, controlled by `gc.worktreePruneExpire` config.
|
||||
|
||||
**Repair broken connections** (after a manual move):
|
||||
```bash
|
||||
git worktree repair # from main worktree or after it was moved
|
||||
git worktree repair <path> # reconnect a specific linked worktree
|
||||
```
|
||||
|
||||
Sparse-checkout worktrees, locking for removable media, the full `add` flag table, and the config key reference: `references/worktrees.md`.
|
||||
|
||||
## Worked Examples
|
||||
|
||||
**Emergency fix without disrupting current work** — no stashing needed, ongoing work in the main worktree is untouched:
|
||||
```bash
|
||||
git worktree add -b emergency-fix ../temp main
|
||||
cd ../temp
|
||||
# fix, commit
|
||||
git commit -a -m "fix: critical production bug"
|
||||
cd -
|
||||
git worktree remove ../temp
|
||||
```
|
||||
|
||||
**Review a PR branch alongside your current work** — no context switch, both branches stay checked out:
|
||||
```bash
|
||||
git worktree add ../review-pr-123 origin/feature-xyz
|
||||
# open ../review-pr-123 in a second editor window or terminal
|
||||
```
|
||||
|
||||
## Return Format for Agents
|
||||
|
||||
When invoking worktree operations, return structured results:
|
||||
```yaml
|
||||
worktrees:
|
||||
- path: <directory-path>
|
||||
@@ -116,10 +59,8 @@ worktrees:
|
||||
commit: <short-hash>
|
||||
locked: <true/false>
|
||||
lock_reason: <reason or empty>
|
||||
- ...
|
||||
```
|
||||
Derive these fields from `git worktree list --porcelain -z` — its `worktree`/`branch`/`HEAD`/`locked` lines map directly to `path`/`branch`/`commit`/`locked`+`lock_reason`.
|
||||
|
||||
For single operations, include the operation result (e.g., `created: true`, `removed: true`, `moved: true`).
|
||||
|
||||
For multi-step flows spanning branch strategy plus worktree setup, compose with the `git-workflow` skill — it handles the broader orchestration, this skill handles the worktree mechanics.
|
||||
Derive those fields from `git worktree list --porcelain -z`. For a single
|
||||
operation, report its outcome instead — `created: true`, `moved: true`,
|
||||
`removed: true`.
|
||||
|
||||
@@ -10,4 +10,4 @@ This directory contains provenance metadata and research sources for the `git-wo
|
||||
## Files
|
||||
|
||||
- `sources.md` — Extracted research sources and their contributing documents
|
||||
- `worktrees.md` — Full `add` flag table, sparse-checkout setup, removable-media locking, remote-branch disambiguation, and the config key reference
|
||||
- `worktrees.md` — Shared vs. per-worktree state, the `add` command forms, the full `add` flag table, orphan branches, sparse-checkout setup, removable-media locking, remote-branch disambiguation, where to run `repair` from, the config key reference, and the emergency-fix and PR-review workflow patterns
|
||||
|
||||
@@ -12,5 +12,5 @@
|
||||
- **Research doc:** plugins/git/docs/research/docs/git/worktrees.md (whole-document reference — covers `## Concept Overview`, `## Key Commands`, `## Workflow Patterns`, `## Common Gotchas`, `## Configuration`)
|
||||
|
||||
**Contributing files:**
|
||||
- SKILL.md (Concept, Gotchas, Common Operations, Worked Examples, Return Format)
|
||||
- references/worktrees.md (full `add` flag table, sparse-checkout, removable media, remote disambiguation, configuration)
|
||||
- SKILL.md (Gotchas, Step 1 dispatch table and per-operation gates, Step 2 report format)
|
||||
- references/worktrees.md (shared vs. per-worktree state, `add` command forms, full `add` flag table, orphan branches, sparse-checkout, removable media, remote disambiguation, `repair` invocation directory, configuration, workflow patterns)
|
||||
|
||||
@@ -4,6 +4,48 @@ source_keys:
|
||||
- git-scm-worktree-docs
|
||||
---
|
||||
|
||||
# Git worktrees
|
||||
|
||||
## Shared vs. per-worktree state
|
||||
|
||||
All worktrees share one object store, one config, and most refs under `refs/`. Each worktree keeps
|
||||
its own `HEAD`, index, and per-worktree metadata (`ORIG_HEAD`, `MERGE_HEAD`, `refs/bisect/`,
|
||||
`refs/worktree/`, `refs/rewritten/`) under `$GIT_DIR/worktrees/<name>/`. Exactly one **main
|
||||
worktree** exists per repo — the one `git init` or `git clone` produced — and it cannot be removed
|
||||
or moved. Every other worktree is a **linked worktree** created by `git worktree add`.
|
||||
|
||||
## Identifying a worktree
|
||||
|
||||
`lock`, `move`, `remove` and `repair` accept a full path, a unique basename, or a unique partial
|
||||
path. An ambiguous name errors rather than picking one; `git worktree list` shows the identifiers
|
||||
that are usable.
|
||||
|
||||
## `add` forms
|
||||
|
||||
```bash
|
||||
git worktree add <path> <branch> # <branch> exists locally: check it out — non-destructive
|
||||
git worktree add -b <branch> <path> # create a new branch; fails if it exists
|
||||
git worktree add <path> # branch named after $(basename <path>): checked out
|
||||
# if it exists, else created from HEAD
|
||||
git worktree add -B <branch> <path> # create the branch, or reset an existing one to HEAD,
|
||||
# discarding the commits it carried
|
||||
git worktree add --track -b <branch> <path> <remote>/<branch>
|
||||
# new local branch tracking the remote — always works
|
||||
git worktree add <path> <branch> # <branch> absent locally and in exactly one remote:
|
||||
# Git expands this to the --track -b form above
|
||||
git worktree add -d <path> # detached HEAD, no branch
|
||||
```
|
||||
|
||||
The same `git worktree add <path> <branch>` spelling appears twice above and does two
|
||||
different things: it checks out a local branch when one exists, and only otherwise falls
|
||||
through to the remote-tracking shortcut. Read the local branch list before relying on either.
|
||||
|
||||
**Do not write `git worktree add <path> <remote>/<branch>`.** A remote-tracking ref resolves as a
|
||||
commit-ish, so the tracking shortcut never fires and the worktree lands on a **detached HEAD with
|
||||
no local branch and no upstream** — commits there go unreachable once HEAD moves or the worktree is
|
||||
removed, and `git push` fails without an explicit refspec. That spelling is correct only as the
|
||||
final argument of the `--track -b` form.
|
||||
|
||||
## Full `add` flag table
|
||||
|
||||
| Flag | Meaning |
|
||||
@@ -11,7 +53,7 @@ source_keys:
|
||||
| `-b <branch>` | Create and check out a new branch; fails if it exists |
|
||||
| `-B <branch>` | Like `-b` but resets the branch if it already exists |
|
||||
| `-d` / `--detach` | Detach HEAD; useful for throwaway experiments |
|
||||
| `--orphan` | Create empty unborn branch |
|
||||
| `--orphan` | Create empty unborn branch — **Git 2.42+**; earlier versions exit 129 with `error: unknown option 'orphan'`. Fallback below |
|
||||
| `--no-checkout` | Suppress initial checkout (for sparse-checkout setup) |
|
||||
| `--guess-remote` | Look for a matching remote-tracking branch by path basename |
|
||||
| `--lock [--reason <str>]` | Lock immediately on creation (atomic; avoids race vs. add-then-lock) |
|
||||
@@ -25,7 +67,22 @@ Using `-` as `<commit-ish>` is shorthand for `@{-1}` (the branch checked out bef
|
||||
```bash
|
||||
git worktree add --orphan -b <branch> <path>
|
||||
```
|
||||
Creates an empty branch with no commits.
|
||||
Creates an empty branch with no commits. **`--orphan` needs Git 2.42 or later** — it was added
|
||||
upstream in 2.42, and on 2.39.5 this fails with `error: unknown option 'orphan'` and exit 129.
|
||||
Check `git --version` before reaching for it.
|
||||
|
||||
Fallback on older Git, verified on 2.39.5 — detach first, then orphan the linked worktree in place,
|
||||
which leaves the main worktree on its own branch throughout:
|
||||
|
||||
```bash
|
||||
git worktree add -d <path> # linked worktree, detached HEAD
|
||||
cd <path>
|
||||
git switch --orphan <branch> # unborn branch: empty index, empty working tree
|
||||
```
|
||||
|
||||
`git worktree list` then shows the new worktree at `0000000 [<branch>]` until its first commit.
|
||||
Do not substitute `git switch --orphan` in the *main* worktree: it clears that checkout, which is
|
||||
the disruption worktrees exist to avoid.
|
||||
|
||||
## Sparse-checkout worktree
|
||||
|
||||
@@ -48,9 +105,41 @@ git worktree unlock <path> # when reconnected
|
||||
## Remote-branch disambiguation
|
||||
|
||||
```bash
|
||||
git worktree add <path> <remote>/<branch>
|
||||
git worktree add --track -b <branch> <path> <remote>/<branch> # explicit: no guessing at all
|
||||
git worktree add <path> <branch> # shortcut: needs one clear remote
|
||||
```
|
||||
For ambiguous names across remotes, `checkout.defaultRemote` config disambiguates explicitly, or `--guess-remote` auto-matches by path basename (default controlled by `worktree.guessRemote` config). If a branch name matches multiple remotes during `worktree add` and neither is set, Git refuses rather than guessing.
|
||||
**The bare-name shortcut needs exactly one remote.** It fires only when `<branch>` is not found
|
||||
locally, none of `-b`/`-B`/`--detach` were given, and a tracking branch of that name exists in
|
||||
exactly one remote. When several remotes carry the name, `checkout.defaultRemote` picks one for
|
||||
disambiguation purposes; with no such setting the shortcut has no single remote to resolve against
|
||||
and does not apply. When the remote is ambiguous or unknown, use the explicit `--track -b` form.
|
||||
|
||||
`--guess-remote` covers the *other* spelling — `git worktree add <path>` with no `<commit-ish>` at
|
||||
all. It bases the new branch on the remote-tracking branch matching `$(basename <path>)` when
|
||||
exactly one remote has it, and marks that branch as upstream. Its default comes from the
|
||||
`worktree.guessRemote` config.
|
||||
|
||||
## Repair after a manual move
|
||||
|
||||
```bash
|
||||
git worktree repair # the MAIN worktree moved: run it there to reconnect every linked
|
||||
# worktree back to the main worktree
|
||||
git worktree repair # a LINKED worktree moved: run it inside that recently-moved worktree
|
||||
git worktree repair <path>... # reconnect a specific linked worktree — runnable from any worktree,
|
||||
# naming each moved tree's new path
|
||||
```
|
||||
|
||||
Which form applies depends on what moved:
|
||||
|
||||
| What moved | Remedy |
|
||||
|---|---|
|
||||
| The main worktree (or bare repo) | `git worktree repair` in the main worktree |
|
||||
| One linked worktree | `git worktree repair` inside that worktree |
|
||||
| Several linked worktrees | `git worktree repair <path>...` from any worktree, listing each new path |
|
||||
| Both main and linked worktrees | `git worktree repair <path>...` in the main worktree, naming each linked worktree's new path — this restores the connections in both directions |
|
||||
|
||||
Only the no-argument form is tied to the current directory. The `<path>...` form is not — it
|
||||
reestablishes the connection to every path you name, run from any worktree.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -61,3 +150,29 @@ For ambiguous names across remotes, `checkout.defaultRemote` config disambiguate
|
||||
| `gc.worktreePruneExpire` | How long before stale worktree metadata is pruned by `git gc` |
|
||||
| `extensions.worktreeConfig` | Enable per-worktree config scope (`config.worktree` file) — see Gotchas in SKILL.md |
|
||||
| `checkout.defaultRemote` | Disambiguates which remote to use when a branch name matches multiple remotes during `worktree add` |
|
||||
|
||||
## Workflow patterns
|
||||
|
||||
**Emergency fix without disrupting current work** — nothing is stashed, and the main worktree is
|
||||
untouched throughout:
|
||||
|
||||
```bash
|
||||
git worktree add -b emergency-fix ../temp main
|
||||
cd ../temp
|
||||
# fix, then commit
|
||||
git commit -a -m "fix: critical production bug"
|
||||
cd -
|
||||
git worktree remove ../temp
|
||||
```
|
||||
|
||||
**Review a PR branch alongside your own work** — both branches stay checked out, so there is no
|
||||
context switch:
|
||||
|
||||
```bash
|
||||
git worktree add ../review-pr-123 origin/feature-xyz # detached HEAD — read-only review
|
||||
git worktree add --track -b feature-xyz ../review-pr-123 origin/feature-xyz # if you will commit
|
||||
# open ../review-pr-123 in a second editor window or terminal
|
||||
```
|
||||
|
||||
Pick the second form the moment you intend to push anything back: the first leaves no branch to
|
||||
push and no upstream to push to.
|
||||
|
||||
@@ -19,6 +19,8 @@ Invoke with no arguments. The skill determines from context whether to create a
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `references/create-config.md` | Loaded when the repo has no `.pre-commit-config.yaml` — the create-from-scratch flow |
|
||||
| `references/modify-config.md` | Loaded when a `.pre-commit-config.yaml` already exists — add, remove, top-level keys, rev staleness |
|
||||
| `references/hooks-by-language.md` | Hook recommendations by detected language/extension |
|
||||
| `references/README.md` | Index of files in references/ |
|
||||
| `references/sources.md` | Provenance — research sources that informed this skill |
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
---
|
||||
name: pc-author
|
||||
description: >
|
||||
Use when the user wants to create, add hooks to, remove hooks from, update,
|
||||
or configure .pre-commit-config.yaml. Triggers on: "set up pre-commit",
|
||||
"add a hook", "remove this hook", "configure pre-commit", "create a pre-commit
|
||||
config", "disable trailing whitespace hook", "add shellcheck", "update my
|
||||
pre-commit config", even if the user does not name pre-commit explicitly.
|
||||
Do not use for running hooks, installing git hooks, or bumping revision pins
|
||||
— use pc-run for those.
|
||||
Use when the user wants to create or edit `.pre-commit-config.yaml` — add,
|
||||
remove, or configure hooks — even when they name only the tool ("add
|
||||
shellcheck"). Not running, installing, or updating hooks -> `pc-run`.
|
||||
allowed-tools: Bash Read Write Edit
|
||||
metadata:
|
||||
category: devtools
|
||||
@@ -20,72 +16,22 @@ metadata:
|
||||
|
||||
## Gotchas
|
||||
|
||||
- `rev` must be an immutable tag or commit SHA — never a branch name. `pre-commit autoupdate` breaks silently on branches.
|
||||
- Fixers (`trailing-whitespace`, `end-of-file-fixer`, `pretty-format-json`) modify files but do NOT auto-stage them. The commit is blocked; the user must re-stage and recommit. Warn when adding fixers.
|
||||
- `pre-commit validate-config` catches YAML structure errors but does NOT check whether hook `id`s exist in the target repo's manifest, and does NOT download or run hooks. It is fast; run it after every write.
|
||||
- When removing a hook leaves its repo block with zero hooks, delete the entire repo block — an empty `hooks: []` causes `validate-config` to fail.
|
||||
- `language: system` and `language: script` are deprecated names. Use `language: unsupported` and `language: unsupported_script` for new local hooks.
|
||||
- `rev` must be an immutable tag or commit SHA, never a branch name. A branch looks like it works and then breaks `pre-commit autoupdate` silently.
|
||||
- `pre-commit validate-config` checks YAML structure only — it never confirms a hook `id` exists upstream, so a config it accepts can still fail on first use.
|
||||
|
||||
## Route
|
||||
|
||||
Check before acting:
|
||||
| Condition | Flow | Read |
|
||||
|---|---|---|
|
||||
| No `.pre-commit-config.yaml` in the repo | Create | `references/create-config.md` |
|
||||
| `.pre-commit-config.yaml` exists | Modify | `references/modify-config.md` |
|
||||
|
||||
- `.pre-commit-config.yaml` does not exist → **Create from scratch**
|
||||
- File exists → **Modify existing**
|
||||
Read only the file matching the resolved flow — each is self-contained.
|
||||
|
||||
## Create from scratch
|
||||
The target is always `.pre-commit-config.yaml`, the config that consumes hooks. A request to publish hooks for other repos to consume means `.pre-commit-hooks.yaml`, a different file this skill does not author.
|
||||
|
||||
1. Run a shallow extension scan:
|
||||
```bash
|
||||
git ls-files | grep -oE '\.[a-z]+$' | sort | uniq -c | sort -rn
|
||||
```
|
||||
2. Read `references/hooks-by-language.md` to map detected extensions to recommended hooks. For a minimal starting point instead of a full recommendation set, `pre-commit sample-config > .pre-commit-config.yaml` prints a small starter config to build on.
|
||||
3. State the proposed config in full before writing. Wait for user confirmation.
|
||||
4. Write `.pre-commit-config.yaml`.
|
||||
5. Run `pre-commit validate-config`. If non-zero: show the error, fix it, re-validate. Never leave a broken config.
|
||||
## Gates common to both flows
|
||||
|
||||
## Modify existing
|
||||
|
||||
Read `.pre-commit-config.yaml` first. Note any stale `rev` values (see **Rev staleness** below) but do not change them.
|
||||
|
||||
### Adding a hook
|
||||
|
||||
1. Run a shallow extension scan to detect languages in the repo:
|
||||
```bash
|
||||
git ls-files | grep -oE '\.[a-z]+$' | sort | uniq -c | sort -rn
|
||||
```
|
||||
2. Read `references/hooks-by-language.md` for the correct repo URL, rev, and recommended args for any hook before writing.
|
||||
3. Check for duplicates — if the same hook ID or equivalent tool already exists in the config, say so and stop.
|
||||
4. To sanity-check a hook against the repo's actual files before committing to it in config, smoke-test it with `pre-commit try-repo <repo-url> <hook-id> --verbose` (or a local path for hooks under development). This runs the hook without writing anything.
|
||||
5. If the hook's source repo already exists in the config, add the hook under that repo block. Otherwise append a new repo block.
|
||||
6. State the proposed addition. Wait for confirmation.
|
||||
7. Write. Run `pre-commit validate-config`. If non-zero: show error, fix, re-validate.
|
||||
|
||||
### Removing a hook
|
||||
|
||||
1. Identify the hook entry and its repo block.
|
||||
2. State what will be removed: hook ID, and whether the parent repo block will also be deleted (if it would have zero hooks remaining). Wait for confirmation.
|
||||
3. Remove the hook entry. If the repo block now has zero hooks remaining, remove the entire repo block.
|
||||
4. Write. Run `pre-commit validate-config`. If non-zero: revert the edit, show the error, and stop — do not leave a broken config (removal edits are not safely auto-fixable, unlike a bad new hook block, which can usually be corrected in place).
|
||||
|
||||
### Configuring top-level keys
|
||||
|
||||
Only when the user explicitly asks. Valid keys: `fail_fast`, `default_stages`, `default_language_version`, `minimum_pre_commit_version`, `exclude`, `files`, `default_install_hook_types`.
|
||||
|
||||
State the proposed change and wait for confirmation before writing.
|
||||
|
||||
## Rev staleness
|
||||
|
||||
When reading the config, for each repo listed in `references/hooks-by-language.md`, compare its `rev` in the user's config against the rev in that file. Flag any mismatch as potentially outdated and tell the user to run `pc-run` to autoupdate. Repos not in the reference cannot be checked — skip them silently. Do not modify `rev` values yourself.
|
||||
|
||||
The reference table's pins can themselves go stale between updates — treat a mismatch as a prompt to check, not a certainty. `pre-commit autoupdate` (via `pc-run`) is the authoritative source for what the current rev actually is.
|
||||
|
||||
## Scope boundary
|
||||
|
||||
This skill manages `.pre-commit-config.yaml` only. It does not:
|
||||
- Author `.pre-commit-hooks.yaml` (publishing hooks for external consumers)
|
||||
- Run `pre-commit install`
|
||||
- Execute hooks or run the test suite
|
||||
- Bump `rev` values
|
||||
|
||||
For those operations, use `pc-run`.
|
||||
1. State the proposed config or edit in full and wait for confirmation before writing. Hook choices are opinions imposed on everyone else's commit loop, not defaults to assume.
|
||||
2. Run `pre-commit validate-config` after every write. On a non-zero exit, show the error and resolve it before reporting done — never leave a config that cannot be parsed.
|
||||
3. Never edit a `rev` value. Report staleness and hand the bump to `pc-run`, which runs `autoupdate` against the hook repos themselves.
|
||||
|
||||
@@ -10,5 +10,7 @@ source_keys:
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `create-config.md` | The create flow — read when the repo has no `.pre-commit-config.yaml` |
|
||||
| `modify-config.md` | The modify flow — read when a `.pre-commit-config.yaml` already exists |
|
||||
| `hooks-by-language.md` | Hook recommendations by language/context — repo, rev, and rationale for adding hooks |
|
||||
| `sources.md` | Provenance: research sources that informed this skill |
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
# Creating a `.pre-commit-config.yaml`
|
||||
|
||||
Reached from `SKILL.md`'s Route table when the repo has no config yet. Self-contained — the modify
|
||||
flow's file is not needed here. `SKILL.md`'s three common gates still apply.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Detect what languages are actually in the repo with a shallow extension scan, rather than
|
||||
inferring them from the project's name or README:
|
||||
|
||||
```bash
|
||||
git ls-files | grep -oE '\.[a-z]+$' | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
2. Read `references/hooks-by-language.md` and map the detected extensions to recommended hooks.
|
||||
Take the repo URL, `rev` and args from that file rather than from memory — a `rev` that does not
|
||||
exist is the most common way a fresh config fails on its first run.
|
||||
|
||||
For a deliberately minimal starting point instead of a full recommendation set,
|
||||
`pre-commit sample-config > .pre-commit-config.yaml` prints a small starter config to build on.
|
||||
|
||||
3. State the proposed config in full and wait for the user's confirmation.
|
||||
|
||||
4. Write `.pre-commit-config.yaml`, then run `pre-commit validate-config`. If it exits non-zero,
|
||||
show the error, fix it in place, and re-validate.
|
||||
@@ -11,6 +11,11 @@ source_keys:
|
||||
Use this table when creating a config from scratch or recommending hooks to add.
|
||||
Always check the existing config for duplicates before proposing.
|
||||
|
||||
Fixer hooks (`trailing-whitespace`, `end-of-file-fixer`, `pretty-format-json` and the like) rewrite
|
||||
files but do NOT re-stage them, so the commit is still blocked and the user has to stage and commit
|
||||
again. Say so when proposing one — otherwise the first blocked commit reads as the hook being
|
||||
broken.
|
||||
|
||||
## Universal (recommend for every repo)
|
||||
|
||||
| Hook ID | Repo | Rev | Rationale |
|
||||
@@ -101,6 +106,9 @@ Use for repo-specific scripts that don't belong in an external hook repo.
|
||||
stages: [pre-push]
|
||||
```
|
||||
|
||||
`language: system` and `language: script` are deprecated names for the first two below.
|
||||
New local hooks use `unsupported` and `unsupported_script`.
|
||||
|
||||
Language choices for local hooks:
|
||||
- `unsupported` — system PATH tool (pre-commit does not manage env)
|
||||
- `unsupported_script` — script at a repo-relative path
|
||||
@@ -117,4 +125,4 @@ Language choices for local hooks:
|
||||
|
||||
## Rev pin freshness
|
||||
|
||||
The revs above were last verified current at time of writing (matched against the plugin's own research corpus in `docs/research/docs/pre-commit/`; rows marked "Unverified" have no such backing and must be checked against upstream before use). Since `pc-author`'s "Rev staleness" check treats this table as ground truth, a pin that goes stale here produces false-positive staleness warnings for users who already have a newer, correct rev. Re-verify these pins periodically (e.g. against each repo's latest release tag). When in doubt, treat `pre-commit autoupdate`'s own output as the authoritative staleness signal, not a mismatch against this table.
|
||||
The revs above were last verified current at time of writing (matched against the plugin's own research corpus in `docs/research/docs/pre-commit/`; rows marked "Unverified" have no such backing and must be checked against upstream before use). Since the "Rev staleness" check in `references/modify-config.md` treats this table as ground truth, a pin that goes stale here produces false-positive staleness warnings for users who already have a newer, correct rev. Re-verify these pins periodically (e.g. against each repo's latest release tag). When in doubt, treat `pre-commit autoupdate`'s own output as the authoritative staleness signal, not a mismatch against this table.
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
# Modifying an existing `.pre-commit-config.yaml`
|
||||
|
||||
Reached from `SKILL.md`'s Route table when the repo already has a config. Self-contained — the
|
||||
create flow's file is not needed here. `SKILL.md`'s three common gates still apply.
|
||||
|
||||
Read the existing `.pre-commit-config.yaml` before editing. Note any stale `rev` values (see
|
||||
**Rev staleness** below) but do not change them.
|
||||
|
||||
## Adding a hook
|
||||
|
||||
1. Run a shallow extension scan, so the addition is judged against the languages actually present:
|
||||
|
||||
```bash
|
||||
git ls-files | grep -oE '\.[a-z]+$' | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
2. Read `references/hooks-by-language.md` for the correct repo URL, `rev` and recommended args
|
||||
before writing anything.
|
||||
|
||||
3. Check for duplicates. If the same hook ID, or an equivalent tool, is already configured, say so
|
||||
and stop rather than adding a second one.
|
||||
|
||||
4. To sanity-check a hook against the repo's real files before committing to it, smoke-test it:
|
||||
|
||||
```bash
|
||||
pre-commit try-repo <repo-url> <hook-id> --verbose
|
||||
```
|
||||
|
||||
Use a local path in place of the URL for a hook under development. This runs the hook without
|
||||
writing anything.
|
||||
|
||||
5. If the hook's source repo is already a block in the config, add the hook under that block.
|
||||
Otherwise append a new repo block.
|
||||
|
||||
6. State the proposed addition, wait for confirmation, write, and run `pre-commit validate-config`.
|
||||
On a non-zero exit, show the error, fix it, and re-validate.
|
||||
|
||||
## Removing a hook
|
||||
|
||||
1. Identify the hook entry and its parent repo block.
|
||||
|
||||
2. State what will be removed — the hook ID, and whether the parent repo block goes with it because
|
||||
it would be left with zero hooks. Wait for confirmation.
|
||||
|
||||
3. Remove the hook entry. If the repo block now has no hooks left, remove the whole block: an empty
|
||||
`hooks: []` fails `validate-config`.
|
||||
|
||||
4. Write, then run `pre-commit validate-config`. On a non-zero exit, **revert the edit**, show the
|
||||
error, and stop. A removal is not safely fixable in place the way a malformed new hook block is,
|
||||
so recovering the prior state beats patching forward.
|
||||
|
||||
## Configuring top-level keys
|
||||
|
||||
Only when the user explicitly asks. Valid keys: `fail_fast`, `default_stages`,
|
||||
`default_language_version`, `minimum_pre_commit_version`, `exclude`, `files`,
|
||||
`default_install_hook_types`.
|
||||
|
||||
State the proposed change and wait for confirmation before writing.
|
||||
|
||||
## Rev staleness
|
||||
|
||||
For each repo in the config that also appears in `references/hooks-by-language.md`, compare the
|
||||
two `rev` values. Flag a mismatch as potentially outdated and tell the user to run `pc-run` to
|
||||
autoupdate. Repos absent from the reference cannot be checked — skip them silently. Do not modify
|
||||
any `rev` yourself.
|
||||
|
||||
The reference table's own pins go stale between updates, so treat a mismatch as a prompt to check
|
||||
rather than proof of staleness. `pre-commit autoupdate`, via `pc-run`, is the authoritative answer
|
||||
to what the current `rev` actually is.
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
- **URL:** context7:/pre-commit/pre-commit.com
|
||||
- **Description:** Official pre-commit.com documentation — installation, configuration schema, CLI reference, hook authoring, advanced features, troubleshooting
|
||||
- **Contributing files:** SKILL.md, references/hooks-by-language.md
|
||||
- **Contributing files:** SKILL.md, references/create-config.md, references/modify-config.md, references/hooks-by-language.md
|
||||
- **Research doc:** plugins/git/docs/research/docs/pre-commit/{overview,configuration,cli-reference,hook-authoring}.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
- **URL:** https://pre-commit.com/
|
||||
- **Description:** Pre-commit framework homepage — full docs covering install, config, CLI, hook authoring, stages, local hooks, meta hooks, hazmat helpers, CI integration
|
||||
- **Contributing files:** SKILL.md, references/hooks-by-language.md
|
||||
- **Contributing files:** SKILL.md, references/create-config.md, references/modify-config.md, references/hooks-by-language.md
|
||||
- **Research doc:** plugins/git/docs/research/docs/pre-commit/{overview,configuration,cli-reference,hook-authoring}.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ Common invocations:
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `references/failure-patterns.md` | Hook failure causes and concrete fix suggestions |
|
||||
| `references/install.md` | The install flow — loaded when the user asks to install or set up hooks |
|
||||
| `references/autoupdate.md` | The autoupdate flow — loaded when the user asks to bump hook revs |
|
||||
| `references/clean.md` | The clean flow — loaded when the user asks to wipe the cache or rebuild environments |
|
||||
| `references/failure-patterns.md` | Hook failure causes and concrete fix suggestions — loaded when a hook fails or never fires |
|
||||
| `references/sources.md` | Provenance: research sources that informed this skill |
|
||||
| `references/README.md` | Directory index for references/ |
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
---
|
||||
name: pc-run
|
||||
description: >
|
||||
Use when the user wants to run pre-commit hooks, install git hooks, update
|
||||
hook versions, or maintain the pre-commit cache. Triggers on: "run
|
||||
pre-commit", "run all hooks", "check everything passes", "install hooks",
|
||||
"wire hooks into git", "update hook versions", "autoupdate", "bump revs",
|
||||
"clean the cache", "rebuild environments", "gc", "why is my hook failing",
|
||||
"hooks aren't running". Do not use for creating or editing
|
||||
`.pre-commit-config.yaml` — use `pc-author` for that.
|
||||
Use when the user wants to run pre-commit hooks, wire them into git, bump hook
|
||||
revs, maintain the cache, or diagnose why a hook fails or never fires. Not
|
||||
creating or editing the pre-commit config -> `pc-author`.
|
||||
|
||||
compatibility: Requires pre-commit installed and available on PATH.
|
||||
|
||||
@@ -22,102 +18,44 @@ allowed-tools: Bash Read
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Hooks not running on `git commit` almost always means `pre-commit install` was never run in this clone. Git hooks are per-clone — they are not committed to the repo.
|
||||
- When a hook modifies files (e.g. `trailing-whitespace`, `end-of-file-fixer`), the commit is blocked intentionally — the staged version is stale. The fix is `git add -u && git commit`. Do NOT call `pre-commit install -f` here; that is for overwriting existing hooks, not re-staging.
|
||||
- `pre-commit autoupdate` modifies `.pre-commit-config.yaml` in-place. Re-read the file after calling it to show the user the updated `rev` values.
|
||||
- The `SKIP` env var requires exact hook `id` values, comma-separated, no spaces: `SKIP=check-yaml,gitleaks git commit -m "msg"`. A space after the comma silently skips nothing.
|
||||
- Never use `git commit --no-verify` (or `-n`) to bypass a failing hook. Hooks are the automated QA gate; bypassing them breaks the pipeline. Diagnose and fix the failure instead — see the hook-specific guidance below and in `references/failure-patterns.md`.
|
||||
- A stages mismatch — hook stage not installed — means the hook was added to the config but `pre-commit install` was not re-run with the correct `-t` flags. Hooks in stages not listed under `default_install_hook_types` will never fire.
|
||||
- The `SKIP` env var takes exact hook `id` values, comma-separated with no spaces: `SKIP=check-yaml,gitleaks git commit -m "msg"`. A space after a comma silently skips nothing instead of erroring.
|
||||
- Never bypass a failing hook with `git commit --no-verify` (or `-n`). Hooks are the automated QA gate, so a bypassed commit pushes the failure downstream where it costs more — diagnose it instead.
|
||||
- `- files were modified by this hook` is not a bug. A fixer hook rewrote a staged file, so the staged snapshot is stale and the commit is blocked on purpose. Re-stage and re-run the same commit: `git add -u && git commit`. Do NOT reach for `pre-commit install -f` here — it overwrites `.git/hooks/` and has nothing to do with re-staging.
|
||||
|
||||
## Gate — `pre-commit clean`
|
||||
|
||||
Confirm with the user before running `pre-commit clean`, on every path that reaches it — including when it turns up as the fix for a stale or broken environment. It wipes the whole cache at `~/.cache/pre-commit`, which is machine-wide and shared by every repo on the box, forcing every hook environment to be re-downloaded.
|
||||
|
||||
> "This will wipe the entire pre-commit cache. All hook environments will be re-downloaded on next run. Proceed?"
|
||||
|
||||
`pre-commit gc` drops only unused environments and needs no confirmation — prefer it when the goal is just to reclaim disk.
|
||||
|
||||
## Route
|
||||
|
||||
Determine intent from the user's request, then execute the matching operation:
|
||||
Determine intent from the user's request, then execute the matching operation. Where the matching row names a `references/` file, read that one file and no other — each flow file is self-contained.
|
||||
|
||||
| User intent | Operation |
|
||||
|---|---|
|
||||
| "run", "check", "verify", "test hooks" | `pre-commit run --all-files` (default) |
|
||||
| "staged", "simulate commit" | `pre-commit run` (staged files only) |
|
||||
| "CI", "changed files only", "diff range" | `pre-commit run --from-ref <base> --to-ref <head>` — prefer this over `--all-files` on large repos |
|
||||
| "install", "set up hooks", "wire into git" | `pre-commit install` — see Install |
|
||||
| "pre-create environments", "install-hooks", "warm cache" | `pre-commit install-hooks` — see Install |
|
||||
| "remove hooks", "uninstall", "tear down pre-commit" | `pre-commit uninstall` |
|
||||
| "autoupdate", "update versions", "bump revs" | `pre-commit autoupdate` |
|
||||
| "gc", "garbage collect" | `pre-commit gc` |
|
||||
| "clean", "wipe cache", "rebuild from scratch" | `pre-commit clean` — see Clean |
|
||||
| "install", "set up hooks", "wire into git" | `pre-commit install` — read `references/install.md` |
|
||||
| "pre-create environments", "warm cache" | `pre-commit install-hooks` — builds every hook environment without running a hook |
|
||||
| "remove hooks", "uninstall", "tear down" | `pre-commit uninstall` — removes pre-commit from `.git/hooks/` |
|
||||
| "autoupdate", "update versions", "bump revs" | `pre-commit autoupdate` — read `references/autoupdate.md` |
|
||||
| "gc", "garbage collect" | `pre-commit gc` — drops unused cached environments only, safe at any time |
|
||||
| "clean", "wipe cache", "rebuild from scratch" | `pre-commit clean` — read `references/clean.md` |
|
||||
| "hooks aren't running", "hook never fires", "why did a hook fail", a hook failure whose cause is unclear | Diagnose — read `references/failure-patterns.md` |
|
||||
|
||||
If the intent is ambiguous, default to `pre-commit run --all-files`.
|
||||
If the intent is ambiguous, default to `pre-commit run --all-files` — do not stop to ask, and do
|
||||
not fall through to a narrower row on a guess.
|
||||
|
||||
## Run
|
||||
|
||||
Default: `pre-commit run --all-files`. Never silently run staged-only.
|
||||
Default to `pre-commit run --all-files`; never silently narrow to staged files. Run `pre-commit run` (staged only) or `pre-commit run <hook-id>` (one named hook) when the user asks for it.
|
||||
|
||||
```bash
|
||||
pre-commit run --all-files
|
||||
```
|
||||
When hooks fail:
|
||||
|
||||
**When hooks fail**, read the output and:
|
||||
1. Identify which hook failed and the specific cause. Be concrete: "gitleaks blocked `config.json` (high-entropy string on line 12)", not just "gitleaks failed".
|
||||
2. Suggest a concrete next step. Common patterns are in `references/failure-patterns.md`.
|
||||
3. Do NOT auto-fix code files. Do NOT modify `.pre-commit-config.yaml`. Those are the user's or `pc-author`'s responsibility.
|
||||
|
||||
If the user asks to run only staged files: `pre-commit run` (no `--all-files`).
|
||||
If the user names a specific hook: `pre-commit run <hook-id>`.
|
||||
|
||||
## Install
|
||||
|
||||
Only run when the user explicitly asks to install or set up hooks.
|
||||
|
||||
Before running, check for existing hook files:
|
||||
|
||||
```bash
|
||||
ls .git/hooks/
|
||||
```
|
||||
|
||||
If any hook files exist (e.g. a hand-written `pre-commit`), `pre-commit install` does NOT refuse or error — it defaults to migration mode, which runs the existing hook and pre-commit's hooks both. Only `-f` replaces the existing hook file outright, and that replacement is not reversible via `pre-commit uninstall` — uninstall only removes pre-commit from `.git/hooks/`, it does not restore whatever hand-written hook `-f` overwrote. If files are present, tell the user: "Existing hook files found at `.git/hooks/<names>`. Plain `pre-commit install` will run both; `pre-commit install -f` will overwrite them permanently instead. Proceed with plain install, or overwrite?" Wait for confirmation before using `-f`.
|
||||
|
||||
```bash
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
Re-run with `-t` flags when `default_install_hook_types` was changed or when hooks in non-default stages aren't firing:
|
||||
|
||||
```bash
|
||||
pre-commit install -t pre-commit -t pre-push -t commit-msg
|
||||
```
|
||||
|
||||
To pre-create all hook environments without running hooks (useful for CI warm-up or first-time setup):
|
||||
|
||||
```bash
|
||||
pre-commit install-hooks
|
||||
```
|
||||
|
||||
To remove pre-commit from `.git/hooks/` entirely:
|
||||
|
||||
```bash
|
||||
pre-commit uninstall
|
||||
```
|
||||
|
||||
## Autoupdate
|
||||
|
||||
```bash
|
||||
pre-commit autoupdate
|
||||
```
|
||||
|
||||
After it completes, read `.pre-commit-config.yaml` and report which `rev` values changed. If the user wants to pin to exact SHAs (for reproducibility): `pre-commit autoupdate --freeze`.
|
||||
|
||||
## Clean and GC
|
||||
|
||||
**`gc`** — removes only unused cached environments. Safe to run at any time:
|
||||
```bash
|
||||
pre-commit gc
|
||||
```
|
||||
|
||||
**`clean`** — wipes the entire cache at `~/.cache/pre-commit`. All hook environments will be re-downloaded on next run. Require explicit confirmation before running:
|
||||
|
||||
> "This will wipe the entire pre-commit cache. All hook environments will be re-downloaded on next run. Proceed?"
|
||||
|
||||
Wait for the user to say yes before executing:
|
||||
|
||||
```bash
|
||||
pre-commit clean
|
||||
```
|
||||
1. Name the hook and the specific cause. Be concrete — "gitleaks blocked `config.json` (high-entropy string on line 12)", not "gitleaks failed".
|
||||
2. Suggest one concrete next step. Common causes and their concrete fixes are in `references/failure-patterns.md` — read it whenever the output does not already name the fix.
|
||||
3. Do not auto-fix code files, and do not edit `.pre-commit-config.yaml` — those belong to the user or to `pc-author`.
|
||||
|
||||
@@ -10,5 +10,8 @@ source_keys:
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `failure-patterns.md` | Hook failure causes and concrete fix suggestions — loaded when hooks fail |
|
||||
| `install.md` | The install flow — read when the user asks to install or set up hooks |
|
||||
| `autoupdate.md` | The autoupdate flow — read when the user asks to bump hook revs |
|
||||
| `clean.md` | The clean flow — read when the user asks to wipe the cache or rebuild environments |
|
||||
| `failure-patterns.md` | Hook failure causes and concrete fix suggestions — read when a hook fails or never fires |
|
||||
| `sources.md` | Provenance: research sources that informed this skill |
|
||||
|
||||
17
plugins/git/.apm/skills/pc-run/references/autoupdate.md
Normal file
17
plugins/git/.apm/skills/pc-run/references/autoupdate.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
# Bumping hook revs with `autoupdate`
|
||||
|
||||
Reached from `SKILL.md`'s Route table when the user asks to update hook versions or bump revs. Self-contained.
|
||||
|
||||
```bash
|
||||
pre-commit autoupdate
|
||||
```
|
||||
|
||||
This rewrites `.pre-commit-config.yaml` in place, so re-read the file afterwards and report which `rev` values changed. It is the one operation in this skill that writes that file, and the exception is deliberate: the rewrite is pre-commit's own, resolved against the hook repos, not a hand edit — which is why `pc-author` hands rev bumps here rather than making them itself.
|
||||
|
||||
Add `--freeze` when the user wants exact SHAs pinned for reproducibility.
|
||||
17
plugins/git/.apm/skills/pc-run/references/clean.md
Normal file
17
plugins/git/.apm/skills/pc-run/references/clean.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
# Wiping the pre-commit cache
|
||||
|
||||
Reached from `SKILL.md`'s Route table when the user asks to clean the cache or rebuild environments from scratch. The confirmation gate on `pre-commit clean` stays in `SKILL.md`, because it must fire on every path that reaches this command, not only this one.
|
||||
|
||||
## Gate
|
||||
|
||||
`pre-commit clean` runs only after the confirmation gate in `SKILL.md` clears — that gate, its exact wording, and the `pre-commit gc` alternative live there and are not restated here.
|
||||
|
||||
```bash
|
||||
pre-commit clean
|
||||
```
|
||||
@@ -18,6 +18,8 @@ git add -u
|
||||
git commit -m "same message"
|
||||
```
|
||||
|
||||
Do NOT reach for `pre-commit install -f` here. That flag overwrites existing hook files in `.git/hooks/`; it has nothing to do with re-staging.
|
||||
|
||||
## Secret detected (gitleaks)
|
||||
|
||||
> Not sourced from the pre-commit research corpus (`context7-pre-commit-com`/`pre-commit-com` cover pre-commit itself, not gitleaks) — general tool knowledge, verify against gitleaks' own docs if precision matters.
|
||||
@@ -87,13 +89,14 @@ Fix: The user (or `pc-author`) must add `args: [--autofix]` to the hook override
|
||||
|
||||
Cause: A hook's cached environment is corrupted or out of date.
|
||||
|
||||
Fix:
|
||||
Fix: `pre-commit clean` is gated. It wipes the machine-wide cache at `~/.cache/pre-commit`, shared by every repo on the box, so get explicit confirmation before running it — "This will wipe the entire pre-commit cache. All hook environments will be re-downloaded on next run. Proceed?"
|
||||
|
||||
```bash
|
||||
pre-commit clean # wipe all environments
|
||||
pre-commit clean # gated — confirm with the user first
|
||||
pre-commit install-hooks # rebuild everything
|
||||
```
|
||||
|
||||
Or less destructively:
|
||||
Or less destructively, needing no confirmation:
|
||||
```bash
|
||||
pre-commit gc # remove only unused environments
|
||||
```
|
||||
|
||||
31
plugins/git/.apm/skills/pc-run/references/install.md
Normal file
31
plugins/git/.apm/skills/pc-run/references/install.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
# Installing hooks into `.git/hooks/`
|
||||
|
||||
Reached from `SKILL.md`'s Route table when the user asks to install or set up hooks. Self-contained.
|
||||
|
||||
Only run this flow when the user explicitly asks for it. Installing rewrites their clone's `.git/hooks/`; it is never a side effect of another request.
|
||||
|
||||
## Gate — existing hook files
|
||||
|
||||
Check `ls .git/hooks/` first. With hook files already there, `pre-commit install` does not refuse — it silently enters migration mode and runs both. Only `-f` replaces them, and `pre-commit uninstall` cannot restore whatever `-f` overwrote.
|
||||
|
||||
So when hook files are present, put the choice to the user in these terms, including the irreversibility, and wait for an answer before passing `-f`:
|
||||
|
||||
> "Existing hook files found in `.git/hooks/`. Plain `pre-commit install` runs both; `-f` overwrites them permanently and `pre-commit uninstall` cannot restore them. Plain install, or overwrite?"
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
Re-run with `-t` flags when `default_install_hook_types` changed, or when hooks in a non-default stage never fire — a hook whose stage was never installed cannot run:
|
||||
|
||||
```bash
|
||||
pre-commit install -t pre-commit -t pre-push -t commit-msg
|
||||
```
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
- **URL:** context7:/pre-commit/pre-commit.com
|
||||
- **Description:** Official pre-commit.com documentation — installation, configuration schema, CLI reference, hook authoring, advanced features, troubleshooting
|
||||
- **Contributing files:** SKILL.md, references/failure-patterns.md
|
||||
- **Contributing files:** SKILL.md, references/install.md, references/autoupdate.md, references/clean.md, references/failure-patterns.md
|
||||
- **Research doc:** plugins/git/docs/research/docs/pre-commit/{overview,cli-reference,troubleshooting}.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
- **URL:** https://pre-commit.com/
|
||||
- **Description:** Pre-commit framework homepage — full docs covering install, config, CLI, hook authoring, stages, local hooks, meta hooks, hazmat helpers, CI integration
|
||||
- **Contributing files:** SKILL.md, references/failure-patterns.md
|
||||
- **Contributing files:** SKILL.md, references/install.md, references/autoupdate.md, references/clean.md, references/failure-patterns.md
|
||||
- **Research doc:** plugins/git/docs/research/docs/pre-commit/{overview,cli-reference,troubleshooting}.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user