* feat(worktrees): copy project-level .worktreeinclude paths into new worktrees
Read .worktreeinclude at the repo root (gitignore syntax) and copy matching
gitignored paths from the primary checkout into each newly created local
worktree, so .env and other local config carry over with zero per-user setup.
- Literal patterns resolve by direct stat; globs match against
ls-files --others --ignored --exclude-standard --directory (collapsed
dirs keep huge repos fast); every candidate is re-verified with
check-ignore so tracked or unignored files are never copied.
- Copy semantics, never symlink: APFS clone-copy on macOS, real copy
elsewhere, so each worktree owns its files (unlike repo.symlinkPaths,
which it merges with rather than replaces).
- Failures never block worktree creation.
- Remote (SSH) creation skips it, same as symlinkPaths.
- Split APFS clone helpers into worktree-apfs-clone.ts (max-lines).
Closes#7549
* fix(worktrees): harden worktree include copying
* fix(worktrees): support nested includes on Git 2.25
* fix(worktrees): bound include copy costs
* fix(worktrees): close include correctness and perf gaps
* fix(worktrees): preserve included copy semantics
* fix(worktrees): harden include resolution
* fix(worktrees): preserve bounded include resolution
* fix(worktrees): bound include filesystem resolution
* fix(worktrees): harden include matching
* fix(worktrees): tighten include matching and scan bounds
* fix(types): use concrete filesystem stat types
* fix(worktrees): harden included path materialization
* perf(worktrees): stop include parsing at resolver budgets
* chore(skills): refresh bundled skill manifests
* refactor(worktrees): reduce .worktreeinclude to focused literal-only scope
The reviewed implementation grew well past the ticket (#7549), which asks for a
size-M feature that reuses existing worktree machinery. Trim back to the minimal
change that solves the reported problem safely:
- Resolver now supports literal files and directories only. Glob/negation lines
are skipped with a warning (documented follow-up), which removes the entire
user-controlled-regex ReDoS surface, the CPU/byte budgets, the git enumeration
scan, and the case-sensitivity engine. The filesystem + git check-ignore
handle existence and case for free.
- Copy layer folded back into worktree-symlinks.ts (link/copy modes share one
loop); dropped worktree-path-copy.ts, worktree-target-safety.ts, the
descendant-dedup/realpath/target-parent machinery, and the per-materialization
APFS filesystem cache. Kept the df/diskutil probe timeout.
- Reverted unrelated changes: check-ignored-paths timeout param and the
git-binary-compatibility enumeration tests.
Net: -1903/+172 across the include+copy code. Behavior for the ticket's cases
(.env, .env.local, .vscode/, node_modules, config/secrets.json) is unchanged;
gitignored-only + copy-not-symlink semantics preserved.
Closes#7549
* fix(worktrees): dereference symlinked .worktreeinclude entries + cache APFS volume probe
Two issues found by review + perf audit of the copy path:
- Correctness (HIGH): a listed entry that is itself a gitignored symlink was
copied AS a symlink (fs.cp dereference:false), and the darwin APFS branch was
skipped for all symlink sources. Editing the worktree's copy then wrote through
to the shared/primary target — inverting copy-mode's 'each worktree owns its
files' guarantee, and escaping the worktree entirely if the link pointed
outside it. Now resolve realpath for a top-level symlink in copy mode so we
copy content; nested symlinks inside a copied dir stay as-is (cp -R semantics).
- Perf: assertSameApfsVolume ran df+diskutil per copied path (4 subprocesses
each), so an N-entry include spawned ~4N short-lived processes on the macOS
create hot path, all re-probing one volume. Add a per-materialization
device-keyed cache: one probe per distinct volume (4N -> ~4).
Tests: symlinked-file and symlinked-dir dereference regressions (no leak to
primary); APFS volume probed once regardless of copied-path count.