All posts

Per-Worktree Environment Variables with Git Hooks

The problem

I use Git worktrees a lot. If you don’t know them, they let you check out multiple branches at the same time into separate folders. Really useful when you want to work on two things without constantly switching branches.

The annoying part is the .env file. It’s gitignored so it doesn’t come with the worktree when you create one. Every time I’d spin up a new worktree I had to manually copy it over.

So I wrote a small Git hook to do it automatically.

My setup

I work with a bare Git repo. All worktrees live directly inside it as siblings:

my-project.git/
├── master/         # main worktree, .env lives here
├── feature-branch/ # feature worktree
├── bugfix-branch/  # feature worktree
├── hooks/
├── objects/
└── refs/

Creating a new worktree looks like this:

git worktree add feature-branch -b feature-branch master
  • feature-branch - the folder name that gets created inside the bare repo
  • -b feature-branch - the new branch name
  • master - which branch to base it on

What is a post-checkout hook

Git hooks are shell scripts that run at certain points. The post-checkout hook runs every time you do a checkout, and also when you create a new worktree.

Git passes it three arguments. The first one is the previous HEAD hash. When you create a brand new worktree there’s no previous commit, so Git sends 40 zeros instead. That’s how we know a worktree was just created.

The script

#!/bin/zsh
if [[ "$1" == "0000000000000000000000000000000000000000" ]]; then
  MAIN_WORKTREE_NAME="master"
  ENV_FILE=".env"
  SOURCE="$(dirname "$(pwd)")/$MAIN_WORKTREE_NAME/$ENV_FILE"

  if [[ -f "$SOURCE" ]]; then
      cp "$SOURCE" "$(pwd)/$ENV_FILE"
      echo "✓ Copied $ENV_FILE from main worktree"
  else
      echo "⚠ No $ENV_FILE found at $SOURCE"
  fi
fi
hooks/post-checkout

A few things worth explaining here.

The if check at the top detects that a new worktree was just created. Git passes 40 zeros as the previous HEAD hash when there is no previous commit.

MAIN_WORKTREE_NAME is the name of your main worktree folder.

ENV_FILE is the name of the env file you want to copy.

dirname $(pwd) gives you the bare repo root by stripping the last path component from the current directory. Since $(pwd) is the new worktree path, going one level up lands you at the bare repo root. From there /$MAIN_WORKTREE_NAME/$ENV_FILE points directly to the source file.

How to set it up

In a bare repo the hook goes directly in hooks/, not .git/hooks/:

vim hooks/post-checkout

Paste the script, then make it executable:

chmod +x hooks/post-checkout

Make sure you run this from the bare repo root, not from inside a worktree. The worktree path is relative to where you run the command, and the script relies on that to find the .env in master.

git worktree add test-branch -b test-branch master

If it worked you should see something like this:

$ git worktree add test-branch -b test-branch master
Preparing worktree (new branch 'test-branch')
HEAD is now at abc1234 your last commit message
 Copied .env from main worktree

That’s basically it

Small script, saves a bit of annoyance every time. If your setup is more complex you can extend the hook to copy multiple files or handle different cases. But this covers the common case.