________ ___ _________ ___ __
|\ ____\|\ \|\___ ___\ |\ \ |\ \
\ \ \___|\ \ \|___ \ \_|_________\ \ \ \ \ \
\ \ \ __\ \ \ \ \ \|\_________\ \ \ __\ \ \
\ \ \|\ \ \ \ \ \ \|_________|\ \ \|\__\_\ \
\ \_______\ \__\ \ \__\ \ \____________\
\|_______|\|__| \|__| \|____________|
A meta-repo is a working folder containing multiple Git repositories, each organized into child folders. Meta-repos are a common pattern for working with microservices locally and managing development that spans across backend, frontend, and packages at the same time.
git-w makes it easy to set up, share, manage, and run operations across a meta-repo.
Invoke it as git w <cmd> via Git's plugin system. As long as git-w is in your $PATH, Git will find it automatically.
It uses a config file that can be committed to version control to share your meta-repo configurations between teams.
- Declare and track multiple repos in a single
.gitwconfig file (TOML) - Clone missing repos and pull existing ones with one
restorecommand - Run
fetch,pull,push, andstatusacross all repos (or a filtered subset) in parallel - Execute any arbitrary git command across repos with
exec - Organize repos into named groups
- Set an active context to scope all commands to a group without specifying it each time
- Local overrides (active context) stored in
.gitw.local, which is kept out of version control automatically
Requires Go 1.26+.
go install github.com/robertwritescode/git-w@latestOr clone, build, and install to $GOPATH/bin with Mage:
git clone https://github.com/robertwritescode/git-w.git
cd git-w
mage installMake sure $GOPATH/bin (or $GOBIN) is in your $PATH so that Git can find the plugin.
# Create a workspace config in the current directory
git w init my-workspace
# Clone a repo and register it
git w clone https://github.com/org/repo-a
# Register an existing local repo
git w add ../repo-b
# Recursively register all git repos under a directory
git w add -r ./projects/
# Fetch across all repos
git w fetch
# Show a status table for all repos
git w infogit-w uses two TOML files in the same directory:
| File | Purpose | Version control |
|---|---|---|
.gitw |
Workspace definition (repos, groups, settings) | Commit this |
.gitw.local |
Local state (active context) | Auto-added to .gitignore |
Example .gitw:
[workspace]
name = "my-workspace"
# auto_gitignore = true # default: automatically adds cloned repo paths to .gitignore
[repos.repo-a]
path = "repos/repo-a"
url = "https://github.com/org/repo-a"
[repos.repo-b]
path = "repos/repo-b"
url = "https://github.com/org/repo-b"
[repos.repo-c]
path = "repos/repo-c"
[groups.backend]
repos = ["repo-a", "repo-b"]
[groups.frontend]
repos = ["repo-c"]
path = "/absolute/path/used/for/auto-context"| Command | Description |
|---|---|
git w init [name] |
Create a .gitw in the current directory. Defaults to the directory name. |
git w restore |
Materialize all repos: clone missing ones, pull existing ones (runs in parallel). |
| Command | Description |
|---|---|
git w add [<path>] |
Register an existing local git repo. |
git w add -r [<dir>] |
Recursively find and register all git repos under a directory. |
git w clone <url> [<path>] |
Clone a remote repo and register it in the workspace. |
git w remove <name>... |
Unregister one or more repos (also removes them from all groups). Alias: rm |
git w rename <old> <new> |
Rename a repo in the config. |
git w list [name] |
List all registered repo names. With a name, prints the absolute path to that repo. Alias: ls |
Both add and clone accept -g <group> / --group <group> to also add the repo to a group.
All git commands accept an optional list of repo names to filter targets. With no filter, the command runs against all repos (or the active context group, if one is set).
| Command | Description |
|---|---|
git w fetch [repos...] |
Run git fetch in repos. Alias: f |
git w pull [repos...] |
Run git pull in repos. Alias: pl |
git w push [repos...] |
Run git push in repos. Alias: ps |
git w status [repos...] |
Run git status -sb in repos. Alias: st |
git w exec [repos...] -- <git-args> |
Run any git command across repos concurrently. Output is prefixed with [repo-name]. Aliases: x, run |
Examples:
# Fetch all repos
git w fetch
# Pull only two specific repos
git w pull repo-a repo-b
# Run an arbitrary git command across all repos
git w exec -- log --oneline -5
# Run a command on specific repos
git w exec repo-a repo-c -- diff HEAD~1Groups let you organize repos into named sets. Many commands accept repo names as filters; groups serve as a logical layer on top of that.
# Create a group and add repos to it
git w group add repo-a repo-b --name backend
# Add repos to an existing group
git w group add repo-c --name backend
# List all groups
git w group list
# Show repos in a group
git w group info backend
# Show repos in all groups
git w group info
# Remove repos from a group
git w group remove-repo repo-c --name backend
# Rename a group
git w group rename backend services
# Delete a group
git w group remove backendThe active context scopes all git commands to a specific group without needing to specify repos each time. The active context is stored in .gitw.local and is local to your machine.
# Show the active context
git w context
# Set the active context to a group
git w context backend
# Auto-detect the active context based on CWD (uses the group's path attribute)
git w context auto
# Clear the active context
git w context noneWith an active context set, fetch, pull, push, status, exec, and info all operate on that group's repos by default.
# Show a status table for all repos (branch, remote state, last commit)
git w info
# Show status table for a specific group
git w info backend# Generate shell completion script (bash, zsh, fish, powershell)
git w completion bash
git w completion zsh| Flag | Description |
|---|---|
--config <path> |
Path to .gitw config. Defaults to the nearest .gitw found by walking up from the current directory. |
This project uses Mage as its build tool.
# Build binary to bin/git-w
mage build
# Install to $GOPATH/bin
mage install
# Run tests (with race detector)
mage test
# Lint (golangci-lint)
mage lint
# Lint + test + build (default)
mage
# Generate coverage report
mage coverReleases are automated from .github/workflows/release.yml using Release Please + GoReleaser on pushes to main.
Required repository secret:
TAP_GITHUB_TOKEN— a PAT with access torobertwritescode/homebrew-tap(at minimum, Contents: Read and write) so GoReleaser can update the tap cask.
Required repository setting:
- In GitHub, go to Settings → Actions → General and enable Allow GitHub Actions to create and approve pull requests (required for Release Please PR automation).
Release trigger:
- To publish a release, merge the open Release Please PR (for example,
chore(main): release 1.2.3) intomain; this is what creates the tag and runs the GoReleaser job. - To postpone/cancel a pending release, close that Release Please PR without merging and remove its
autorelease: pendinglabel; Release Please will open or update a new one after subsequent conventional commits land onmain.
Troubleshooting:
- If the release job fails with
resource not accessible by integration, the token used for the target repo is missing required permissions; verifyTAP_GITHUB_TOKENcan write contents torobertwritescode/homebrew-tap. - The release workflow runs
brew style --fixonCasks/git-w.rbinrobertwritescode/homebrew-tapafter GoReleaser publishes the cask. This is intentional: current GoReleaser output can fail Homebrew's latest cask style cops, so CI auto-normalizes stanza ordering/format before final push.
git-w was directly inspired by gita, a Python tool for managing multiple git repos side-by-side. gita introduced the core ideas of grouping repos and running git commands across them in parallel — git-w extends these ideas with a portable, single-binary implementation and a richer feature set.
Other projects in this space worth knowing about:
| Project | Language | Notes |
|---|---|---|
| gita | Python | Original inspiration. Groups, parallel commands, shell delegating. |
| myrepos (mr) | Perl | Classic multi-repo tool; highly configurable via .mrconfig. |
| meta | Node.js | Manages repos as a monorepo alternative using a .meta file. |
| mu-repo | Python | Runs git commands across multiple repos; supports grouping. |
| repo | Python | Google's tool for Android development; XML manifest-based. |
| git-xargs | Go | Run arbitrary commands across many GitHub repos via the API. |
| git-workspace | Rust | Sync and fetch repos from git providers into a structured local workspace. |