Writing August 29, 2026 4 min read

Your Worktree Is Three Days Old

Claude Code’s --worktree flag (-w for friends) is one of my favorite features: it spins up a fresh git worktree for the session, so parallel tasks don’t trample each other and my actual checkout stays clean. I used it happily until a PR came back with merge conflicts against code that had been on main for days. The branch Claude was working on was cut from a stale main, and nothing warned either of us.

Where the branch actually comes from

By default, --worktree branches from origin/HEAD — the remote default branch. That’s the right choice, but only if origin/HEAD is current, and the worktrees documentation is refreshingly honest about when it is:

When the repository hasn’t been fetched in the last 24 hours, it fetches the default branch, capped at five seconds, and uses the locally cached ref if the fetch fails.

So there are two ways to start from yesterday’s main: the repo was fetched within the last 24 hours (perhaps by another worktree session this morning), or the fetch hit the five-second cap and quietly fell back to whatever was cached. On my hobby repos this is fine. On a work repo with double-digit merges a day, it means the freshness of your base commit depends on timing and network luck, and (while most of the time it shouldn’t take more than 5 seconds) we may very well get unlucky eventually.

I kinda understand why the CLI does this, kinda don’t. Maybe people are really that impatient, but I’d rather wait, or fail, than not know.

The solution

I already wrap claude in a shell function, because at work it needs CLAUDE_CONFIG_DIR pointed at a separate profile with the corporate proxy settings to avoid spending my precious personal tokens on work. Fetching first fits in the same place:

claude() {
  if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git remote get-url origin >/dev/null 2>&1; then
    git fetch origin || { echo "claude(): git fetch failed, aborting" >&2; return 1; }
  fi
  CLAUDE_CONFIG_DIR="$HOME/.claude-work" command claude "$@"
}

Now every session starts with an explicit fetch, and if the fetch fails, the whole thing stops with an error instead of silently building on old code. No surprises.

A few details that took more iterations than I’d like to admit:

command claude, not claude. The function has the same name as the binary, which is the point — muscle memory keeps working. It also means that calling claude inside the function calls the function, forever. command claude skips function lookup and runs the actual binary.

return 1, not exit 1. A shell function runs in your interactive shell, not a subshell. exit 1 doesn’t abort the function; it closes your terminal. Ask me how I know.

The guard clause. The fetch only runs inside a git work tree that actually has an origin remote. Turns out, not everyone is a hoarder like me. People actually have local Git repos for small tasks that are not backed up to Github. Crazy! Without the guard, running claude in a random directory or in a local-only repo would die on a fetch that makes no sense there. The wrapper should make the worktree case safer, not break every other case.

origin is hardcoded. Every repo I care about calls its default remote origin, so I kept it simple. If yours don’t, you should see a doctor.

Back in the day software errors were loud. It just screamed in your face, instead of surprising you later. And God saw that it was good.