diff --git a/.gitignore b/.gitignore index f685aca6954..453e4e65fb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +node_modules node_modules/ dist/ .env diff --git a/cli/src/__tests__/worktree.test.ts b/cli/src/__tests__/worktree.test.ts index 6f6af963a49..157c467e447 100644 --- a/cli/src/__tests__/worktree.test.ts +++ b/cli/src/__tests__/worktree.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { execFileSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; import { afterEach, describe, expect, it, vi } from "vitest"; import { copyGitHooksToWorktreeGitDir, @@ -557,6 +558,39 @@ describe("worktree helpers", () => { } }); + it("ignores symlinked node_modules paths in git worktrees", () => { + const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-worktree-gitignore-")); + const repoRoot = path.join(tempRoot, "repo"); + const sharedRoot = path.join(tempRoot, "shared"); + const repoGitignorePath = fileURLToPath(new URL("../../../.gitignore", import.meta.url)); + + try { + fs.mkdirSync(repoRoot, { recursive: true }); + fs.mkdirSync(path.join(sharedRoot, "node_modules"), { recursive: true }); + fs.mkdirSync(path.join(sharedRoot, "packages", "db", "node_modules"), { recursive: true }); + fs.mkdirSync(path.join(repoRoot, "packages", "db"), { recursive: true }); + fs.writeFileSync(path.join(repoRoot, ".gitignore"), fs.readFileSync(repoGitignorePath, "utf8"), "utf8"); + + fs.symlinkSync(path.join(sharedRoot, "node_modules"), path.join(repoRoot, "node_modules")); + fs.symlinkSync( + path.join(sharedRoot, "packages", "db", "node_modules"), + path.join(repoRoot, "packages", "db", "node_modules"), + ); + + execFileSync("git", ["init"], { cwd: repoRoot, stdio: "ignore" }); + + const ignored = execFileSync("git", ["check-ignore", "-v", "node_modules", "packages/db/node_modules"], { + cwd: repoRoot, + encoding: "utf8", + }); + + expect(ignored).toContain(".gitignore:1:node_modules\tnode_modules"); + expect(ignored).toContain(".gitignore:1:node_modules\tpackages/db/node_modules"); + } finally { + fs.rmSync(tempRoot, { recursive: true, force: true }); + } + }); + it("creates and initializes a worktree from the top-level worktree:make command", async () => { const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-worktree-make-")); const repoRoot = path.join(tempRoot, "repo");