From 899271433b634dbd04d12bf018833afed6b547a2 Mon Sep 17 00:00:00 2001 From: Corwin Marsh <7642548+corwinm@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:35:23 -0800 Subject: [PATCH] fix(init): stop auto-adding default worktrees ignore Avoid mutating .gitignore for default worktree paths while preserving reposDir ignore handling and update init integration coverage accordingly.\n\nRefs: https://github.com/corwinm/arashi-arashi/issues/120 --- src/commands/init.ts | 15 ++------------- tests/integration/init.test.ts | 29 ++++++++++++++++++++++------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/commands/init.ts b/src/commands/init.ts index 0a39989..6fdb2df 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -15,8 +15,6 @@ import { exec as gitExec } from "../lib/git.ts"; import { discoverRepositories } from "../core/repository.ts"; import { DEFAULT_WORKTREES_DIR, - DEFAULT_WORKTREES_GITIGNORE_ENTRY, - isDefaultWorktreesDir, normalizeWorktreesDir, WorktreeLocationValidationError, } from "../lib/worktree-location.ts"; @@ -419,12 +417,9 @@ function hasGitignorePattern(content: string, pattern: string): boolean { .some((line) => line === pattern || line === alternate); } -async function updateGitignore(cwd: string, reposDir: string, worktreesDir: string): Promise { +async function updateGitignore(cwd: string, reposDir: string): Promise { const gitignorePath = join(cwd, ".gitignore"); const patterns = [normalizeGitignorePattern(reposDir)]; - if (isDefaultWorktreesDir(worktreesDir)) { - patterns.push(DEFAULT_WORKTREES_GITIGNORE_ENTRY); - } let content = ""; let originalContent: string | null = null; @@ -798,15 +793,12 @@ async function executeInit(options: InitOptions): Promise { // 11. Update .gitignore const gitignorePath = join(cwd, ".gitignore"); const managedPatterns = [normalizeGitignorePattern(reposDir)]; - if (isDefaultWorktreesDir(worktreesDir)) { - managedPatterns.push(DEFAULT_WORKTREES_GITIGNORE_ENTRY); - } if (options.dryRun) { logDryRun("UPDATE_FILE", `${gitignorePath} (add: ${managedPatterns.join(", ")})`); } else { logVerbose("Updating .gitignore...", options); try { - await updateGitignore(cwd, reposDir, worktreesDir); + await updateGitignore(cwd, reposDir); logVerbose("✓ .gitignore updated", options); } catch (error) { await executeRollback(); @@ -871,9 +863,6 @@ function displaySuccess(result: InitResult, options: InitOptions): void { const reposDir = options.reposDir || "./repos"; console.log(`\nUpdated .gitignore to exclude: ${normalizeGitignorePattern(reposDir)}`); - if (isDefaultWorktreesDir(options.worktreesDir ?? DEFAULT_WORKTREES_DIR)) { - console.log(` • ${DEFAULT_WORKTREES_GITIGNORE_ENTRY}`); - } console.log("\nNext steps:"); if (result.discoveredCount && result.discoveredCount > 0) { diff --git a/tests/integration/init.test.ts b/tests/integration/init.test.ts index 8b806a6..fc94590 100644 --- a/tests/integration/init.test.ts +++ b/tests/integration/init.test.ts @@ -129,7 +129,7 @@ describe("init command - success cases", () => { expect(await filesystem.fileExists(gitignorePath)).toBe(true); const gitignoreContent = await filesystem.readTextFile(gitignorePath); expect(gitignoreContent).toContain("repos/"); - expect(gitignoreContent).toContain(".arashi/worktrees/"); + expect(gitignoreContent).not.toContain(".arashi/worktrees/"); }); test("init with custom repos directory", async () => { @@ -147,7 +147,7 @@ describe("init command - success cases", () => { // Verify .gitignore updated with custom path const gitignoreContent = await filesystem.readTextFile(join(testDir, ".gitignore")); expect(gitignoreContent).toContain("custom-repos/"); - expect(gitignoreContent).toContain(".arashi/worktrees/"); + expect(gitignoreContent).not.toContain(".arashi/worktrees/"); }); test("init with custom worktrees directory does not auto-ignore custom path", async () => { @@ -212,7 +212,7 @@ describe("init command - success cases", () => { const gitignoreContent1 = await filesystem.readTextFile(join(testDir, ".gitignore")); const reposLineCount1 = (gitignoreContent1.match(/repos\//g) || []).length; - const worktreesLineCount1 = (gitignoreContent1.match(/\.arashi\/worktrees\//g) || []).length; + expect(gitignoreContent1).not.toContain(".arashi/worktrees/"); // Delete config to allow re-init await rm(join(testDir, ".arashi"), { recursive: true }); @@ -222,11 +222,10 @@ describe("init command - success cases", () => { const gitignoreContent2 = await filesystem.readTextFile(join(testDir, ".gitignore")); const reposLineCount2 = (gitignoreContent2.match(/repos\//g) || []).length; - const worktreesLineCount2 = (gitignoreContent2.match(/\.arashi\/worktrees\//g) || []).length; + expect(gitignoreContent2).not.toContain(".arashi/worktrees/"); // Verify repos/ pattern appears same number of times expect(reposLineCount2).toBe(reposLineCount1); - expect(worktreesLineCount2).toBe(worktreesLineCount1); }); test("hook templates are not overwritten if they exist", async () => { @@ -439,7 +438,7 @@ describe("init command - edge cases", () => { expect(await filesystem.fileExists(gitignorePath)).toBe(true); const content = await filesystem.readTextFile(gitignorePath); expect(content).toContain("repos/"); - expect(content).toContain(".arashi/worktrees/"); + expect(content).not.toContain(".arashi/worktrees/"); await cleanup(testDir); }); @@ -458,7 +457,23 @@ describe("init command - edge cases", () => { const content = await filesystem.readTextFile(join(testDir, ".gitignore")); expect(content).toContain("node_modules/\n"); expect(content).toContain("repos/"); - expect(content).toContain(".arashi/worktrees/"); + expect(content).not.toContain(".arashi/worktrees/"); + + await cleanup(testDir); + }); + + test("preserves existing default worktrees ignore entry without rewriting it", async () => { + testDir = await createTempGitRepo(); + + const existingContent = "repos/\n.arashi/worktrees/\nnode_modules/\n"; + await writeFile(join(testDir, ".gitignore"), existingContent); + + const result = await runInitCommand(testDir); + + expect(result.exitCode).toBe(0); + + const content = await filesystem.readTextFile(join(testDir, ".gitignore")); + expect(content).toBe(existingContent); await cleanup(testDir); });