Imagine you are building a large feature and find yourself deep in the context of a problem when you receive a request to solve a minor bug.
While you know how to fix this bug instantly, this is still a major context switch as you have to
stash or temporarily commit your current changes
open a new branch
make the “minor” change
re-create the context
git-worktree aims to address this issue by allowing you to work on multiple branches in the same repository.
However, it only provides the ability to quickly switch a branch without losing context. To have a working project where you can test changes end to end, you need some additional things like
the environment variables, as the .env files are usually git ignored, they aren’t copied over in the git-worktree folder.
ability to run multiple versions of the codebase (if it is a web server for example)
A friend introduced me to Worktrunk the other day and I instantly loved it. Although, you can use git-worktree commands directly, worktrunk provides nicer APIs to achieve the same goals (copying .env files over, less typing).
It is a very thin wrapper, here is an example
$ wt switch -c feat1
# gets translate to
# git worktree add -b feat ../repo.feat1 && \
# cd ../repo.feat1See their docs for other examples but you get the idea.
This is very project specific so it would vary from project to project but usually my projects are powered by .env files. You can run a git hook using lefthook to generate an env file (by getting the relevant secrets from 1password or locally encrypted env files using dotenvx for example). In fact, this is what I was doing until a week or so ago before I ran into worktrunk.
Worktrunk has a concept of .worktreeinclude file which is a .gitignore style file for copying files over, here is what it looks like for a web project that I work on
$ cat .worktreeinclude
.env
.dev.varsJust this file is not enough, you need a way to run worktrunk hooks on each work tree, here is my config currently
$ cat .config/wt.toml
[post-create]
install = "pnpm install"
env = "wt step copy-ignored"So essentially, when a git-worktree is created via worktrunk, we install the dependencies and copy over the .env files (yes this works for nested files too). With this, you are already ready to work on the project and run a dev server (but what about port clashes?).
You are usually testing things end to end in a web server, I usually like to even run projects with https on a custom URL. To power this, I use caddy, here is what my Caddyfile usually looks.
$ cat Caddyfile
trackfootball.localhost {
reverse_proxy localhost:5173
}But when using multiple git-worktrees, I didn’t want to stop the dev server so port 5173 (vite default) gets free. I wanted to create a predictable pattern so the next available port is used (and is addressable with https).
Luckily, vite does half the job of running the dev server at 5174, if 5173 is not available but depending on your application, you might have to get a random port and run your application there.
For the other half, I use caddy, here is the modified Caddyfile
$ cat Caddyfile
:80 {
@default host trackfootball.localhost
reverse_proxy @default localhost:5173
@port header_regexp port Host ^trackfootball-([0-9]+)\.localhost
reverse_proxy @port localhost:{re.port.1}
}Essentially, this allows the following scheme to work
trackfootball.localhost - localhost:5173 # hardcoded, existing setup keeps on working
trackfootball-5173.localhost - localhost:5173 # port derived from url
trackfootball-5174.localhost - localhost:5174 # port derived from url
# and so on...You might have to change some other things in your application for this to work for you or change the URL scheme to suit your application needs.
For example, this setup doesn’t support ‘login’ locally for me because the cookie domain is set to trackfootball.localhost. But it doesn’t affect my local because of my local login setup (more on that later, I don’t really recommend that approach). I will likely change this to be 5173.trackfootball.localhost in the future so the local login + cookies keep on working without any change.
Is it 2026 without a post mentioning AI agents? While this post is 100% handwritten, you might have noticed by now that the above setup would make AI agents have access to isolated environment with git-worktree easily (depending on what all do you need to / want to isolate in your environment).
In fact, worktrunk supports running any binary command with a single command while creating the git-worktree. Here is how you would boot the amp in one go:
$ wt switch -c -x amp feat1I mentioned earlier in the post about why I am not bothered with local login auth cookie not working in my current setup, the reason is that I use an env var UNSAFE_AUTH_BYPASS_USER to completely bypass authentication locally (it is something I am experimenting with and don’t recommend it yet). In code it roughly looks like this:
ctx.user = null
if (
env.UNSAFE_AUTH_BYPASS_USER === '1' ||
env.UNSAFE_AUTH_BYPASS_USER === 'true'
) {
ctx.user = {
id: 1,
createdAt: new Date('2021-05-05T12:41:06.248Z'),
updatedAt: new Date('2025-06-14T19:07:43.754Z'),
email: 'john.doe@mail.com',
firstName: 'John',
lastName: 'Doe',
locale: 'en',
picture: 'https://i.pravatar.cc/150?u=jd@mail.com',
auth0Sub: 'google-oauth2|104619003144932489723',
emailVerified: true,
type: 'ADMIN',
} as User
return
}I did this to not having to deal with login flow and expiry locally (which I don’t recommend as it means that this code path will be less frequently hit). This also makes it easier for an AI agent to build features behind auth as it doesn’t have to struggle with authentication for each task.
This post described my current git-worktree setup, this hasn’t stood the test of time yet but I wanted to share it already to get feedback from the wider community.
Thanks for reading this and please share tips and your setup with me.