Skip to content

Same git remote URL produces identical project_id for different local clones → only one project visible in desktop sidebar #42315

Closed

Description

@gangbanbro

Description

Title (EN)

Same git remote URL produces identical project_id for different local clones → only one project visible in desktop sidebar

标题 (中文)

相同的 git remote URL 导致不同本地 clone 生成相同的 project_id,桌面端侧边栏只能看到一个项目


English version

Description

When two local directories are clones of the same git remote (but on different branches / versions), opencode assigns them the same project_id. As a result, the desktop client aggregates them into a single entry in the sidebar project list, and only the first-added project is usable. The second directory cannot be added/switched to as a separate project.

Environment

  • opencode desktop: 1.18.18 (Windows)
  • OS: Windows

Reproduction steps

  1. Clone the same repo into two local directories, e.g.:
    • /path/to/repo-branchA (branch A)
    • /path/to/repo-branchB (branch B)
  2. Both have the same remote origin URL:
    origin  https://git.example.com/group/repo.git
    
  3. Add both directories as projects in the desktop client.
  4. Observe the sidebar: only one project entry is shown.

Expected behavior

Both directories should be listed as separate projects (or at least be switchable), since they are different working directories with different branches.

Actual behavior

Both directories resolve to the same project_id:

<sha1 hash>

which is sha1("git-remote:git.example.com/group/repo").

The desktop sidebar deduplicates by project_id, so only the first-added project is visible.

Root cause (from source inspection)

In Project.resolve (node-4IhTfWZ9.js around line 85716), the project_id is resolved with this priority:

  1. sha1("git-remote:" + normalizedRemoteUrl) ← always wins when a remote is set
  2. cached value in .git/opencode
  3. root commit hash (git rev-list --max-parents=0 HEAD)

Since both clones share the same remote URL, step 1 produces an identical id, and the fallbacks are never reached. Even the root-commit fallback would be identical here because both clones share the same root commit.

The project_directory table already supports a many-to-one mapping (PK is (project_id, directory)), so the data layer can hold multiple directories per project. The limitation is in the UI, which aggregates by project_id only.

Why this matters

For developers who maintain multiple versions/branches of the same repo side-by-side (e.g. a maintenance branch + a development branch), this makes it impossible to manage them as separate projects in the desktop client.

Suggested improvements (any of)

  1. UI: Show distinct entries per directory even when they share a project_id (the project_directory table already supports this).
  2. ID resolution: Incorporate the local working directory (or branch) into the project_id as a tiebreaker when remote URLs collide, e.g. sha1("git-remote:" + remote + ":" + worktreePath) or sha1(remote + "@" + branch).
  3. Workspace: Allow registering an existing independent clone directory as a workspace target (currently workspaces only support git worktree-linked directories).

中文版

问题描述

当两个本地目录是同一个 git remote 的 clone(但分支/版本不同)时,opencode 会给它们分配相同的 project_id。这导致桌面端在侧边栏的项目列表里把两者聚合成一条,只能看到最先添加的那个项目,第二个目录无法作为独立项目添加/切换使用。

环境信息

  • opencode 桌面端:1.18.18(Windows)
  • 操作系统:Windows

复现步骤

  1. 把同一个仓库 clone 到两个本地目录,例如:
    • /path/to/repo-branchA(分支 A)
    • /path/to/repo-branchB(分支 B)
  2. 两者的 remote origin URL 相同:
    origin  https://git.example.com/group/repo.git
    
  3. 在桌面端把这两个目录都添加为项目。
  4. 观察侧边栏:只显示了一个项目条目。

期望行为

两个目录应作为独立项目展示(或至少可切换),因为它们是不同的工作目录、不同分支。

实际行为

两个目录解析出了相同的 project_id:

<sha1 hash>

即 sha1("git-remote:git.example.com/group/repo")。

桌面端侧边栏按 project_id 去重,因此只显示最先添加的项目。

根因(源码分析)

在 Project.resolve(node-4IhTfWZ9.js 约 85716 行)中,project_id 按以下优先级解析:

  1. sha1("git-remote:" + 规范化后的 remote URL) ← 只要设置了 remote 就一定命中
  2. .git/opencode 缓存文件
  3. root commit hash(git rev-list --max-parents=0 HEAD)

由于两个 clone 的 remote URL 相同,第 1 步就产生相同 id,fallback 永远不会触发。即便走到 fallback,root commit 也相同(同源仓库),结果仍会一致。

project_directory 表本身已支持多目录映射(主键是 (project_id, directory) 联合主键),数据层可以容纳同一 project_id 下的多个目录。限制在 UI 层——只按 project_id 聚合展示。

为什么这很重要

对于需要并行维护同一仓库多个版本/分支的开发者(例如同时维护某个维护分支和开发另一个分支),这导致无法在桌面端把它们作为独立项目来管理。

建议改进(任选其一)

  1. UI:即使 project_id 相同,也按 directory 展示不同条目(project_directory 表已支持)。
  2. ID 解析:当 remote URL 冲突时,把本地工作目录(或分支)纳入 project_id 计算,例如 sha1("git-remote:" + remote + ":" + worktreePath) 或 sha1(remote + "@" + branch)。
  3. Workspace:允许把已有的独立 clone 目录注册为 workspace 目标(目前 workspace 只支持 git worktree 关联的目录)。

Plugins

No response

OpenCode version

No response

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. github-actions commented on Aug 13, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Please review those issues and consider whether your report adds new information before continuing.

  2. benizzio-opencode commented on Aug 18, 2026

    @benizzio-opencode

    Confirmed on OpenCode Desktop 1.18.18 on Linux.

    We had two clones at different paths with the same basename and the same origin URL. After closing clone A and selecting clone B, Desktop reopened clone A. The local database showed exactly the collision described here:

    project.worktree = /path/A/repo
    project.sandboxes = ["/path/B/repo"]
    project_directory = /path/A/repo (main), /path/B/repo
    

    Both directories resolved to the same project ID. Closing the project in the Desktop UI only removed sidebar state; it did not remove or replace the canonical project row, so selecting B was normalized back to A.

    Our tested workaround was intentionally destructive because we no longer needed A or its OpenCode session history:

    1. Fully quit Desktop.
    2. Back up ~/.local/share/opencode/opencode.db with SQLite's .backup, plus ~/.config/ai.opencode.desktop/opencode.global.dat, the active opencode.window.*.dat, and A's hashed opencode.workspace.*.dat.
    3. Enable foreign keys and delete the shared project row from opencode.db. This cascades to its sessions, messages, permissions, and both project_directory mappings.
    4. Remove A from the nested server and globalSync.project state in opencode.global.dat, remove A's notification/layout/session cache entries, remove its window tab entry, and remove A's hashed workspace state file.
    5. Keep B as server.projects.local[0] and lastProject.local, then restart Desktop.

    On restart, OpenCode recreated the same Git-derived project ID with:

    project.worktree = /path/B/repo
    project.sandboxes = []
    

    There were no remaining A directory/session/cache references, and Desktop opened B normally.

    Important: deleting the shared project row removes session history for that project ID, so this workaround is only appropriate when the old history can be discarded. Editing opencode.global.dat alone was not enough because the stale canonical path also lived in opencode.db.

  3. Hona commented on Sep 2, 2026

    @Hona
    Member

    Fixed for opencode2 launch via #46713
    You can try the desktop beta here: https://github.com/anomalyco/opencode-beta

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions