# meta: a repository that points at other repositories

> Mateodelnorte's meta answers the monorepo argument by refusing to pick a side. The code stays in many repos, and a single meta repo holds the references that let you clone and command all of them at once.

**mateodelnorte/meta** — tool for turning many repos into a meta repo. why choose many repos or a monolithic repo, when you can have both with a meta repo?

- Repository: https://github.com/mateodelnorte/meta
- Stars: 2,229 · Forks: 105
- Language: JavaScript
- License: MIT
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/mateodelnorte-meta

## Both, which is the whole argument

The repository description asks the question directly: why choose many repos or a monolithic repo when you can have both with a meta repo. The README restates it as meta being a tool for managing multi-project systems and libraries, and the Why section lists the reasons as bullets.

The design is small enough to describe in one sentence. Your code stays where it was, in one repository per project. A separate meta repository holds a `.meta` file listing references to the child repositories. The meta repository has no source code of its own, so there is no build output in it, no release from it, and no coupling between projects.

What you get in exchange is one command surface. Cloning a meta repo clones every child repo. Running a command can target all of them. Installing dependencies can run across all of them. And because the children remain independent repositories, each one keeps its own deployment story and can be reused outside the group, which the README lists as a reason directly.

The list of reasons also includes the one the tool argues against itself: no strange side effects of git submodules or subtree. That is the real comparison. Git can already compose many repositories into one working tree, and meta's pitch is that you do not have to accept how that composition behaves.

## The .meta file is the entire state

Initialization is four steps, and the second one is the part people miss. You create a directory, run `git init` inside it, and then run `meta init`, which creates the `.meta` file that will hold references to any child repositories you add.

Projects get added with two commands. `meta project create [folder] [repo url]` makes a new one, and `meta project import [folder] [repo url]` brings in an existing one. For each project added, meta updates two files: your `.gitignore` and the `.meta` file itself. The `.gitignore` update matters, because a child repo cloned into a subdirectory of the meta repo would otherwise show up as untracked content in the parent's status.

That is the whole model. There is no daemon, no index to rebuild and no database. The state of the multi-repo project is a text file listing directories and URLs, and it is committed alongside the parent repository, which is what lets a team share one project setup regardless of where anyone cloned it.

```
meta git clone git@github.com:mateodelnorte/meta.git
```

The clone command replaces the `git clone` you would normally run. It fetches the meta repo and all of the children in one step. The update counterpart matters just as much for day-to-day work: when a teammate adds a project and you `git pull origin master` to get the new `.meta`, you then run `meta git update`, which clones any repository listed in that file that you do not have locally yet.

This is the operation git submodules do badly, because `git pull` alone does not fetch new submodules. meta treats it as the default behaviour of the parent.

## Plugins are the extension point, and four commands ship by default

The README is explicit that all meta functionality is contributed by plugins. Plugins are node modules whose names begin with `meta-`, installed either globally or in the meta repo's `node_modules`. The recommendation is to install them as devDependencies in the meta repo's package.json, which pins them to the project rather than to one machine.

The plugin architecture is described as built on commander.js, which is how a plugin can wrap a command it knows nothing about. `meta exec` runs an arbitrary command against the child repositories, and it is itself a plugin that ships by default. Anything a plugin does not cover, you fall back to `meta exec`.

Two capabilities make the plugins more than command aliases. They inherit from `loop`, a separate project by the same author, the ability to target a chosen set of directories, so a command can run against a subset rather than everything. The README gives the example of checking git status in only some directories:

```
meta git status --include-only dir1,dir2
```

And a plugin can extend the underlying tool rather than just wrap it. The example given is `git update`, which is not a git command at all: `meta git update` clones repositories present in the `.meta` file that are not cloned locally, a problem that does not exist with a single repository.

The available plugin list names meta-init, meta-project, meta-git, meta-exec, meta-gh and meta-loop. The naming convention is doing real work here. Because each command maps to a plugin whose name matches the command, `meta npm install` implies a `meta-npm` plugin exists, and the README uses installing that plugin as the worked example of adding functionality.

## A development branch version beside a release history that stopped in 2021

The package.json in this repository declares the version as `0.0.0-development`, which is not a published version number. That is a deliberate pattern for a tool whose releases are cut from a different branch, so the default branch shows a placeholder while the release pipeline substitutes a real version.

It also means the version field tells you nothing about what you would install. For that you have to look at the release history, and there the dates are the interesting part. The three most recent releases are v2.2.25 from April 2021, v2.2.24 and v2.2.23 both from the same day in April 2021, and all three contain exactly one change: a dependency bump, to meta-project and to meta-loop respectively.

