Arborist lets you work on multiple features across several repositories at the same time, 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 reference clone (a clean, unchanged copy of the repository that is never directly modified) of each repository and creating temporary workspaces for actual development.
Here's what that looks like on disk:
~/my-project/
├── .arb/repos/
│ ├── frontend/ ← reference 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 is an isolated development environment, typically used to represent 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 reference 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. Under the hood, Arborist tracks the identity and state of commits across both axes — detecting rebases, squash merges, and conflicts before they happen — so its commands can tell you what's safe before you act.
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-mode› Workspace: add-dark-mode
› Branch: add-dark-mode (same as workspace, use --branch to override)
› Base: repo default (use --base to override)
Without 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.
? Repos:
◉ 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 optional shell integration 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 frontend› Workspace: fix-login-crash
› Branch: fix-login-crash (same as workspace, use --branch to override)
› Base: repo default (use --base to override)
› Repos: frontend
Passing 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:
add-dark-mode (base origin/main, share origin/add-dark-mode)
REPO LAST COMMIT BASE SHARE LOCAL
* backend just now 1 ahead 1 to push clean
frontend 5 minutes 1 ahead, 1 behind 1 to push clean
Rebase to integrate the upstream changes (the integration axis from the mental model):
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 (the sharing axis) 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) to predict file-level conflicts per repo. The plan shows "conflict unlikely" or "conflict likely" so you can decide before anything runs. For repos with uncommitted changes, Arborist suggests --autostash and predicts whether re-applying the stash will also conflict.
api rebase add-auth onto origin/main — 4 behind, 3 ahead (conflict unlikely) (autostash)
payments rebase fix-checkout onto origin/main — 6 behind, 2 ahead (conflict likely)
shared up to date
If a rebase or merge does hit a conflict, Arborist continues with the remaining repos and reports per-repo conflict details and resolution instructions in a single pass. One conflicting repo never blocks the others.
For arb pull --merge, if the remote was rewritten and you have no unique commits, Arborist resets to the rewritten tip instead of attempting a three-way merge.
arb undo reverses the last operation across all repos — whether it's still in progress or already completed. Say you run a rebase, hit a conflict, resolve it, continue — and then realise the resolution was wrong:
arb rebase # conflicts in payments
# fix conflicts... or so you think
arb rebase --continue # completes — but the result looks wrong
arb undo # rolls back all repos to their pre-rebase state, ready to try againYou can also undo selectively — keep the rebase in most repos but roll back the ones that went wrong:
arb undo payments # undo only payments, leave the rest
arb undo # later: undo any remaining reposThis works for any tracked operation: rebase, merge, retarget, extract, pull, reset, rename. Undo aborts in-progress git operations, resets HEADs, and rolls back config — even uncommitted changes. It detects if you have done other changes, so it never silently discards work. It is as close to a magic wand as you can get!
When you rebase, squash-merge, or force-push, Git creates new commits that replace old ones. The old and new commits look different (different hashes), but represent the same work. Arborist matches them automatically — using patch identity and reflog history — so status and plans always show what's genuinely new versus what you've already seen.
arb status breaks push and pull counts down by identity — "outdated" for remote commits already reflected in your local history, "new" for genuinely new remote work:
my-feature (base origin/main, share origin/my-feature)
REPO LAST COMMIT BASE SHARE LOCAL
api 1 hour 2 ahead 1 from base + 2 rebased → 2 outdated clean
shared 2 hours 2 ahead, 3 behind 2 new → 1 new 1 change
When the "new" count is zero, every remote-only commit is already reflected in yours — a force push won't overwrite any collaborator work. arb push uses this to allow pushing after rebase, amend, or squash without requiring --force, and to block pushes of already-merged branches. arb rebase replays only the genuinely new work.
After your PR is merged, Arborist detects it — even for squash merges — and shows it clearly in arb list. PR numbers are detected automatically from merge and squash commit messages — no configuration needed:
WORKSPACE BRANCH REPOS LAST COMMIT STATUS
proj-208-login proj-208-login 3 3 hours merged (#42), gone
proj-215-dark 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.
arb watchA live status dashboard that auto-refreshes on filesystem changes. Press r to rebase, m to merge, l to pull, p to push — each runs the full interactive flow (plan, confirm, execute) without leaving the dashboard. Press f to fetch, v to toggle verbose mode. Ideal for a split terminal while AI agents or tests work in the background.
my-project • feature-branch • status
REPO LAST COMMIT BASE SHARE LOCAL
api 1 minute 2 ahead 1 to push clean
shared 3 minutes 2 ahead, 3 behind up to date 1 change
f fetch • v verbose • r rebase • m merge • l pull • p push • q quit
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.
Need to build a feature on top of another in-progress feature? Use --base to branch from a specific branch instead of the default:
arb create auth-ui --base feat/auth --all-reposWhen the base branch is later merged (e.g. via a PR), arb status detects this and shows "base merged" — preventing the painful mistake of rebasing onto a branch that's already gone. Run arb retarget to cleanly rebase onto the default branch and update the workspace config.
Already committed everything to one branch? arb extract splits it into a stack
after the fact — see arb help stacked.
Contributing to an open-source project? Clone your fork and register the upstream in one step:
arb repo clone https://github.com/you/api.git --upstream https://github.com/org/api.gitArborist auto-detects remote roles from git config, so rebase targets upstream while push goes to your fork — no extra configuration needed. Different repos in the same workspace can mix layouts freely — some forked, some single-origin — and Arborist resolves remote roles independently for each.
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 line
arb exec npm install # run a command in every repo
arb status -w dirty,unpushed # filter repos by status flagsAll state-changing commands support --dry-run to preview the plan and --yes to skip confirmation prompts. status, branch, list, log, and repo list support --json for structured output and --quiet for one name per line — useful for feeding into other commands. arb exec runs any command in each repo; --where (-w) filters repos by status flags like dirty, behind-base, or ahead-share and works across most 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. And you still need to build the tools.
- 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 creating workspaces, checking status, and running commands.
- Staying in sync, how rebase, merge, push, pull, and reset work across repos.
- Managing workspaces, how to list, navigate, rename, and remove workspaces.
- Workspace templates, a way to seed files into new workspaces.
- Fork workflows, how to use Arborist with fork-based development.
- Stacked branches, branching from feature branches and retargeting when they merge.
- Scripting and automation, using Arborist from scripts and pipelines.
- Tips and tricks, small conveniences for day-to-day usage.
- Under the hood, how commit matching, conflict prediction, and phased rendering work.