Git worktrees are a great fit for parallel development: one repository can have several branches checked out into separate directories at the same time.
That convenience creates a coordination gap.
Imagine two tasks starting from the same commit. One worktree handles invoices; another handles payments. Both tasks need a Django migration in the same billing app:
invoice worktree:
billing/migrations/0002_invoice.py
payment worktree:
billing/migrations/0002_payment.py
Neither file has to be committed yet. Each task looks reasonable in isolation, but the two branches now deserve a joint review before they meet.
I built BranchRadar to flag that situation while the work is still in progress. It reads Git metadata and compares changed paths across worktrees, including committed, staged, unstaged, and untracked files.
One important caveat up front: this is a path-level warning, not proof that the Django migration graph is broken. The goal is to surface a coordination decision early enough for a human to inspect it.
Install it
BranchRadar v0.1.1 requires Python 3.11+ and Git. You can install the release wheel directly into a virtual environment:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install \
https://github.com/mengchar-cmu-F25/branchradar/releases/download/v0.1.1/branchradar-0.1.1-py3-none-any.whl
The basic command is:
branchradar --repo /path/to/repo --base main
Reproduce the problem in a disposable repository
This demo creates an isolated Git repository and two worktrees. The migration files are intentionally empty because BranchRadar is testing path-level coordination, not executing Django.
br_demo=$(mktemp -d)
git init -b main "$br_demo/repo"
git -C "$br_demo/repo" \
-c user.name=Demo \
-c user.email=demo@example.test \
commit --allow-empty -m base
git -C "$br_demo/repo" worktree add -b invoice "$br_demo/invoice"
git -C "$br_demo/repo" worktree add -b payment "$br_demo/payment"
At first, both worktrees are clean:
branchradar --repo "$br_demo/repo"
The result ends with:
Risks: 0
Now create one untracked migration in each worktree:
mkdir -p \
"$br_demo/invoice/billing/migrations" \
"$br_demo/payment/billing/migrations"
touch "$br_demo/invoice/billing/migrations/0002_invoice.py"
touch "$br_demo/payment/billing/migrations/0002_payment.py"
branchradar --repo "$br_demo/repo"
Here is the relevant output. I shortened only the temporary path and base commit:
BranchRadar: 2 branches against main (<base-commit>)
- invoice (1 files) @ <tmp>/invoice; migrations=billing; dirty=1
- payment (1 files) @ <tmp>/payment; migrations=billing; dirty=1
Risks: 1
- [HIGH] parallel_django_migrations: invoice [refs/heads/invoice] <-> payment [refs/heads/payment]: both branches change Django migrations in 'billing'
invoice [refs/heads/invoice]: billing/migrations/0002_invoice.py
payment [refs/heads/payment]: billing/migrations/0002_payment.py
Both files are still untracked. The worktrees can even point to the same commit; BranchRadar includes their current working-tree state when it calculates the paths to compare.
A finding exits with status 1, so it can be used as a local guard or CI check. Run this demo interactively: a script using set -e would stop at that expected warning.
A negative control
A useful detector also needs an example it should not flag.
Remove the payment migration from billing, then create one under a different Django app:
rm "$br_demo/payment/billing/migrations/0002_payment.py"
mkdir -p "$br_demo/payment/orders/migrations"
touch "$br_demo/payment/orders/migrations/0002_order.py"
branchradar --repo "$br_demo/repo"
Now both worktrees contain migration changes, but their app paths do not overlap:
BranchRadar: 2 branches against main (<base-commit>)
- invoice (1 files) @ <tmp>/invoice; migrations=billing; dirty=1
- payment (1 files) @ <tmp>/payment; migrations=orders; dirty=1
Risks: 0
The command exits with status 0.
This matters because BranchRadar is not warning merely because two branches contain migration files. It groups migration paths by the directory before /migrations/ and reports a risk when both sides of a branch pair change migrations in the same app.
How the comparison works
By default, BranchRadar analyzes branches currently checked out in worktrees. --all-local-branches opts other local branches into the scan.
For every pair of candidates, it finds their mutual merge base and collects the paths exclusive to each side after that point. This helps avoid treating stacked or shared history as two independent changes. Checked-out worktrees also contribute staged, unstaged, and untracked non-ignored paths.
For automation, JSON output is available:
branchradar --repo /path/to/repo --base main --format json
The exit statuses are intentionally simple:
0 no configured path overlap found
1 one or more risks found
2 usage or Git error
BranchRadar also has a configurable detector for API producer/consumer overlap. For example:
[contracts.public_api]
producer = ["backend/api/**", "openapi/**"]
consumer = ["frontend/src/api/**"]
If one branch changes a configured producer path while another changes its consumer path, the report names both branches, their merge base, and the evidence paths. That is still a request for joint review, not a claim that the API is definitely incompatible.
What v0.1.1 intentionally does not do
BranchRadar does not:
- inspect migration operations or dependency declarations;
- prove that two migrations are incompatible;
- validate API semantics;
- run Django commands or project tests;
- merge or modify branches;
- run coding agents or build a general code graph.
Two same-app migrations may be compatible and still produce a warning. Migrations in different apps may have relationships that path matching cannot see. Squash-equivalent work without shared Git ancestry can also produce a warning.
I kept this first version narrow because an explainable warning with exact paths is useful only if the signal remains easy to verify.
The feedback I want
If you use Git worktrees—or run coding agents in parallel—I would especially value answers to these questions:
- Is “two independent migration changes in the same Django app” useful in your workflow, or too noisy?
- Should a later version inspect Django migration dependencies, or is path-level evidence easier to trust?
- Should checked-out worktrees remain the default scope, or should every local branch be scanned automatically?
The repository, fixed v0.1.1 wheel, full synthetic demo, and issue tracker are here:
github.com/mengchar-cmu-F25/branchradar
Disclosure: this post was produced with AI assistance. Its commands and claims were checked against the linked repository, its passing test suite, and a fresh disposable two-worktree run. I remain responsible for the content.
Top comments (0)