All notes

Reuse node_modules in a git worktree on Windows, and remove it safely

GitWindowsPowerShellNode.js

Create the worktree, give it the main checkout’s packages through a directory junction, and delete that junction before you remove the worktree. With the junction still in place, git worktree remove deleted the packages it pointed to when I tested it, and exited 0.

I needed a second worktree on 31 October. hrih.si’s main checkout had to stay exactly as it was until a release job ran on 3 November, because that job compared it with a saved patch. The llms.txt endpoint had to be built and tested in the meantime, so it went into a worktree on a branch of its own.

Create it

In PowerShell, from the main checkout:

git worktree add -b llms-txt ..\hrih-site-llms main
New-Item -ItemType Junction -Path ..\hrih-site-llms\node_modules -Target (Resolve-Path .\node_modules)

The worktree shares the repository’s history and gets its own files and branch. The junction makes its node_modules the very folder the main checkout uses, so npm run build and the tests run there straight away. A junction needs neither administrator rights nor Developer Mode, unlike a symbolic link.

Everything that writes into node_modules now writes into the main checkout’s copy. Astro and Vite keep their caches there, so both checkouts share them, and an npm install in the worktree would change the main checkout’s packages too.

Remove the junction first

In a test repository with the same layout, and node_modules git-ignored as usual, I removed a worktree five ways and checked whether a file in the linked packages survived:

How Exit code Packages survive
git worktree remove, junction in place 0 no
git worktree remove --force, junction in place 0 no
[System.IO.Directory]::Delete(junction, $false), then git worktree remove 0 yes
Remove-Item on the junction 0 yes
Remove-Item -Recurse -Force on the junction 0 yes

That was Git for Windows 2.33.1 and PowerShell 7.4. Git didn’t even ask for --force: an ignored folder doesn’t count as a change, so the worktree looked clean, and git deleted its whole tree, through the junction. Git for Windows 2.54.0, from April 2026, stops at junctions instead, and leaves the junction and the worktree folder on disk. Removing the junction first gives the same clean result on any version.

So remove the link, then the worktree:

[System.IO.Directory]::Delete((Resolve-Path ..\hrih-site-llms\node_modules).Path, $false)
git worktree remove ..\hrih-site-llms
git branch -d llms-txt

Directory.Delete with $false removes the junction and never recurses, in PowerShell 7 and in Windows PowerShell 5.1 alike. PowerShell 7’s Remove-Item also detaches a junction without touching its target, even with -Recurse, but I haven’t tested 5.1’s. Give Directory.Delete a full path, as Resolve-Path does here. .NET resolves a relative path against the process’s working directory, and that isn’t always PowerShell’s current location. git branch -d only deletes a branch that’s been merged.

For llms.txt, the junction came out on 31 October, right after the commit on the branch, and the worktree on 3 November, once the branch was merged. The main checkout’s packages were intact both times.