Arborist lets you work on multiple features across several repositories in parallel, without juggling branches, breaking your flow, or losing changes.
Based on Git worktrees, Arborist complements standard Git with structured workspaces and cross-repo coordination. If your project spans multiple repos — microservices, a frontend/backend split, shared libraries — Arborist keeps them in sync.
arborist (noun) ˈär-bə-rist — a specialist in the care and maintenance of trees
Git worktrees make it possible to check out multiple branches of the same repository at the same time, each in its own directory. Arborist builds on this by keeping a stable canonical clone of each repository and creating temporary workspaces for actual development.
Here's what that looks like on disk:
~/my-project/
├── .arb/repos/
│ ├── frontend/ ← canonical clones, managed by Arborist
│ ├── backend/
│ └── shared/
│
├── add-dark-mode/ ← workspace on branch "add-dark-mode"
│ ├── frontend/
│ └── backend/
│
└── fix-login-crash/ ← workspace on branch "fix-login-crash"
└── frontend/
You work in the workspaces. Each workspace represents one feature or issue. It contains a working copy of each selected repository, with the feature branch checked out. Workspaces can exist side by side and are removed when the task is complete. The canonical clones under .arb/ are managed by Arborist — you never touch them directly.
Keeping your work in sync involves two axes:
| Axis | Commands | Purpose |
|---|---|---|
| Integration | rebase, merge |
Keep your feature branch up to date with the base branch |
| Sharing | push, pull |
Share your feature branch with collaborators |
Arborist's synchronization commands handle this across all repos at once.
Arborist requires Git 2.17+ and works on macOS, Linux, or Windows using WSL. Git 2.38+ enables conflict prediction before rebase, merge, and pull operations.
Quick install:
curl -fsSL https://raw.githubusercontent.com/henrikje/arborist/main/install.sh | bash
Homebrew:
brew install henrikje/tap/arb
On Linux, install Homebrew's requirements to avoid "cannot be installed" errors.
From source (requires Bun):
git clone https://github.com/henrikje/arborist
cd arborist
./install.sh
mkdir ~/my-project
cd ~/my-project
arb initInitialized project
~/my-project
Next steps:
arb repo clone <url> Clone repos into the project
arb create <name> Create a workspace
arb init marks the current directory as an Arborist project to hold your workspaces.
Next, clone the repositories you want to work with. They are stored in .arb/repos.
arb repo clone https://github.com/example/frontend.git
arb repo clone https://github.com/example/backend.git
arb repo clone https://github.com/example/shared.gitWith the repos cloned, create a workspace. Let's say you're adding dark mode.
arb create add-dark-modeWithout repo arguments, Arborist prompts you to pick which repos to include. Not every feature touches every repo — picking just the ones you need keeps the workspace focused.
? Select repos to include
◉ backend
❯ ◉ frontend
◯ shared
↑↓ navigate • space select • a all • i invert • ⏎ submit
Both selected repos will be checked out on the add-dark-mode branch. With the shell extension installed, Arborist automatically moves you into the new workspace.
# You're in ~/my-project/add-dark-mode
cd frontend
# hack hack hack
git commit -am "Add dark mode toggle to navbar"Frontend is done. On to the backend:
cd ../backend
# hack hack hackThen a bug report comes in: logins are crashing! You need to fix it now, but your backend work is mid-flight. No time to commit. No problem, you create a second workspace:
arb create fix-login-crash frontendPassing repos inline skips the interactive prompt — useful when you know exactly what you need.
Both workspaces now exist side by side. arb list shows the full picture:
WORKSPACE BRANCH REPOS LAST COMMIT STATUS
* fix-login-crash fix-login-crash 1 1 day no issues
add-dark-mode add-dark-mode 2 2 minutes dirty, unpushed
Fix the bug, push, and clean up:
# You're in ~/my-project/fix-login-crash
cd frontend
# hack hack hack
git commit -am "Fix null pointer in login flow"
arb push
arb delete fix-login-crashThe hotfix is shipped. Pick up where you left off:
arb cd add-dark-mode/backend # arb cd works from anywhere inside the project
# finish backend work
git commit -am "Add dark mode API endpoint"Let's run arb status to get an overview. The hotfix landed on main while you were away, so frontend is now one commit behind:
REPO LAST COMMIT BASE SHARE LOCAL
* backend just now origin/main 1 ahead origin/add-dark-mode 1 to push clean
frontend 5 minutes origin/main 1 ahead, 1 behind origin/add-dark-mode 1 to push clean
Rebase to integrate the upstream changes:
arb rebaseArborist shows a plan, including a conflict prediction for each repo, and asks for confirmation before proceeding:
backend up to date
frontend rebase add-dark-mode onto origin/main — 1 behind, 1 ahead (conflict unlikely) (HEAD a1b2c3d)
? Rebase 1 repo? (y/N)
Synchronization commands automatically fetch all repos in parallel before operating, so you can be sure that the plan is up to date.
Before pushing, review what you've done across both repos:
arb log==> backend <==
a1b2c3d Add dark mode API endpoint
==> frontend <==
e4f5g6h Add dark mode toggle to navbar
Logged 2 repos (2 commits)
Then push both repos and clean up:
arb push
arb delete add-dark-modeNow you're ready to create new workspaces to tackle new tasks!
The tour covered the essentials. Here are more capabilities worth knowing about.
Before a rebase, merge, or pull runs, Arborist performs a trial three-way merge in memory (using the same algorithm Git uses) against each repo to identify actual file-level conflicts. It can identify when earlier commits have been merged, rebased, and even squash merged. The result appears in the plan:
api rebase add-auth onto origin/main — 4 behind, 3 ahead (conflict unlikely) (autostash)
frontend replay 1 commit onto origin/main (was squash-merged)
shared up to date
You see which repos will conflict before you commit to the operation. Arborist also suggests options to handle possibly dangerous situations, such as --autostash to operate on a repo with uncommitted changes without manual stashing.
If a rebase or merge does hit a conflict, Arborist continues with the remaining repos and reports everything at the end. One conflicting repo never blocks the others. You see per-repo conflict details and resolution instructions in a single pass.
For arb pull --merge, Arborist can also detect rebased/force-pushed remote history and, when local has no unique commits to preserve, show a safe reset action in the plan and reset to the rewritten remote tip instead of running a three-way merge.
arb list --where at-risk # workspaces that need attention
arb delete --where gone # clean up after merged PRs
arb status --where dirty,unpushed # repos matching either
arb push --where unpushed+^behind-base # push only repos that won't need a rebaseArborist tracks status flags across repos — dirty, unpushed, behind-base, diverged, drifted, and more. The --where flag (-w for short) lets you filter by any combination, and works across most commands. Use --dirty as a shorthand for --where dirty.
arb exec npm install
arb exec --dirty git stash
arb open codearb exec runs any command in each repo, using the repo directory as working directory. arb open does the same but passes all repo paths as arguments — useful for editors like VS Code or IntelliJ. Combine with --dirty or --where to narrow the scope.
After your PR is merged, Arborist detects it — even for squash merges — and shows it clearly in arb list. Ticket keys (like Jira or Linear) and PR numbers are detected automatically from branch names and commit messages — no configuration needed:
WORKSPACE TICKET BRANCH REPOS LAST COMMIT STATUS
proj-208-login PROJ-208 proj-208-login 3 3 hours merged (#42), gone
proj-215-dark PROJ-215 proj-215-dark 2 1 day merged, gone
new-feature new-feature 3 5 minutes unpushed
No guessing which branches have landed. You see "merged" with the detected PR number from the merge/squash commit, and "gone" when the remote branch was deleted. Ready to arb delete.
cd my-feature/api
arb template add .env
# from now on, every new workspace gets api/.env automaticallyTemplates let you capture files and have them seeded into every new workspace. Common uses include .env files, IDE settings, and AI agent config. Templates live in .arb/templates/ and are version-controllable. See Template examples for ready-to-use starting points.
arb --help # list all commands
arb create --help # detailed usage for a specific commandEvery command supports --help. If you're unsure what flags are available or how a command works, --help is the fastest way to find out.
arb create auth-ui --base feat/auth --all-reposThe --base flag creates a workspace that branches from a specific base instead of the default, letting you stack feature branches. When the base branch is later merged into the default branch (e.g. via a PR), arb status detects this and shows "base merged" — preventing the common and painful mistake of rebasing onto a branch that's already been merged. Run arb rebase --retarget to cleanly rebase onto the default branch and update the workspace config. For deeper stacks (e.g. A → B → C), use arb rebase --retarget feat/A to retarget to a specific branch.
arb repo clone https://github.com/you/api.git --upstream https://github.com/org/api.gitOne command clones your fork and registers the canonical repository. Arborist auto-detects remote roles from git config, so rebase targets the base while push goes to your fork — no additional configuration needed. Different repos in the same workspace can use different remote layouts — some forked, some single-origin — and Arborist resolves remote roles independently for each, so rebase targets the right base and push goes to the right fork without per-repo configuration.
arb push --dry-run # preview without executing
arb rebase --yes # skip confirmation
arb branch --quiet # just the branch name
arb status --json | jq ... # machine-readable output
arb list --quiet | xargs ... # one workspace name per lineAll state-changing commands support --dry-run to preview the plan and --yes to skip confirmation prompts. status, branch, list, log, diff, and repo list support --json for structured output and --quiet for one name per line — useful for feeding into other commands. Exit codes are meaningful: 0 for success, 1 for issues, 130 for user abort. Human-facing output goes to stderr, machine-parsable data to stdout — so piping works naturally.
There are several ways to approach multi-repo development:
- Raw
git worktree+ scripts — Flexible and lightweight, but you must build your own cross-repo status, safety checks, and coordination. - Multiple clones per feature — Simple, but duplicates repos and makes it harder to see overall state.
- Submodules / meta-repos — Centralize checkouts, but add Git complexity and don’t inherently solve parallel feature isolation.
- Repo orchestration tools (
repo,west, etc.) — Good for syncing large trees, less focused on feature-branch workflows. - Monorepo — Removes the coordination problem entirely, but may mix projects with different lifecycles, and restructuring is not always an option.
Arborist is for teams that want to keep repositories independent while adding a thin, Git-native coordination layer for safe, parallel, multi-repo feature work.
To learn more about Arborist, check out the following resources:
- Day-to-day usage, a deeper dive into the commands you use when working in a workspace.
- Managing workspaces, how to create, navigate, and remove workspaces.
- Workspace templates, a way to seed files into new workspaces.
- Fork workflows, how to use Arborist with fork-based development.
- Scripting and automation, using Arborist from scripts and pipelines.
- Tips and tricks, small conveniences for day-to-day usage.
- Under the hood, how Arborist works internally.