Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 2 additions & 13 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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<void> {
async function updateGitignore(cwd: string, reposDir: string): Promise<void> {
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;
Expand Down Expand Up @@ -798,15 +793,12 @@ async function executeInit(options: InitOptions): Promise<InitResult> {
// 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();
Expand Down Expand Up @@ -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) {
Expand Down
29 changes: 22 additions & 7 deletions tests/integration/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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 });
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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);
});
Expand All @@ -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);
});
Expand Down
Loading