Going Parallel: Git Worktrees, or How to Be Several Developers at Once
Part 6 of The Thingino Developer’s Journey / <- Part 5
By now you have a problem, and it’s a good one: too many irons in the fire. A camera port waiting on review feedback. A streamer feature mid-refactor. A user in Discord with a bug that needs a quick fix now. And if you’ve started using AI coding agents, you may have several of them wanting to work at the same time, in the same tree, on the same files. That way lies madness.
The traditional answer — git stash, switch branch, rebuild everything,
fix, switch back, stash pop, rebuild everything again — is miserable in
any project. In a firmware project where a context switch can cost a 30-minute
rebuild, it’s disqualifying. And if an AI agent had twenty minutes of context
built up in that tree, the branch switch just lobotomized it.
Worktrees in one paragraph
A git worktree is an additional working directory attached to the same
repository. One .git object store, one shared history — but each worktree
has its own checked-out files, its own branch, its own build state. Work in
one is invisible to the others until it’s committed. It’s the isolation of
multiple clones without the duplication, and it’s been hiding in git since
2015:
thingino-firmware/ <- main checkout (master, make update lives here)
thingino-firmware-feat-motion/ <- worktree: feature work
thingino-firmware-fix-rtsp-crash/ <- worktree: that Discord bug
thingino-firmware-port-newcam/ <- worktree: camera port
The working model that makes this scale — for humans and agents alike:
One task, one branch, one worktree, one agent.
Each task gets a directory. Each directory gets one occupant. Nobody stashes,
nobody switches, nobody overwrites anyone’s work. Abandoning an experiment is
rm-grade cheap; the branch and its commits survive in the shared history.
Why this is especially good in Thingino
Here’s the part that surprises people: Thingino was accidentally worktree-ready all along.
- Builds can’t collide. Output paths include the branch name:
output/<branch>/<camera>-.../. Two worktrees building the same camera on different branches write to different directories by construction. - The expensive stuff is shared. ccache lives in
~/.buildroot-ccache, so a “clean” build in a fresh worktree is mostly cache hits. The download cache and prebuilt toolchains can be shared too. A new worktree costs about a checkout, not a first-build. - Long builds stop being hostages. Kick off a full build in one worktree, keep editing in another. Your hotfix no longer waits for your feature’s linker.
But a bare git worktree add is not enough here, and this is the one
place where Thingino needs you to know three things: the buildroot/
submodule must be initialized per-worktree, Thingino’s Buildroot patches must
be applied to it, and the dl/ cache should be shared rather than
re-downloaded. That’s exactly why the tree ships a helper.
The helper: scripts/worktree.sh
# From the main checkout: create a task worktree
scripts/worktree.sh create feat/motion-tuning
# It: creates ../thingino-firmware-feat-motion-tuning on a new branch,
# initializes buildroot (sharing objects with your main checkout - no
# re-clone), applies the Buildroot override patches, and symlinks dl/.
cd ../thingino-firmware-feat-motion-tuning
CAMERA=atom_cam2_t31x_gc2053_atbm6031 make fast # business as usual
At the end of a session, keep the branch from drifting away from master:
scripts/worktree.sh sync # rebase onto origin/master, re-sync buildroot
git push --force-with-lease # after a rebase, always with-lease
And when the PR merges:
cd ../thingino-firmware # back to the main checkout
scripts/worktree.sh remove feat/motion-tuning # directory gone, branch preserved
scripts/worktree.sh list shows the whole fleet.
The house rules
Learn these once and worktrees will never bite you:
- Never run
make updatein a feature worktree. Itgit pull --rebasees whatever branch you’re on. Update the main checkout withmake update; sync feature worktrees withworktree.sh sync. - A branch lives in one worktree at a time.
fatal: already checked outisn’t an error, it’s a bodyguard — it makes the two-agents-one-branch disaster structurally impossible. - Gitignored files don’t travel. Your
local.mk,user/layers, andoverrides/don’t appear in new worktrees. Usually that’s what you want — a task worktree should be close to pristine. Set up per-worktree copies only when the task needs them, and never point two worktrees at the sameoverrides/<pkg>/checkout (that’s shared mutable state, the exact thing we came here to eliminate). - Container builds want the main checkout. A linked worktree’s
.gitis a pointer file to the main repo — which isn’t mounted inside the build container. Build on the host in worktrees; usebuild-container.shfrom the main checkout.
And now, the agents
This workflow is how you run multiple AI coding agents on Thingino without
chaos. Give each agent its own worktree, its own task, and explicit scope
(“you own ../thingino-firmware-fix-rtsp-crash, this is the acceptance
criterion, every other directory is read-only”). Agents keep their context
because nobody switches branches under them; agents can’t corrupt each other
because they’re in different directories; reviewing an agent’s work is
reviewing a branch — the same PR flow you’d use for a human.
While a long build runs, lock the worktree so nothing prunes it out from under the compiler:
git worktree lock ../thingino-firmware-feat-motion-tuning --reason "build running"
Your role shifts from typist to tech lead of a very fast, very literal team: scope tasks, review diffs, merge, delete the worktree, repeat.
The full arc
Look at where you started: cloning a repo and waiting an hour for a build. Now you’re running a main checkout that tracks master, three task worktrees building three cameras in parallel, a package override with real git history, and an agent grinding through a refactor in a directory it can’t escape — while you review the camera port that just got its second thumbs-up.
That’s the whole journey. The deep reference lives in docs/worktrees.md;
the rest lives in docs/, in the Discord, and in the boot logs of cameras
that don’t phone home anymore — partly because of you.
What you learned
- Worktrees: one repo, many working directories, zero stashing.
- Thingino is naturally worktree-friendly (branch-scoped output, shared
caches) — and
scripts/worktree.shhandles the parts that aren’t (submodule, patches, dl). - House rules: no
make updatein worktrees, one branch per worktree, gitignored files don’t travel, containers stay in the main checkout. - One task, one branch, one worktree, one agent.
— The End. Now go port something.