From df09d69e7ce2ec37c33acc044fa76f8704fb5f01 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Tue, 4 Aug 2026 01:44:06 -0700 Subject: [PATCH] fix(worktrees): keep WSL trash on its owning host --- src/main/git/remove-worktree.test.ts | 18 ++++++++++++++++++ src/main/git/worktree.ts | 3 ++- src/main/worktree-trash.test.ts | 9 ++++++++- src/main/worktree-trash.ts | 5 ++++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/main/git/remove-worktree.test.ts b/src/main/git/remove-worktree.test.ts index fd8b476ff87..15e115221ea 100644 --- a/src/main/git/remove-worktree.test.ts +++ b/src/main/git/remove-worktree.test.ts @@ -489,6 +489,24 @@ branch refs/heads/main expect(getGitCalls()).toContain('git worktree remove /repo-feature') }) + it('does not rename a WSL checkout configured for a native Windows repo', async () => { + const originalPlatform = process.platform + const worktreePath = '\\\\wsl.localhost\\Ubuntu\\home\\dev\\feature' + Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' }) + try { + mockGitCommands({}) + + await removeWorktree('C:\\repo', worktreePath, false, { + knownRemovedWorktree: { branch: '', head: '', locked: false } + }) + + expect(moveWorktreeDirectoryToTrashMock).not.toHaveBeenCalled() + expect(getGitCalls()).toEqual([`git worktree remove ${worktreePath}`]) + } finally { + Object.defineProperty(process, 'platform', { configurable: true, value: originalPlatform }) + } + }) + it('passes one --force before the worktree path for dirty-file removal', async () => { mockGitCommands({ 'git worktree list --porcelain': { diff --git a/src/main/git/worktree.ts b/src/main/git/worktree.ts index e532962309a..064eb0887fd 100644 --- a/src/main/git/worktree.ts +++ b/src/main/git/worktree.ts @@ -13,6 +13,7 @@ import { restoreWorktreeDirectoryFromTrash, scheduleWorktreeTrashDeletion } from '../worktree-trash' +import { parseWslPath } from '../wsl' import type { GitWorktreeInfo, LocalBaseRefRefreshResult, @@ -1187,7 +1188,7 @@ async function tryRemoveWorktreeWithDeferredDirectoryDeletion( options: RemoveWorktreeOptions ): Promise { // Why: WSL-owned checkouts are deleted inside the distro, so Node on Windows must not rename them. - if (options.wslDistro) { + if (options.wslDistro || parseWslPath(worktreePath)) { return false } if (!force) { diff --git a/src/main/worktree-trash.test.ts b/src/main/worktree-trash.test.ts index 302bb56d0c5..49a8af75a1d 100644 --- a/src/main/worktree-trash.test.ts +++ b/src/main/worktree-trash.test.ts @@ -202,7 +202,14 @@ describe('collectWorktreeTrashSweepRoots', () => { try { expect( collectWorktreeTrashSweepRoots( - [repo({ path: '\\\\wsl.localhost\\Ubuntu\\home\\dev\\orca' })], + [ + repo({ path: '\\\\wsl.localhost\\Ubuntu\\home\\dev\\orca' }), + repo({ + id: 'repo-2', + path: 'C:\\code\\orca', + worktreeBasePath: '\\\\wsl.localhost\\Ubuntu\\home\\dev\\trees' + }) + ], settings ) ).toEqual([]) diff --git a/src/main/worktree-trash.ts b/src/main/worktree-trash.ts index 34cc29aef16..5c0aa32be2b 100644 --- a/src/main/worktree-trash.ts +++ b/src/main/worktree-trash.ts @@ -163,7 +163,10 @@ export function collectWorktreeTrashSweepRoots( continue } try { - roots.add(computeWorkspaceRoot(repo.path, getWorktreePathSettings(repo, settings))) + const workspaceRoot = computeWorkspaceRoot(repo.path, getWorktreePathSettings(repo, settings)) + if (!parseWslPath(workspaceRoot)) { + roots.add(workspaceRoot) + } } catch { // A repo with an unusable configured base path simply has no trash root to sweep. }