| ... | @@ -739,13 +739,13 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -739,13 +739,13 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 739 | Effect.gen(function* () { | 739 | Effect.gen(function* () { |
| 740 | const cwd = yield* makeTmpDir(); | 740 | const cwd = yield* makeTmpDir(); |
| 741 | const pathService = yield* Path.Path; | 741 | const pathService = yield* Path.Path; |
| 742 | const missingWorktree = pathService.join(cwd, "missing-worktree"); | 742 | const fileSystem = yield* FileSystem.FileSystem; |
| | 743 | const notAWorktree = pathService.join(cwd, "not-a-worktree"); |
| | 744 | yield* fileSystem.makeDirectory(notAWorktree); |
| 743 | const driver = yield* GitVcsDriver.GitVcsDriver; | 745 | const driver = yield* GitVcsDriver.GitVcsDriver; |
| 744 | yield* driver.initRepo({ cwd }); | 746 | yield* driver.initRepo({ cwd }); |
| 745 | | 747 | |
| 746 | const error = yield* driver | 748 | const error = yield* driver.removeWorktree({ cwd, path: notAWorktree }).pipe(Effect.flip); |
| 747 | .removeWorktree({ cwd, path: missingWorktree }) | | |
| 748 | .pipe(Effect.flip); | | |
| 749 | | 749 | |
| 750 | assert.deepInclude(error, { | 750 | assert.deepInclude(error, { |
| 751 | _tag: "GitCommandError", | 751 | _tag: "GitCommandError", |
| ... | @@ -755,9 +755,22 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -755,9 +755,22 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 755 | cwd, | 755 | cwd, |
| 756 | }); | 756 | }); |
| 757 | assert.notProperty(error, "cause"); | 757 | assert.notProperty(error, "cause"); |
| | 758 | assert.notProperty(error, "stderr"); |
| 758 | assert.notInclude(error.detail, "Git command failed in"); | 759 | assert.notInclude(error.detail, "Git command failed in"); |
| 759 | }), | 760 | }), |
| 760 | ); | 761 | ); |
| | 762 | |
| | 763 | it.effect("treats removing an already-gone worktree as a no-op", () => |
| | 764 | Effect.gen(function* () { |
| | 765 | const cwd = yield* makeTmpDir(); |
| | 766 | const pathService = yield* Path.Path; |
| | 767 | const missingWorktree = pathService.join(cwd, "missing-worktree"); |
| | 768 | const driver = yield* GitVcsDriver.GitVcsDriver; |
| | 769 | yield* driver.initRepo({ cwd }); |
| | 770 | |
| | 771 | yield* driver.removeWorktree({ cwd, path: missingWorktree }); |
| | 772 | }), |
| | 773 | ); |
| 761 | }); | 774 | }); |
| 762 | | 775 | |
| 763 | describe("review diff previews", () => { | 776 | describe("review diff previews", () => { |
| ... | @@ -1487,6 +1500,57 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -1487,6 +1500,57 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 1487 | assert.equal(yield* fileSystem.exists(worktreePath), false); | 1500 | assert.equal(yield* fileSystem.exists(worktreePath), false); |
| 1488 | }), | 1501 | }), |
| 1489 | ); | 1502 | ); |
| | 1503 | |
| | 1504 | it.effect("removes the same worktree path twice without failing", () => |
| | 1505 | Effect.gen(function* () { |
| | 1506 | const cwd = yield* makeTmpDir(); |
| | 1507 | const { initialBranch } = yield* initRepoWithCommit(cwd); |
| | 1508 | const pathService = yield* Path.Path; |
| | 1509 | const worktreePath = pathService.join(yield* makeTmpDir("git-worktrees-"), "shared"); |
| | 1510 | const driver = yield* GitVcsDriver.GitVcsDriver; |
| | 1511 | |
| | 1512 | yield* driver.createWorktree({ |
| | 1513 | cwd, |
| | 1514 | path: worktreePath, |
| | 1515 | refName: initialBranch, |
| | 1516 | newRefName: "feature/shared", |
| | 1517 | }); |
| | 1518 | |
| | 1519 | // Two threads can record the same worktree path; the second delete |
| | 1520 | // must be a no-op instead of exit 128. |
| | 1521 | yield* driver.removeWorktree({ cwd, path: worktreePath }); |
| | 1522 | yield* driver.removeWorktree({ cwd, path: worktreePath }); |
| | 1523 | }), |
| | 1524 | ); |
| | 1525 | |
| | 1526 | it.effect("prunes stale registrations when removing an already-gone worktree", () => |
| | 1527 | Effect.gen(function* () { |
| | 1528 | const cwd = yield* makeTmpDir(); |
| | 1529 | const { initialBranch } = yield* initRepoWithCommit(cwd); |
| | 1530 | const pathService = yield* Path.Path; |
| | 1531 | const fileSystem = yield* FileSystem.FileSystem; |
| | 1532 | const worktreesRoot = yield* makeTmpDir("git-worktrees-"); |
| | 1533 | const stalePath = pathService.join(worktreesRoot, "stale"); |
| | 1534 | const driver = yield* GitVcsDriver.GitVcsDriver; |
| | 1535 | |
| | 1536 | yield* driver.createWorktree({ |
| | 1537 | cwd, |
| | 1538 | path: stalePath, |
| | 1539 | refName: initialBranch, |
| | 1540 | newRefName: "feature/stale", |
| | 1541 | }); |
| | 1542 | // Delete the directory behind git's back so the registration goes stale. |
| | 1543 | yield* fileSystem.remove(stalePath, { recursive: true }); |
| | 1544 | |
| | 1545 | yield* driver.removeWorktree({ |
| | 1546 | cwd, |
| | 1547 | path: pathService.join(worktreesRoot, "never-registered"), |
| | 1548 | }); |
| | 1549 | |
| | 1550 | const registered = yield* git(cwd, ["worktree", "list", "--porcelain"]); |
| | 1551 | assert.notInclude(registered, "stale"); |
| | 1552 | }), |
| | 1553 | ); |
| 1490 | }); | 1554 | }); |
| 1491 | | 1555 | |
| 1492 | describe("remote operations", () => { | 1556 | describe("remote operations", () => { |