Worktrees should share the same project directory as the main repo

Status Open
Maintainer reply None cached
Activity 11 comments · opened Mar 14, 2026

Problem

When using git worktrees (either via claude --worktree or manually created), Claude Code creates a separate project directory in ~/.claude/projects/ for each worktree path. This splits conversation history, auto-memory, and project-level settings across multiple isolated directories — even though the worktrees are branches of the same repository.

For example, working in ~/project and then creating a worktree at ~/project-feature-branch produces:

~/.claude/projects/-home-user-project/           # main
~/.claude/projects/-home-user-project-feature-branch/  # worktree — separate conversations, memory, settings

This means:

  • Auto-memory is fragmented — memories saved in a worktree session are invisible to main repo sessions and vice versa
  • Conversation history is split — the /resume picker may show sessions across worktrees, but project-level context is lost
  • Project settings diverge — each worktree gets its own project config

Over time this creates significant knowledge fragmentation, especially for teams using worktrees for parallel development (e.g., running multiple Claude Code sessions in tmux panes on different branches).

Proposed Solution

Detect when a working directory is a git worktree and resolve the project directory to the main worktree's path instead of the current working directory path.

Git already provides this via:

git worktree list --porcelain  # lists all worktrees and identifies the main one
git rev-parse --git-common-dir  # resolves to the shared .git dir

