Skip to content

Fix worktree creation EACCES when repo root is the home directory - #334994

Open
Suliat Mayowa Ogungbe (suliatmayowaogungbe-glitch) wants to merge 9 commits into
microsoft:mainfrom
suliatmayowaogungbe-glitch:fix-worktree-home-dir-eacces
Open

Fix worktree creation EACCES when repo root is the home directory#334994
Suliat Mayowa Ogungbe (suliatmayowaogungbe-glitch) wants to merge 9 commits into
microsoft:mainfrom
suliatmayowaogungbe-glitch:fix-worktree-home-dir-eacces

Conversation

@suliatmayowaogungbe-glitch

@suliatmayowaogungbe-glitch Suliat Mayowa Ogungbe (suliatmayowaogungbe-glitch) commented Sep 8, 2026

Copy link
Copy Markdown

Fixes #316940. getWorktreesRoot() now nests worktrees under .git/vscode-worktrees when the repository root is the user's home directory, instead of trying to write to its parent directory (e.g. /home). This container is invisible to git status/ls-files (git never walks .git), so it can't dirty the repo or leak into the include-file copy step, and the workspace-trust provenance check (isWorktreeUnderRepository) recognizes it the same way it already recognized the sibling-directory case — nested worktrees inherit trust from an already-trusted base repo exactly like sibling worktrees always have. Falls back to the pre-existing sibling behavior on the rare case where the repo root is home but .git is a redirect file rather than a directory (a submodule or a checkout made via git worktree add).

@suliatmayowaogungbe-glitch

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The home-directory comparison remains case-sensitive on case-insensitive platforms, allowing the original permission failure to persist.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes worktree creation when the repository root is the user’s home directory.

Changes:

  • Adds an in-home .worktrees fallback.
  • Passes the host home directory during worktree creation.
  • Adds path-selection tests.
File summaries
File Description
worktreePaths.ts Selects the fallback worktree location.
worktreeIsolation.ts Supplies the host home directory.
worktreeIsolation.test.ts Tests fallback and existing behavior.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

