You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Worktrees resolve to different project IDs due to per-worktree cache lookup #16995
When OpenCode is opened from a git worktree, it looks for the .git/opencode cache file in the per-worktree git directory (.git/worktrees/<name>/opencode) rather than the common git directory (.git/opencode). This causes each worktree to compute a potentially different project ID, resulting in separate project records and empty session lists.
Steps to Reproduce
Have a repo with a cached project ID in .git/opencode
Create a git worktree: git worktree add ../my-worktree feature-branch
Open OpenCode from the worktree directory
Open the session list — it shows "No results found"
Root Cause
In src/project/project.ts, the fromDirectory() function resolves the .git path and looks for an opencode file there. For worktrees, .git is a file (not a directory) pointing to .git/worktrees/<name>/, so the cache lookup goes to .git/worktrees/<name>/opencode instead of the main .git/opencode.
Since the per-worktree cache doesn't exist, OpenCode falls back to git rev-list --max-parents=0 --all. This command can return different results depending on stash state (e.g., git stash -u creates orphan root commits), leading to a different project ID being computed and cached per-worktree.
Expected Behavior
All worktrees of the same repository should resolve to the same project ID and share sessions. The cache file should be read from and written to the common git directory (obtainable via git rev-parse --git-common-dir), not the per-worktree git directory.
Workaround
Manually create the cache file in each worktree's git directory:
In fromDirectory(), use git rev-parse --git-common-dir (which is already being called later in the function) to determine where to read/write the opencode cache file, instead of using the dotgit path which varies per worktree.
This issue might be a duplicate of existing issues. Please check:
Git worktrees + stash can cause projectID mismatch (sessions 404 across sandboxes) #12726: Describes the exact same root cause — worktree .git file causes per-worktree cache lookup, fallback git rev-list --max-parents=0 --all can return different root commits (e.g. due to stash), leading to mismatched project IDs and missing sessions. The proposed fix (use git rev-parse --git-common-dir) is identical.
After further investigation, the initial analysis was incomplete. The cache file approach (<gitdir>/opencode) cannot work in worktrees at all, regardless of where the file is placed.
Filesystem.up finds the .git entry by checking exists(). In a worktree, .git is a file (not a directory), containing gitdir: /path/to/.git/worktrees/<name>.
path.join("/path/to/worktree/.git", "opencode") produces /path/to/worktree/.git/opencode — but .git is a file, so this path can never exist. The read always fails silently, and OpenCode falls back to git rev-list --max-parents=0 --all.
So the cache is never read or written in worktrees. Every time OpenCode launches from a worktree, it recomputes the project ID from scratch.
Why it produces wrong IDs
git stash -u (stash with untracked files) creates orphan commits (zero parents) to store untracked file trees. git rev-list --max-parents=0 --all picks these up. Since the results are sorted lexicographically and the first entry is used as the project ID, any stash orphan that sorts before the real initial commit produces a different project ID.
With logic that resolves to the common git directory (which is always a real directory):
constdotgit=awaitmatches.next().then((x)=>x.value);letsandbox=path.dirname(dotgit);// Resolve the actual git directory (handles worktrees where .git is a file)constcommonDir=awaitgit(["rev-parse","--git-common-dir"],{cwd: sandbox}).then(async(result)=>gitpath(sandbox,awaitresult.text())).catch(()=>dotgit);letid=awaitFilesystem.readText(path.join(commonDir,"opencode"))
And similarly for the write-back.
Workaround
A shell function wrapper that detects worktrees and runs opencode from the main repo root:
The previous workaround (redirecting to main repo root) has a critical flaw: OpenCode operates in the main repo directory instead of the worktree, so file edits and git operations happen in the wrong place.
Better solution: Convert the worktree's .git file to a symlink pointing to the git worktree metadata directory (e.g., .git/worktrees/<name>/). This makes path.join(".git", "opencode") resolve through the symlink, while git operations continue to work correctly.
Confirmed the same root cause in a worktree sandbox scenario. After a daemon restart, the project ID changed because the git remote URL was modified (fork). The new project ID caused fromDirectory() to create a separate project row and snapshot, splitting sessions across two projects. The original session was migrated via migrateProjectId, but existing sessions with the same title were forked (fork #1) under the new project.
The current git.find() already uses --git-common-dir correctly for cache lookup — the cache itself is sound. The remaining gap is that fromDirectory() has no sandbox‑based project fallback: when the resolved ID differs from an existing project whose sandboxes already contain the directory, it still creates a new project.
Opened a draft PR with a fix: if the directory is already a sandbox of another project, reuse its ID instead of creating a new one. This prevents the duplicate project and the spurious fork session.
Activity
github-actions commentedon Mar 11, 2026
This issue might be a duplicate of existing issues. Please check:
.gitfile causes per-worktree cache lookup, fallbackgit rev-list --max-parents=0 --allcan return different root commits (e.g. due to stash), leading to mismatched project IDs and missing sessions. The proposed fix (usegit rev-parse --git-common-dir) is identical.afkent27 commentedon Mar 11, 2026
Additional Findings
After further investigation, the initial analysis was incomplete. The cache file approach (
<gitdir>/opencode) cannot work in worktrees at all, regardless of where the file is placed.Root Cause (Corrected)
In
fromDirectory(), the code does:Filesystem.upfinds the.gitentry by checkingexists(). In a worktree,.gitis a file (not a directory), containinggitdir: /path/to/.git/worktrees/<name>.path.join("/path/to/worktree/.git", "opencode")produces/path/to/worktree/.git/opencode— but.gitis a file, so this path can never exist. The read always fails silently, and OpenCode falls back togit rev-list --max-parents=0 --all.Similarly, the write-back also fails silently:
So the cache is never read or written in worktrees. Every time OpenCode launches from a worktree, it recomputes the project ID from scratch.
Why it produces wrong IDs
git stash -u(stash with untracked files) creates orphan commits (zero parents) to store untracked file trees.git rev-list --max-parents=0 --allpicks these up. Since the results are sorted lexicographically and the first entry is used as the project ID, any stash orphan that sorts before the real initial commit produces a different project ID.Suggested Fix
Replace:
With logic that resolves to the common git directory (which is always a real directory):
And similarly for the write-back.
Workaround
A shell function wrapper that detects worktrees and runs opencode from the main repo root:
This passes the main repo root as the
[project]positional argument so OpenCode finds the real.gitdirectory (not the worktree.gitfile).natewill commentedon Mar 11, 2026
#16684 fixes this
afkent27 commentedon Mar 11, 2026
Better Workaround: Symlink Approach
The previous workaround (redirecting to main repo root) has a critical flaw: OpenCode operates in the main repo directory instead of the worktree, so file edits and git operations happen in the wrong place.
Better solution: Convert the worktree's
.gitfile to a symlink pointing to the git worktree metadata directory (e.g.,.git/worktrees/<name>/). This makespath.join(".git", "opencode")resolve through the symlink, while git operations continue to work correctly.Shell wrapper that auto-converts on
opencodelaunch:Tested: git status, branch, rev-parse, commit, push all work correctly through the symlink.
natewill commentedon Mar 11, 2026
All you have to do is call project.current with the directory name which calls fromDirectiory and resolves the project correctly
bashrusakh commentedon Jun 24, 2026
Confirmed the same root cause in a worktree sandbox scenario. After a daemon restart, the project ID changed because the git remote URL was modified (fork). The new project ID caused
fromDirectory()to create a separate project row and snapshot, splitting sessions across two projects. The original session was migrated viamigrateProjectId, but existing sessions with the same title were forked (fork #1) under the new project.The current
git.find()already uses--git-common-dircorrectly for cache lookup — the cache itself is sound. The remaining gap is thatfromDirectory()has no sandbox‑based project fallback: when the resolved ID differs from an existing project whose sandboxes already contain the directory, it still creates a new project.Opened a draft PR with a fix: if the directory is already a sandbox of another project, reuse its ID instead of creating a new one. This prevents the duplicate project and the spurious fork session.
github-actions commentedon Aug 24, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.