refactor(git-submodules): retrofit to the ADR-0020 context contract
Description 480 -> 248 chars, body 1011 -> 347 words. The single submodules.md splits into setup-and-update, urls-and-config, and removal. Restores three regressions the first pass introduced: 'repointing' as the trigger for the URL branch, which had none while the boundary clause steered those queries to git-remotes; clone and absorbgitdirs in the output enum, which dispatch still routed to; and status --cached, the flag that makes the pre-commit pointer gate verifiable.
This commit is contained in:
@@ -6,10 +6,26 @@ metadata:
|
||||
|
||||
# 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, and updating or re-pinning. Carries the `add` and `update` flag tables and the
|
||||
keep-pinned and move-the-pin-forward workflows.
|
||||
|
||||
## 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/skills/git-submodules/references/removal.md
Normal file
32
plugins/git/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
|
||||
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
|
||||
git submodule deinit -f <path> # unregister from .git/config
|
||||
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
|
||||
git commit -m "chore: remove <name> submodule"
|
||||
```
|
||||
|
||||
The third step is the one that gets skipped. `.git/modules/<name>/` survives `git rm`, and while it
|
||||
is present Git refuses to add a submodule at the same path again.
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
topic: submodules
|
||||
source_keys:
|
||||
- git-scm-submodule-docs
|
||||
---
|
||||
|
||||
# Adding, initializing, updating and pinning submodules
|
||||
|
||||
## Clone a superproject that already has submodules
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules <url> # Git 2.13+, one step
|
||||
# or, against an existing clone
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
## Add a dependency as a submodule
|
||||
|
||||
```bash
|
||||
git submodule add <url> <path>
|
||||
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
|
||||
|
||||
`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
|
||||
|
||||
`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
|
||||
git submodule update --recursive # after every git pull
|
||||
git config submodule.recurse true # or do it automatically on pull/push/checkout
|
||||
```
|
||||
|
||||
## Move the pin forward to the tracked branch tip
|
||||
|
||||
```bash
|
||||
git submodule update --remote --merge --recursive
|
||||
git commit -am "chore: update submodules to latest"
|
||||
```
|
||||
|
||||
`--remote` requires `submodule.<name>.branch`; 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`.
|
||||
@@ -14,7 +14,9 @@ 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)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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 `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
|
||||
git submodule sync --recursive # push .gitmodules URLs into .git/config
|
||||
git submodule set-url <path> <url> # change the canonical URL
|
||||
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
|
||||
git submodule init
|
||||
# edit .git/config: submodule.<name>.url = <mirror-url>
|
||||
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
|
||||
`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
|
||||
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
|
||||
`git submodule add`.
|
||||
Reference in New Issue
Block a user