# 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.

Source: https://zaryn.xyz/docs/en/subrepos

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).
