Submod
Manage git submodules without the fuss
Install / Use
/learn @bashandbone/SubmodREADME
submod
Git submodules solve a real problem. Managing submodules is a pain. You use them infrequently enough that you always forget which command does what — and when something breaks, the recovery steps are a small nightmare. New contributors hit this especially hard: onboarding onto a project that uses submodules is its own obstacle course.
submod wraps the whole lifecycle in one consistent CLI. Sixteen commands, including nuke-it-from-orbit for when you're done being reasonable. Built on gitoxide and git2, with automatic fallback so operations don't fail silently.[^1] It's actively used across @knitli and @plainlicense, where submodules handle shared functionality between repos.
:rocket: Features
- TOML config — define submodules, sparse-checkout paths, and defaults in one file
- Sparse checkout — clone only the parts of a submodule you actually need
- Global defaults with per-submodule overrides — set it once, customize where it matters
- Fallback chain — tries gitoxide first, falls back to git2, then CLI
- Clear status and errors — you'll know what broke and why
[^1]: The fallback architecture is more a reflection of the status of gitoxide and git2 submodule support than a stability concern. Their features do not consistently provide the full lifecycle of submodule operations. Together they cover >90%, but sometimes immaturely. The fallbacks architecture handles that gracefully and lets native operations grow with those libraries.
📋 Table of Contents
🔧 Installation
Using Cargo
cargo install submod
Using Mise
Mise is a project management tool and package manager that can manage your development environment.
# Global installation
mise use -g cargo:submod@latest
# Project-specific installation
mise use cargo:submod@latest
From Source
git clone https://github.com/yourusername/submod.git
cd submod
cargo install --path .
🚀 Quick Start
-
Initialize a config file in your git repository:
# Create a basic submod.toml configuration cat > submod.toml << EOF [defaults] ignore = "dirty" [my-submodule] path = "vendor/my-lib" url = "https://github.com/example/my-lib.git" sparse_paths = ["src/", "include/", "*.md"] EOF -
Initialize your submodules:
submod init -
Check status:
submod check
⚙️ Configuration
Create a submod.toml file in your repository root:
# Global defaults applied to all submodules
[defaults]
ignore = "dirty" # ignore dirty state in status
update = "checkout" # update method
branch = "main" # default branch to track
# Individual submodule configuration
[vendor-utils]
path = "vendor/utils"
url = "https://github.com/example/utils.git"
sparse_paths = ["src/", "include/", "*.md"]
ignore = "all" # override default ignore setting
active = true # whether submodule is active
[my-submodule]
path = "libs/my-submodule"
url = "https://github.com/example/my-submodule.git"
sparse_paths = ["src/core/", "docs/"]
branch = "develop" # track specific branch
Configuration Options
Global Defaults
ignore: How to handle dirty submodules (all,dirty,untracked,none)update: Update strategy (checkout,rebase,merge,none,!command)branch: Default branch to track (.for current superproject branch)fetchRecurse: Fetch recursion (always,on-demand,never)
Per-Submodule Settings
path: Local path where submodule should be placedurl: Git repository URLsparse_paths: Array of paths to include in sparse checkoutactive: Whether the submodule is active (default:true)- All global defaults can be overridden per submodule
📖 Commands
submod add
Add a new submodule to your configuration and repository:
# Basic add
submod add https://github.com/example/my-lib.git --name my-lib --path libs/my-lib
# With sparse checkout paths and extra options
submod add https://github.com/example/my-lib.git \
--name my-lib \
--path libs/my-lib \
--sparse-paths "src/,include/" \
--branch main \
--ignore all \
--fetch on-demand
Options:
| Flag | Short | Description |
|------|-------|-------------|
| <URL> | | (required) URL or local path of the submodule repository |
| --name | -n | Nickname for the submodule used in your config and commands |
| --path | -p | Local directory path where the submodule should be placed |
| --branch | -b | Branch to track |
| --ignore | -i | Dirty-state ignore level (all, dirty, untracked, none) |
| --sparse-paths | -x | Comma-separated sparse checkout paths or globs |
| --fetch | -f | Recursive fetch behavior (always, on-demand, never) |
| --update | -u | Update strategy (checkout, rebase, merge, none) |
| --shallow | -s | Shallow clone (last commit only) |
| --no-init | | Add to config only; do not clone/initialize |
submod check
Check the status of all configured submodules:
submod check
alias: submod c
submod init
Initialize all missing submodules:
submod init
alias: submid i
submod update
Update all submodules to their latest commits:
submod update
alias: submod u
submod reset
Hard reset submodules (stash changes, reset --hard, clean):
# Reset all submodules
submod reset --all
# Reset specific submodules (comma-separated)
submod reset my-lib,vendor-utils
alias: submod r
submod sync
Run a complete sync (check + init + update):
submod sync
alias: submod s
submod change
Change the configuration of an existing submodule:
submod change my-lib --branch main --sparse-paths "src/,include/" --fetch always
submod change-global
Change global defaults for all submodules:
submod change-global --ignore dirty --update checkout
aliases: submod cg, submod chgl, submod global
submod list
List all configured submodules:
submod list
submod list --recursive
aliases: submod ls, submod l
submod delete
Delete a submodule from configuration and filesystem:
submod delete my-lib
alias: submod del
submod disable
Disable a submodule without deleting files (sets active = false):
submod disable my-lib
alias: submod del
submod nuke-it-from-orbit
Delete all or specific submodules from config and filesystem, with optional reinit:
# Nuke all submodules (re-initializes by default)
submod nuke-it-from-orbit --all
# Nuke specific submodules permanently
submod nuke-it-from-orbit --kill my-lib,old-dep
aliases: submod nuke-em, submod nuke-it, submod nuke-them
Use nuke-it-from-orbit was created because sometimes submodule just... don't cooperate. You're done being nice and just want to get back to work. Nuke it.
submod generate-config
Generate a new configuration file:
# From current git submodule setup
submod generate-config --from-setup .
# As a template with defaults
submod generate-config --template --output my-config.toml
aliases: submod gc, submod genconf
submod completeme
Generate shell completion scripts:
bash
mkdir -p ~/.bash_completion.d
submod completeme bash > ~/.bash_completion.d/submod
aliases: submod comp, submod complete, submod comp-me, submod complete-me
zsh
# zsh has an fpath array with possible function directories. You can
# put your competions in any of these; we use the first one here:
ZSH_DEFAULT="${XDG_DATA_HOME:-~/.local/share}/zsh/site-functions"
ZFUNCDIR=""${fpath[1]:-$ZSH_DEFAULT}"
mkdir -p "$ZFUNCDIR"
submod completeme zsh > "${ZFUNCDIR}/_submod"
fish
mkdir -p "${XDG_CONFIG_HOME:-~/.config}/fish/completions"
submod completeme fish > "{XDG_CONFIG_HOME:-~/.config}/fish/completions/submod.fish
powershell
mkdir -p $Home\Documents\PowerShell\completions
submod completeme powershell > $Home\Documents\PowerShell\completions\submod.completion.ps1
elvish
mkdir -p ~/.config/elvish/completions
submod completeme elvish > ~/.config/elvish/completions/submod.elv
nushell
submod completeme nu > "$NUSHELL_CONFIG_DIR/scripts/completions/submod.nu"
echo 'use completions/submod.nu' >> "$NU_CONFIG_PATH"
</details>
💻 Usage Examples
Basic Workflow
# Start with checking current state
submod check
# Initialize any missing submodules
submod init
# Update everything to latest
submod update
# Or
Related Skills
apple-reminders
341.0kManage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.
gh-issues
341.0kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
healthcheck
341.0kHost security hardening and risk-tolerance configuration for OpenClaw deployments
node-connect
341.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
