"A git worktree can't build: the .csproj files are gitignored"
▸ SYMPTOM
You git worktree add a second checkout to run a headless build, and:
dotnet build Code\MyProject.csproj
MSB1009: Project file does not exist.The exact same command works in the main working tree. The worktree's Code/ and Editor/ folders are there, but there is no .csproj in them at all.
▸ CAUSE
s&box generates the .csproj files from the editor and gitignores them (*.csproj is in .gitignore). They are never committed, so a fresh worktree (which only contains tracked files) has no project file for dotnet build to build. The main tree works only because the editor already generated its csproj there.
▸ FIX
Copy the two project files from the main working tree into the worktree before building:
cp main-tree/Code/*.csproj worktree/Code/
cp main-tree/Editor/*.editor.csproj worktree/Editor/These are boilerplate SDK-style projects whose references are relative to the s&box install (e.g. ../../../../../../../Program Files (x86)/Steam/steamapps/common/sbox/...). The copy resolves correctly only if the worktree sits at the same path depth as the main tree. If both are the same number of segments under your dev root, the relative reference count matches. Never commit the copied .csproj (still gitignored: verify with git status after copying).
If the worktree is at a different (deeper) depth
A scratch worktree nested deeper than the main tree makes the relative ../…/Steam/... refs resolve to the wrong root, and the copied csproj can't find the s&box DLLs. Instead author a worktree-local .csproj with absolute paths (depth-independent) for the Reference / Analyzer / ProjectReference entries, pointing at your s&box install (default C:/Program Files (x86)/Steam/steamapps/common/sbox/...).
Also redirect <OutputPath> and <DocumentationFile> to an isolated in-worktree folder: the stock csproj writes to the shared sbox/.vs/output/, which the live editor uses, and a verify build must not clobber it.
Do not point <BaseIntermediateOutputPath> at a custom folder: it warns MSB3539, and stale generated *.AssemblyInfo.cs / AssemblyAttributes.cs left under it get globbed into the compile, producing a wall of CS0579 duplicate-attribute errors. Leave obj/ at its default (the SDK auto-excludes it from the compile glob) and rm -rf any stray intermediate/output dir before rebuilding.
The --no-incremental flake
With an isolated custom <OutputPath>, a second build run with --no-incremental can fail:
MSB3030: Could not copy … deps.json / staticwebassets.endpoints.json … not foundThis is a stale-state race, not a code error: --no-incremental clears the output, then the copy-outputs step races the just-cleared isolated dir. The first build (or a plain dotnet build without --no-incremental) succeeds. Fix: rm -rf the custom output and obj/, then run dotnet build without --no-incremental. For a deeper worktree with a redirected OutputPath, prefer a clean-plus-plain build over --no-incremental.
▸ WHY IT WORKS
Copying (or authoring) the csproj gives dotnet build the project file that s&box normally regenerates on the editor side but never commits. Matching path depth keeps the relative install references valid; absolute references remove the depth dependency entirely. Isolating the output directory keeps the verify build from overwriting the editor's shared output, and staying on the default obj/ with a plain build avoids both the duplicate-attribute glob and the --no-incremental copy race.
- Published