*/
export function getWorktreesRoot(repositoryRoot: URI): URI {
export function getWorktreesRoot(repositoryRoot: URI, homeDirectory?: URI): URI {
if (homeDirectory && isEqual(normalizePath(repositoryRoot), normalizePath(homeDirectory))) {

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The nested directory dirties the base repository and can cause include-file copying to ingest previous worktrees.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/vs/platform/agentHost/common/worktreePaths.ts:23

  • Project guidance limits function JSDoc to one or two short sentences; this addition extends an already long contract comment with implementation history and caller-specific detail. Condense the block to the observable API behavior.

src/vs/platform/agentHost/test/node/shared/worktreeIsolation.test.ts:212

  • This explanatory block is inside a test body and exceeds the project's one-line limit for inline comments. The platform behavior can be stated in one line.
		// os.homedir() and the repository root URI can report the same directory
		// with different casing. On case-insensitive filesystems (Windows/macOS)
		// this must still be recognized as the home directory; on Linux, differing
		// case is genuinely a different directory.
  • Files reviewed: 3/3 changed files
  • Comments generated: 4
  • Review effort level: Balanced

// difference between the repository root and `os.homedir()` doesn't cause
// the home-directory case below to be missed.
if (homeDirectory && extUriBiasedIgnorePathCase.isEqual(extUriBiasedIgnorePathCase.normalizePath(repositoryRoot), extUriBiasedIgnorePathCase.normalizePath(homeDirectory))) {
return URI.joinPath(repositoryRoot, '.worktrees');

const { worktreePath, branchName, baseBranch } = await this._worktreeCreationSequencer.queue(repositoryRoot.toString(), async () => {
const worktreesRoot = getWorktreesRoot(repositoryRoot);
const worktreesRoot = getWorktreesRoot(repositoryRoot, URI.file(homedir()));
Comment on lines +28 to +31
// Local file-scheme paths from the OS: compare with the platform's own case
// sensitivity (ignored on Windows/macOS, honored on Linux) so a casing
// difference between the repository root and `os.homedir()` doesn't cause
// the home-directory case below to be missed.
Comment on lines +195 to +198
// Repository root *is* the home directory: a sibling directory would be
// `/home/alice.worktrees`, which requires write access to `/home` that a
// non-root user does not have on a standard FHS layout. Nest inside the
// home directory instead.
@suliatmayowaogungbe-glitch

Copy link
Copy Markdown
Author

Fixed — the container now lives under .git/vscode-worktrees instead of directly in the working tree, so git ls-files never walks it (avoids both the git-status dirtying and the include-file copy leaking previous worktrees).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The workspace-trust validator rejects the fallback path before worktree creation in the default trust-enabled flow.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

// difference between the repository root and `os.homedir()` doesn't cause
// the home-directory case below to be missed.
if (homeDirectory && extUriBiasedIgnorePathCase.isEqual(extUriBiasedIgnorePathCase.normalizePath(repositoryRoot), extUriBiasedIgnorePathCase.normalizePath(homeDirectory))) {
return URI.joinPath(repositoryRoot, '.git', 'vscode-worktrees');
@suliatmayowaogungbe-glitch

Copy link
Copy Markdown
Author

Good catch, isWorktreeUnderRepository now also checks the .git-nested container (by passing repositoryRoot as its own homeDirectory, which deterministically reproduces that path without needing real home-directory knowledge in the browser layer). Confirmed this was a real bug: without it, workspace trust would throw and abort worktree creation every time, since the browser-side trust gate had no way to recognize the new fallback location.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The fallback fails for valid repositories whose .git entry is a file, and its trust behavior contradicts the PR description.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

src/vs/platform/agentHost/common/worktreePaths.ts:62

  • The PR description says the fallback deliberately will not inherit workspace trust because the browser gate still assumes the sibling layout, but this change makes that gate accept the .git/vscode-worktrees layout; all current trust callers use this predicate. Please align the implementation and stated security tradeoff—either retain sibling-only trust behavior or update the description/rationale to document that nested worktrees do inherit trust.
	return [getWorktreesRoot(repositoryRoot), getWorktreesRoot(repositoryRoot, repositoryRoot)].some(container => {
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

// difference between the repository root and `os.homedir()` doesn't cause
// the home-directory case below to be missed.
if (homeDirectory && extUriBiasedIgnorePathCase.isEqual(extUriBiasedIgnorePathCase.normalizePath(repositoryRoot), extUriBiasedIgnorePathCase.normalizePath(homeDirectory))) {
return URI.joinPath(repositoryRoot, '.git', 'vscode-worktrees');
@suliatmayowaogungbe-glitch

Copy link
Copy Markdown
Author

Both addressed: (1) .git-file case now falls back to sibling behavior instead of trying to mkdir inside a file, with a test using real .git-as-directory and .git-as-file fixtures; (2) updated the PR description, it was stale, nested worktrees do inherit trust now that isWorktreeUnderRepository recognizes the .git-nested container.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Repository-root recovery does not support the new nested layout, and the real Git operation lacks integration coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment on lines +37 to +38
if (homeDirectory && extUriBiasedIgnorePathCase.isEqual(extUriBiasedIgnorePathCase.normalizePath(repositoryRoot), extUriBiasedIgnorePathCase.normalizePath(homeDirectory))) {
return URI.joinPath(repositoryRoot, '.git', 'vscode-worktrees');
// non-root user does not have on a standard FHS layout. Nest under `.git`
// instead - writable (git requires it), and never walked by `git ls-files`,
// so it cannot dirty `git status` or leak into the gitignored-file copy step.
assert.strictEqual(getWorktreesRoot(home, home).fsPath, URI.file('/home/alice/.git/vscode-worktrees').fsPath);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent Host Incorrectly Constructs Git Worktree Path, Creating Directory in Wrong Location

3 participants