Why would I need worktrees?
When I first learned about Git worktrees, I thought they were an interesting idea, but I couldn't really see myself using them. If you're unfamiliar with them, worktrees let you check out multiple branches of the same Git repository into separate directories. Unlike cloning the repository several times, all worktrees share the underlying Git data. Instead of constantly switching branches in one directory, you can have something like this:
Each directory has its own working files, so you can work on all three branches without switching back and forth. It's a neat trick, but I didn't see why I would want it. My brain is single-threaded: I work on one thing at a time, and when I need to move to another task, I switch branches. Having three directories open at once didn't seem useful when I can only focus on one problem anyway.
As a data scientist working mostly in Python, I also had a more practical reason to stay away: disk space. At the time, I managed dependencies with Poetry, and with a separate virtual environment for each worktree, I could easily end up with this:
-
my-project/
- .venv/GBs
- data/gitignored
- src/
-
my-project-feature-a/
- .venv/GBs
- data/gitignored
- src/
-
my-project-feature-b/
- .venv/GBs
- data/gitignored
- src/
Python environments aren't always small. A deep learning framework such as PyTorch or TensorFlow, together with the usual scientific stack, can easily push a single environment into gigabytes, and three or four worktrees would mean storing essentially the same dependencies three or four times. To be fair, Poetry doesn't require environments to live inside the project directory. By default, it keeps them in a central location and caches downloaded packages. Separate environments still generally contain their own copies of the installed files, though.
Then there were the datasets. In data science, you often work with large files that aren't tracked by Git, such as a 20 GB dataset, model checkpoints, or intermediate results from an experiment. These are usually gitignored, and worktrees don't share gitignored files. You either duplicate them, keep them outside the repository, or set up links to shared storage.
All of this is solvable, but I didn't have a reason to solve it. I was happy switching branches in one directory, and worktrees looked like extra complexity, extra disk usage, and more things to manage, so I ignored them.
Then uv and AI agents changed my mind
The first thing that changed wasn't Git but Python dependency management. I started using uv, and it took away one of my biggest objections. If you haven't tried it, uv is a Python package and project manager written in Rust. It covers most of what I used Poetry for, including dependencies, virtual environments, Python versions, and lockfiles.
Its speed is nice, but for worktrees the more interesting part is how uv handles its package cache. It keeps a global cache and can reuse its contents when creating virtual environments, rather than always copying every package into every environment. It supports several installation methods:
- Copy-on-write cloning: files initially share underlying data blocks, with separate storage allocated only when changes are made.
- Hard linking: multiple file paths point to the same underlying file data.
- Copying: packages are copied normally when necessary.
- Symbolic linking: also supported, although uv discourages it because it couples the environment to the cache.
By default, uv uses copy-on-write cloning on macOS and Linux and hard links on Windows, and the actual savings depend on your filesystem and the link mode. What matters is that multiple virtual environments can share the same underlying package data. If I have three worktrees, each with its own .venv and the same version of PyTorch, I don't necessarily need three physical copies of PyTorch.
Using it is simple. In a uv-managed project, uv sync creates or updates the virtual environment based on the project's dependencies and lockfile. You can find the shared cache with uv cache dir, and uv -v sync shows the installation details:
uv sync
uv cache dir
uv -v sync
This doesn't mean uv guarantees zero duplication. Copy-on-write needs filesystem support, hard links have their own constraints, and sometimes copying is unavoidable. Still, compared with my Poetry setup, it made maintaining multiple environments much more attractive. If you're still using Poetry, I think uv is worth trying for this reason alone, especially if you often create environments with large, overlapping dependencies.
That removed one of my main arguments against worktrees. Large gitignored datasets were still an issue, but those could live in a shared location outside the worktrees. The remaining problem was that I still didn't actually need worktrees, at least not until AI coding agents came along.
Before agents, my workflow was essentially sequential. I'd start a feature, write some code, test it, fix whatever was broken, and eventually move on to something else. I could have several tasks in progress, but since I was the one writing the code, I couldn't meaningfully work on them at the same time. Agents changed that. I could ask one to implement a feature and, while it was working, start another on a completely different task, and then another.
Suddenly, I had a good reason to use worktrees. I could create a separate working directory for each agent:
git worktree add -b feature-a ../feature-a
git worktree add -b feature-b ../feature-b
Each agent then works independently, with its own branch, directory, and environment. One can implement a new feature while another fixes a bug, without their files interfering and without me switching branches. Thanks to uv, the separate Python environments were no longer much of a concern. For the first time, worktrees made complete sense to me. I had found the use case I was missing, so I started using them, and at least initially, it was great.
Turns out, my brain is still single-threaded
For the first few weeks, I felt very productive. I could start an agent on one task, move to another worktree, start another agent, and keep going. Instead of waiting for an agent to finish, I used that time to make progress on something else. Why work on one thing when you can work on five?
Technically, everything worked. Worktrees kept the code isolated, uv made the environments manageable, and the agents wrote code in parallel. The problems that had kept me away from worktrees were either solved or much less important. But after a couple of months, I noticed I was exhausted. It wasn't from writing too much code, since I was writing less than before. It was from constantly switching between different problems.
One agent would finish a feature and need a review. Another would get stuck and need a decision. A third would finish its implementation, but I couldn't remember exactly what I had asked it to do or why we had chosen that approach. I'd switch to one worktree, read through the changes, reconstruct the context, give some feedback, and move on to the next. By the time I returned to the first task, I had to load its context into my head all over again.
AI agents made programming parallel. They didn't make my brain parallel.
The agents could write code independently, but someone still had to review it, understand the trade-offs, make architectural decisions, and figure out how all the changes fit together. That someone was me, and unlike the agents, I couldn't spin up another instance of myself.
The more tasks I started, the more unfinished context I had to carry: which agent was doing what, which decisions we'd already made, which changes were ready to merge, and which experiments were worth continuing. At some point, I wasn't really managing code anymore but a collection of partially completed thoughts, and that was mentally draining.
The funny thing is that every tool was doing exactly what it was supposed to do. Worktrees made it easy to have multiple branches checked out, uv kept me from duplicating huge Python environments, and the agents generated code in parallel. Everything worked except the person coordinating it all. I had started out thinking the main cost of worktrees was disk space. uv made that cost much smaller and agents gave me a reason to use them, but after months of use I found another cost in context switching, and that isn't something a better package manager can fix.
So, should you use Git worktrees?
I don't think worktrees are bad; quite the opposite. If you run multiple AI coding agents, they're an excellent way to isolate their work, and if you work with Python, uv makes separate environments far more practical than they were in my old workflow. I'd recommend looking into both.
I have, however, become more skeptical of the idea that running more agents automatically makes you more productive. There's a difference between how many tasks your tools can execute in parallel and how many you can meaningfully supervise. Worktrees remove one technical limitation and agents remove another, but neither removes the cost of keeping track of everything.
I avoided worktrees at first because I had no use case and didn't want to waste disk space. Then I started using them because the technical costs shrank and the benefits became obvious. After a couple of months, I found myself questioning how much parallelism I actually wanted. Maybe the goal shouldn't be to run as many agents as possible, but as many as you can comfortably understand, review, and manage. No matter how many worktrees I create, my brain is still single-threaded.