Files
holocron/plugins/git/skills/git-submodules/references/setup-and-update.md
Defame1297 20e5627fd7 fix(git): correct three command claims the plugin got wrong, and give rebase an owner
Three statements were false against the git in use (2.39.5), each verified by
running it.

`git rebase --autosquash HEAD~N` without `-i` is a silent no-op: git prints
"Successfully rebased", the `fixup!` commit survives with the same SHA, and
rewrite-history.md presented that as the preferred flow with `-i` as an optional
review step. The agent reports the squash as done and the `fixup!` subject then
trips this repo's own commit-msg gate. `-i` is now the command, not the alternative.

"Fetch never modifies a local branch, so it is always safe to run" — new text on
this branch — is false: `git fetch origin main:probe` fast-forwarded the local
branch, and a `+` prefix force-updates it, losing commits. The claim is scoped to
the no-refspec form.

`git worktree add --orphan` does not exist before Git 2.42; on 2.39.5 it is
`error: unknown option 'orphan'`, exit 129. The same file gives a version floor for
`--recurse-submodules` three sections earlier. Floor added, with a fallback that
was tested before being documented.

git-workflow claimed "every request resolves to exactly one of these six" while
rebase, reset and stash were owned by no skill — `git reset` appeared nowhere in
the plugin, old tree or new — so "undo my last commit" routed nowhere. The claim is
gone, the router gains rows for all three, and the procedures now exist: plain
rebase with `--onto` and conflict handling, a reset mode table gated on `--hard`,
and stash save/pop/list/drop. `--hard` gets an always-loaded Gotcha, matching the
register the force-push refusal already sets.

Cherry-pick had three claimants pointing at git-history while git-commits owned the
flow, and the two copies were not equivalent — git-history's lacked the destination
check, the rtk prefix and `--abort`. Resolved to git-commits per #112; the weaker
duplicate is replaced by a hand-off.

git-commits' metadata.version was deleted rather than bumped in 14af50b, leaving two
house-contract documents citing a worked v0.1.2 to v0.1.3 transition against a file
declaring no version. Restored to 0.1.3.

Also: the divergent-pull explanation stated a `--ff-only` default that does not
exist (it is a hard error); `--remote` "requires" a configured branch where it uses
one; `git push origin --delete` moved to git-remotes, which owns the remote-side
gates; seven retired `git:<name>` source slugs; git-orchestrate quoted a
git-workflow sentence that no longer exists; git-submodules regains the indirect
trigger that made "add a dependency repo" routable; and commit atomicity is a common
gate rather than reachable only from the create flow.

Refs: #112, #113
2026-09-01 12:38:38 +00:00

3.6 KiB

topic, source_keys
topic source_keys
submodules
git-scm-submodule-docs

Adding, initializing, updating and pinning submodules

Clone a superproject that already has submodules

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

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

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

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

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