Worktrees should share the same project directory as the main repo
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
/resumepicker 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)
11 Comments
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
+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
disabledMcpServersin 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:
disabledMcpServers(and other per-project config) needs to be re-applied in every worktree session via/mcp~/.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.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.
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
/tmp/issue-34437-comment.md
We encountered this while investigating why auto-memory wasn't shared across worktrees on Windows (v2.1.89). The memory docs state:
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-toplevelreturns for each worktree, which always gives the worktree's own root rather than the shared repo:Using
--git-common-dirto 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:
autoMemoryDirectoryin.claude/settings.local.json(per the docs, this setting is not accepted from project-scoped.claude/settings.json):We automated this with a
SessionStarthook that detects worktrees via--git-common-dirand 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.
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>offorigin/main(the same path conventionclaude --worktreeuses), and each worktree's CWD ends up with its own~/.claude/projects/<slug>/containing isolated memory and.jsonltranscripts.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-fooslugs to...--claude-worktrees-feat-foo(two dashes at the.claudeboundary, 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
autoMemoryDirectoryapproach 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/resumelist. We wrote a helper (scripts/new-worktree.sh, link below) that copies the most-recent.jsonlfrom 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:
--git-common-dirresolution path @cderv and others have outlined. That closes every shape of this issue (memory, transcripts, permissions, project settings, orphaned worktree dirs) in one change..edge case isn't obvious.$CLAUDE_SESSION_IDto hook environments. The current "most-recent.jsonlby 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/c9a8ace20a9b619168bc6e3617416601I wanted to propose a workaround described here. Would like to know your thoughts and/or experience on this.
Workaround: hub directory +
--add-dirCreate an empty "hub" directory and always launch Claude Code from there, using
--add-dirto point to the actual worktree:All sessions are stored under the hub directory, so they can be shared across worktrees.
Limitations:
--add-diris not persisted — you must re-specify it on every launch/resume.cdinto the worktree for git operations.--add-dirmultiple worktrees of the same repo simultaneously, Claude may read the wrong files since they share identical relative paths.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"
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-recoveryfor this path-split family. From the affected Linux machine, runpython tools/diagnose.pyfirst 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.pypath is the relevant session-recovery boundary because it repoints metadata to the slug containing each transcript file rather than moving the.jsonlfiles. 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 ⭐ :)