Просмотр исходного кода

fix(opencode): lost sessions across worktrees and orphan branches (#16389)

Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Michael Dwan 5 месяцев назад
Родитель
Сommit
4b9b86b544

+ 3 - 2
packages/opencode/src/project/project.ts

@@ -147,7 +147,7 @@ export namespace Project {
 
         // generate id from root commit
         if (!id) {
-          const roots = await git(["rev-list", "--max-parents=0", "--all"], {
+          const roots = await git(["rev-list", "--max-parents=0", "HEAD"], {
             cwd: sandbox,
           })
             .then(async (result) =>
@@ -170,7 +170,8 @@ export namespace Project {
 
           id = roots[0] ? ProjectID.make(roots[0]) : undefined
           if (id) {
-            await Filesystem.write(path.join(dotgit, "opencode"), id).catch(() => undefined)
+            // Write to common dir so the cache is shared across worktrees.
+            await Filesystem.write(path.join(worktree, ".git", "opencode"), id).catch(() => undefined)
           }
         }
 

+ 47 - 1
packages/opencode/test/project/project.test.ts

@@ -23,7 +23,7 @@ mock.module("../../src/util/git", () => ({
       mode === "rev-list-fail" &&
       cmd.includes("git rev-list") &&
       cmd.includes("--max-parents=0") &&
-      cmd.includes("--all")
+      cmd.includes("HEAD")
     ) {
       return Promise.resolve({
         exitCode: 128,
@@ -172,6 +172,52 @@ describe("Project.fromDirectory with worktrees", () => {
     }
   })
 
+  test("worktree should share project ID with main repo", async () => {
+    const p = await loadProject()
+    await using tmp = await tmpdir({ git: true })
+
+    const { project: main } = await p.fromDirectory(tmp.path)
+
+    const worktreePath = path.join(tmp.path, "..", path.basename(tmp.path) + "-wt-shared")
+    try {
+      await $`git worktree add ${worktreePath} -b shared-${Date.now()}`.cwd(tmp.path).quiet()
+
+      const { project: wt } = await p.fromDirectory(worktreePath)
+
+      expect(wt.id).toBe(main.id)
+
+      // Cache should live in the common .git dir, not the worktree's .git file
+      const cache = path.join(tmp.path, ".git", "opencode")
+      const exists = await Filesystem.exists(cache)
+      expect(exists).toBe(true)
+    } finally {
+      await $`git worktree remove ${worktreePath}`
+        .cwd(tmp.path)
+        .quiet()
+        .catch(() => {})
+    }
+  })
+
+  test("separate clones of the same repo should share project ID", async () => {
+    const p = await loadProject()
+    await using tmp = await tmpdir({ git: true })
+
+    // Create a bare remote, push, then clone into a second directory
+    const bare = tmp.path + "-bare"
+    const clone = tmp.path + "-clone"
+    try {
+      await $`git clone --bare ${tmp.path} ${bare}`.quiet()
+      await $`git clone ${bare} ${clone}`.quiet()
+
+      const { project: a } = await p.fromDirectory(tmp.path)
+      const { project: b } = await p.fromDirectory(clone)
+
+      expect(b.id).toBe(a.id)
+    } finally {
+      await $`rm -rf ${bare} ${clone}`.quiet().nothrow()
+    }
+  })
+
   test("should accumulate multiple worktrees in sandboxes", async () => {
     const p = await loadProject()
     await using tmp = await tmpdir({ git: true })