Set against that, the repository's last push is 2026-09-27, the project is not archived, and it has 2225 stars and 104 forks. Both facts are in the repository and neither cancels the other. Commits are landing and published tags have not moved since 2021.

Two more signals point the same way. The README's build badge points at travis-ci.com, and the dependency and contributor badges point at daviddm, a service that has been effectively dormant for years. Those are badges that were accurate when written and now advertise infrastructure nobody is checking. It is worth reading the repository's metadata as a snapshot of around 2021 and the commit history as current work, rather than assuming the two agree.

## Where the boundaries are, and what it does not try to be

It helps to be clear about what this tool is not. It is not a build system, so nothing here compiles or tests your projects. It is not a dependency manager, and `meta npm install` is a convenience that shells out to the package manager rather than an installer with its own lockfile handling. It does not enforce consistency across projects, so nothing checks that two repos declare the same dependency version.

It also does not manage branches or commits for you. `meta exec "git checkout master"` runs a git command across the set, which means the blast radius of that command is every child repository. The README shows it as the basic way to interact with a meta repo, and the risk is implicit: there is no dry run and no transaction, so a command that is wrong in one repository is wrong in all of them. The `--include-only` targeting inherited from loop is the mitigation, and it is the flag to reach for before running anything destructive.

Two features from the Why list suggest where the author's own priorities sit. Giving different teams different slices of an architecture, using multiple meta repos, means overlapping groups of projects can each have their own parent repo, and `meta project migrate` is offered as a way to convert an existing monorepo into that arrangement. Both point at a tool intended for organisations with more repositories than one person can comfortably track.

## How this compares to submodules and workspaces

The three common answers to multi-repository composition are git submodules, monorepo tools like npm or pnpm workspaces, and this. Their tradeoffs differ more than the marketing suggests.

Submodules are the closest in mechanism, since both record a set of child repositories somewhere the parent can see. The difference is what happens on a plain `git pull`. With submodules, a new submodule appears in the tree and stays uninitialised until someone runs a submodule update command. With meta, the `.meta` file is the record and `meta git update` is the step you run after pulling, so the failure mode is a missing checkout rather than a stale pointer. Submodules also record a specific commit per child, which pins versions; meta records a URL, which does not. That is a real difference in kind, and it means meta composes a set of repositories rather than a set of versions.

Workspaces are the opposite bet. npm and pnpm workspaces assume one repository containing many packages, which makes cross-package changes a single commit and dependency linking automatic. What they cost is that every package shares one history, one version and one release, and any change to one package produces a diff against all the others.

Meta's position between the two is coherent: keep independent histories and independent releases, and accept a layer of tooling to work across them. That is a good fit when projects are genuinely separate products sharing a team, and a poor fit when a change routinely spans several of them, since there is no atomic commit for anything that crosses a boundary.

## Conclusion

meta is a genuinely different answer to the monorepo question because it declines the trade rather than picking a side: you keep per-project repositories with their own history and releases, and a small parent repo records the set. The cost is a layer of indirection that every new engineer has to learn, and a tool whose last published release was in 2021 even though the repository is being pushed to. Judge it on that split. The mechanism is well documented for the operations it covers, cloning, updating and running a command across every child repo, and less complete for everything else. If your team already has a working story for multi-repo operations, meta's advantage is small. If you have eleven repos and a script that keeps being rewritten, the `.meta` file and `meta git update` are worth an afternoon.

## FAQ

### What does the meta repository store?

It stores a .meta file listing references to the child repositories, plus a .gitignore that keeps those cloned directories out of the parent's status. The source code stays in the individual project repositories.

### How do I clone a project that uses meta?

Use meta git clone with the meta repo URL instead of git clone. It fetches the parent repository and all of the children listed in the .meta file in one step.

### How do I get new child repositories that a teammate added?

Pull the meta repo to pick up the updated .meta file, then run meta git update. It clones any repository in that file which is not present locally yet.

### Can I run a command against only some of the repositories?

Yes. Commands inherit loop's directory targeting, so flags such as --include-only dir1,dir2 restrict execution to the named directories instead of all child repos.

## Sources

- [Issues](https://github.com/mateodelnorte/meta/issues)
- [License: MIT](https://github.com/mateodelnorte/meta/blob/master/LICENSE)
- [mateodelnorte/meta on GitHub](https://github.com/mateodelnorte/meta)
- [README](https://github.com/mateodelnorte/meta/blob/master/README.md)
- [Releases](https://github.com/mateodelnorte/meta/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mateodelnorte-meta
