Command Palette

Search for a command to run…

ZarynZaryn
Code hosting and version control·4 min read·

Subrepositories

Another repository inside yours, pinned to a commit: a shared UI kit, a vendored library, a dataset. zev fetches and updates it on clone, checkout and pull by itself, keeps the pin in a file you review, and never throws away work done inside it. What Git's submodules should have been.

Sometimes a project needs another project inside it at an exact version, such as a UI kit shared by three apps, a library you patch, or a dataset. Git's answer is submodules, and most people who've used them avoid them: - forget `--recursive` and the folder is empty; - the code inside sits on a detached HEAD, so work done there is easy to lose; - the version you depend on is an invisible entry that reviews don't show. zev's **subrepositories** keep the idea and drop the traps. ```diagram type: cards title: How subrepositories differ from submodules --- 📄 | The pin is a file you read | .zevsubs lists each one's address, branch and commit; a change to it shows in every diff and review 🔁 | Nothing to remember | zev clone, checkout and pull fetch subrepositories and put them at their pins by themselves 🛟 | Your work is safe | a subrepository with unsaved changes or commits of its own is never moved; zev says so instead 🌿 | Always on a branch | no detached HEAD: commit inside it like in any repository ``` ## Add one ```bash zev sub add zaryn/ui-kit lib/ui ``` zev clones the repository into `lib/ui`, writes its address, branch and current commit to `.zevsubs`, and stages that file. Commit it: ```bash zev commit -m "Use the shared UI kit" ``` ```text # Subrepositories: see them with zev sub, fetch them with zev sub update. [lib/ui] url = zaryn/ui-kit branch = main commit = 8fef7c70fd8de3da1fed658b65bab8d1c3296c6f07f4e550274e43a3fde39a9c ``` The parent repository never tracks the subrepository's files. `zev status` and `zev add .` leave them alone, because they belong to the subrepository's own history. ## Everyone gets it, at the same version ```bash zev clone you/app sub lib/ui: fetched, at its pin ``` A clone fetches each subrepository and puts it at its pinned commit. So do `zev checkout`, `zev switch` and `zev pull` whenever the pin changes: switch to a branch that pins an older version and the subrepository follows, then switch back and it returns. There's no `--recursive` to remember and no separate update step. ## Move to a newer version ```bash zev sub pull # pull each subrepository's branch, then pin what you got zev commit -m "UI kit 2.4" ``` `zev sub pin` records whatever a subrepository is at now, for example after you committed a fix inside it. Push that fix from inside the subrepository too, so others can fetch the commit you pinned. ## See where they are ```bash $ zev sub ✓ lib/ui zaryn/ui-kit @ 8fef7c70 at its pin ! vendor/parser zaryn/parser @ 1c2d3e4f has commits newer than the pin: zev sub pin records them ``` Each subrepository is at its pin, behind it, ahead of it (commits of your own), elsewhere, missing, or has unsaved changes, and zev says what to run for each. `--brief` gives one line each and `--json` gives the path, address, branch, pin, current commit, state and number of changes. ## Your work inside one is never lost A subrepository is an ordinary repository on an ordinary branch. `cd lib/ui`, edit, commit and push as usual. zev moves it only when that loses nothing: - **unsaved changes** inside it: zev never moves it and says so; - **commits of your own** beyond the pin: switching the parent leaves it alone and says why. `zev sub pin` records them; `zev sub update --force` puts it back at the pin, and `zev undo` inside the subrepository takes that back. ## Commands ```text zev sub where each one is and what it needs zev sub add <address> <folder> [--branch B] bring a repository in, pinned zev sub update [<folder>…] [--force] fetch, and put each at its pin zev sub pin [<folder>…] record each one's current commit zev sub pull [<folder>…] pull each one's branch, then pin zev sub remove <folder> stop tracking it here (its files stay) ``` ## Coming from Git submodules `zev import-git` lists the submodules it found. Once their repositories are on zev (`zev import-git` them too), run `zev sub add <address> <folder>` for each. | Git submodules | zev subrepositories | |---|---| | `git clone --recursive`, or `git submodule update --init` after | `zev clone`; nothing after | | A tree entry and `.gitmodules`; the version isn't shown in reviews | `.zevsubs`, one readable file with the commit in it | | Detached HEAD inside | On a branch | | `git submodule update` can discard commits made inside | Never moved over unsaved work or your own commits without `--force`, and undoable | | `git status` shows little about the state inside | `zev sub` says where each one is and what to run | See also: [Branches and merging](/docs/en/branches), [Moving from Git](/docs/en/from-git).