All worktrees of the same repo would then share:

  • ~/.claude/projects/<main-repo-path>/memory/
  • ~/.claude/projects/<main-repo-path>/*.jsonl (conversations)
  • ~/.claude/projects/<main-repo-path>/ (project-level settings)

Alternative: opt-in configuration

If automatic detection is too opinionated, a configuration option would also work:

// ~/.claude/settings.json or project CLAUDE.md
{
  "projectDirectoryStrategy": "git-root"  // vs default "working-directory"
}

Current Workaround

Manually moving .jsonl files, UUID session directories, and memory files from worktree project directories into the main project directory, then deleting the empty source directories. This works but needs to be repeated after every worktree session.

Environment

  • Claude Code CLI
  • Linux (also applicable to macOS)

View original on GitHub ↗

11 Comments

github-actions[bot] · 5 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/15776
  2. https://github.com/anthropics/claude-code/issues/30667

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

epechuzal · 5 months ago

+1 — heavy worktree user here and this is a real pain point.

My specific case: I use team-provisioned claude.ai MCP servers (Gmail, Google Calendar, Slack, etc.) which also brings along ~10 MCPs I don't use (Figma, Miro, Notion, Pendo, Postman, etc.). I disable them via disabledMcpServers in the project entry, but every new worktree creates a fresh project entry with an empty disable list — so the unwanted MCPs come back every time.

Two compounding issues:

  1. Settings don't carry overdisabledMcpServers (and other per-project config) needs to be re-applied in every worktree session via /mcp
  2. Dead entries accumulate — nuked worktrees leave orphaned project entries in ~/.claude.json. I just cleaned out 22 dead entries out of 31 total. There's no built-in GC for these.

Resolving the project directory to the main worktree's path (via git rev-parse --git-common-dir) as proposed here would fix both.

jboulter11 · 5 months ago

This, alongside shared permissions, would be great. Just because I make a new worktree, I should not have to also now re-approve all the same stuff and lose my project memories.

nickrahman · 5 months ago

The docs actually say that it should already work this way, so this seems to be a bug.

https://code.claude.com/docs/en/memory#storage-location

Each project gets its own memory directory at ~/.claude/projects/<project>/memory/. The <project> path is derived from the git repository, so all worktrees and subdirectories within the same repo share one auto memory directory. Outside a git repo, the project root is used instead.
yurukusa · 5 months ago

/tmp/issue-34437-comment.md

cderv · 5 months ago

We encountered this while investigating why auto-memory wasn't shared across worktrees on Windows (v2.1.89). The memory docs state:

The <project> path is derived from the git repository, so all worktrees and subdirectories within the same repo share one auto memory directory.

In practice, each worktree gets its own ~/.claude/projects/<key>/ directory — including separate memory, session transcripts, and project settings.

The resulting project keys match what git rev-parse --show-toplevel returns for each worktree, which always gives the worktree's own root rather than the shared repo:

# Main repo
$ git rev-parse --show-toplevel   → /home/user/my-project
$ git rev-parse --git-common-dir  → .git

# Worktree (external sibling dir)
$ git rev-parse --show-toplevel   → /home/user/my-project-feature
$ git rev-parse --git-common-dir  → /home/user/my-project/.git

# Worktree (internal)
$ git rev-parse --show-toplevel   → /home/user/my-project/.worktrees/fix-bug
$ git rev-parse --git-common-dir  → /home/user/my-project/.git

Using --git-common-dir to derive the parent repo path would let all worktrees resolve to the same project key.

Note: on macOS, #39920 reports the opposite for memory — it resolves to the main repo via --git-common-dir, but sessions go to the worktree path. So the behavior appears platform-inconsistent.

Partial workaround for auto-memory only: autoMemoryDirectory in .claude/settings.local.json (per the docs, this setting is not accepted from project-scoped .claude/settings.json):

{
  "autoMemoryDirectory": "~/.claude/projects/<main-project-key>/memory"
}

We automated this with a SessionStart hook that detects worktrees via --git-common-dir and injects the setting if not already present. This only fixes memory sharing — session transcripts and other project data still land in separate directories per worktree.

See also #41283 for a broader discussion of memory identity being tied to filesystem paths.

DesiredPathConsulting · 4 months ago

Another data point — Windows 11, Claude Code v2.1.112, VS Code extension. We hit exactly what @cderv describes in a small internal analytics tool. Worktrees live under .claude/worktrees/<slug> off origin/main (the same path convention claude --worktree uses), and each worktree's CWD ends up with its own ~/.claude/projects/<slug>/ containing isolated memory and .jsonl transcripts.

One Windows-specific detail worth recording here, since the slug rule isn't documented anywhere: on Windows, Claude Code's project-dir slug replaces :, \, /, and . with -, then lowercases the drive letter. The . replacement is the subtle one — a path like ...\.claude\worktrees\feat-foo slugs to ...--claude-worktrees-feat-foo (two dashes at the .claude boundary, not -.claude). We initially shipped a helper that preserved . and had to patch it after observing Claude Code produce a different directory.

Memory — @cderv's autoMemoryDirectory approach is the right shape. We shipped a copy-based SessionStart hook in parallel before seeing that comment; we're migrating to setting-injection. It's live (writes propagate both ways), doesn't require a target directory to pre-exist, and sidesteps most of the slug-rule fragility.

Session transcripts — still a gap. Even with autoMemoryDirectory, a fresh worktree session starts with an empty /resume list. We wrote a helper (scripts/new-worktree.sh, link below) that copies the most-recent .jsonl from the main repo's project dir into the new worktree's project dir at creation time. This covers the specific workflow of "spin up a worktree to pick up where I left off on a different branch." Caveat: tool results in the copied transcript reference paths from the original checkout, which still resolve on disk since the main checkout exists as a sibling, but mid-resume edits target the main checkout rather than the worktree. Documented in our own docs.

Asks, in priority order:

  1. Fix the root cause via the --git-common-dir resolution path @cderv and others have outlined. That closes every shape of this issue (memory, transcripts, permissions, project settings, orphaned worktree dirs) in one change.
  2. Document the slug rule publicly if the root-cause fix is further out. Every userland workaround is reinventing it, and the . edge case isn't obvious.
  3. Expose $CLAUDE_SESSION_ID to hook environments. The current "most-recent .jsonl by mtime" heuristic breaks down with multiple parallel sessions; an explicit session ID at SessionStart would make transcript-copy deterministic.

Full scripts + docs (helper script, SessionStart hook, docs/worktrees.md): https://gist.github.com/DesiredPathConsulting/c9a8ace20a9b619168bc6e3617416601

sepsol · 3 months ago

I wanted to propose a workaround described here. Would like to know your thoughts and/or experience on this.

egolearner · 2 months ago

Workaround: hub directory + --add-dir

Create an empty "hub" directory and always launch Claude Code from there, using --add-dir to point to the actual worktree:

mkdir -p ~/project-hub

# Start a session targeting a specific worktree
cd ~/project-hub && claude --add-dir ~/project/worktree-a --name "my-session"

# Resume later, even switching to a different worktree
cd ~/project-hub && claude --resume "my-session" --add-dir ~/project/worktree-b

All sessions are stored under the hub directory, so they can be shared across worktrees.

Limitations:

  • --add-dir is not persisted — you must re-specify it on every launch/resume.
  • The hub directory has no git context (no branch info, no git status in system prompt), so Claude has to cd into the worktree for git operations.
  • If you accidentally --add-dir multiple worktrees of the same repo simultaneously, Claude may read the wrong files since they share identical relative paths.
MichaelPGifford · 28 days ago

It really sucks that you're working really hard and you've planned every step perfectly. You want to optimize your time by working in parallel, so you spin up 5 or 6 different work trees's to tackle your well planned issues without collision. Then VSCode crashes... All of your claude code sessions get scattered into the 5 or 6 worktrees you created and you have to spend time recovering it. The problem is especially difficult for new users who say, "where has all my work gone"

BasedGPT · 27 days ago

The session files in ~/.claude/projects/ are being keyed from each worktree path, so the same repository ends up with two project slugs. That splits conversation history and project memory even when nothing has been deleted.

I built BasedGPT/claude-code-session-recovery for this path-split family. From the affected Linux machine, run python tools/diagnose.py first and keep the output. Treat it as the starting point with Linux path adjustments, and do not apply a write-bearing command blindly. The exact command it prints will tell you whether the project slugs contain separate transcript files and which repair path is safe.

Do not merge or delete the source directories yet. The repoint_session_to_jsonl.py path is the relevant session-recovery boundary because it repoints metadata to the slug containing each transcript file rather than moving the .jsonl files. The shared-memory and project-settings behaviour still needs an upstream fix.

As a quick check, list the two ~/.claude/projects/ directories and compare their session IDs before changing anything. If both contain different files, preserve both and work from that inventory.

Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)