magit-submodule-mode

magit-submodule-mode: the configured submodules with git's own status marker — a adds, u updates, s syncs, d removes (asks first).

On this page

Your submodules, each with the marker git submodule status prints, the commit the superproject records, and the path. :magit-submodule, or o in the repo dispatch transient.

Submodules (3)
  - a1b2c3d  vendor/uninitialised
    e4f5a6b  vendor/tracking      (v1.2.3)
  + c7d8e9f  vendor/moved         (v1.2.3-4-gabc123)

The marker column is git's, unchanged, so a row reads the same here as in a terminal:

MarkerMeaning
(blank)At the commit the superproject records
-Not initialised — nothing checked out yet. u populates it
+Checked out at a different commit than recorded
UHas merge conflicts

- and U are coloured as removals and + as an addition, so the ones needing attention are findable by scanning the column. The headerline carries the count plus how many are uninitialised, modified or conflicted — a bare total would not tell you that any of them need anything.

Chords

ChordAction
aAdd a submodule — asks for the URL, then the path
uUpdate the submodule at cursor (--init --recursive)
sSync the submodule at cursor's URL from .gitmodules
dRemove the submodule at cursor — asks first
grRefresh (re-read git submodule status)

q and navigation come from magit-core-mode.

The add prompt seeds the path git itself would choose — the URL's last segment without .git — so accepting the default is one keystroke.

Magit's p populate and r register are not separate keys here: u runs submodule update --init --recursive, which subsumes both. Three keys for one intent is three chances to pick the wrong one.

Why d asks

Removing a submodule runs git submodule deinit -f and then git rm -f — it deletes the submodule's whole working tree, including anything uncommitted inside it, and git keeps no copy. That is irreversible in the way that matters, so it routes through a confirmation naming the submodule and saying what is lost, and the chord itself performs no git call at all.

This is the opposite call from magit-remote-mode's d, which does not ask — removing a remote drops config you can retype. Confirmations are spent only where work is destroyed.

Magit's d (unpopulate) has no key here. It is u's inverse, rarely wanted, and putting it next to a destructive d would make the dangerous one easy to hit by accident.

No <CR>, and why

The obvious binding would be "open this submodule's own magit-status". It is not here because magit's working directory is currently process-wide — every magit buffer in the editor is bound to the repository lattice was launched in, and there is no way to point a status buffer at a subdirectory yet. A chord that opened the superproject's status while claiming to open the submodule's would be worse than no chord. The same limitation is what blocks worktree support.

See also