Skip to content

Commit 3e67fb7

Browse files
committed
test: add git worktree tests for top-level and read
Add comprehensive tests for git worktree support: - Test toplevel() works correctly in worktrees - Test toplevel() works from subdirectories in worktrees - Test reading edit commit message from worktree
1 parent 170ef0e commit 3e67fb7

4 files changed

Lines changed: 175 additions & 2 deletions

File tree

@commitlint/read/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@
3838
"devDependencies": {
3939
"@commitlint/test": "^20.0.0",
4040
"@commitlint/utils": "^20.0.0",
41+
"@types/fs-extra": "^11.0.3",
4142
"@types/git-raw-commits": "^2.0.3",
42-
"@types/minimist": "^1.2.4"
43+
"@types/minimist": "^1.2.4",
44+
"@types/tmp": "^0.2.5",
45+
"fs-extra": "^11.0.0",
46+
"tmp": "^0.2.1"
4347
},
4448
"dependencies": {
4549
"@commitlint/top-level": "^20.0.0",

@commitlint/read/src/read.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { test, expect } from "vitest";
22
import fs from "fs/promises";
3+
import fsExtra from "fs-extra";
34
import path from "node:path";
45
import { git } from "@commitlint/test";
56
import { x } from "tinyexec";
7+
import tmp from "tmp";
68

79
import read from "./read.js";
810

@@ -150,3 +152,49 @@ test("should not read any commits when there are no tags", async () => {
150152

151153
expect(result).toHaveLength(0);
152154
});
155+
156+
test("get edit commit message from git worktree", async () => {
157+
const tmpDir = tmp.dirSync({ keep: false, unsafeCleanup: true });
158+
const mainRepoDir = path.join(tmpDir.name, "main");
159+
const worktreeDir = path.join(tmpDir.name, "worktree");
160+
161+
// Initialize main repo
162+
await fsExtra.mkdirp(mainRepoDir);
163+
await x("git", ["init"], { nodeOptions: { cwd: mainRepoDir } });
164+
await x("git", ["config", "user.email", "test@example.com"], {
165+
nodeOptions: { cwd: mainRepoDir },
166+
});
167+
await x("git", ["config", "user.name", "test"], {
168+
nodeOptions: { cwd: mainRepoDir },
169+
});
170+
await x("git", ["config", "commit.gpgsign", "false"], {
171+
nodeOptions: { cwd: mainRepoDir },
172+
});
173+
174+
// Create initial commit in main repo
175+
await fs.writeFile(path.join(mainRepoDir, "file.txt"), "content");
176+
await x("git", ["add", "."], { nodeOptions: { cwd: mainRepoDir } });
177+
await x("git", ["commit", "-m", "initial"], {
178+
nodeOptions: { cwd: mainRepoDir },
179+
});
180+
181+
// Create a branch and worktree
182+
await x("git", ["branch", "worktree-branch"], {
183+
nodeOptions: { cwd: mainRepoDir },
184+
});
185+
await x("git", ["worktree", "add", worktreeDir, "worktree-branch"], {
186+
nodeOptions: { cwd: mainRepoDir },
187+
});
188+
189+
// Make a commit in the worktree
190+
await fs.writeFile(path.join(worktreeDir, "worktree-file.txt"), "worktree");
191+
await x("git", ["add", "."], { nodeOptions: { cwd: worktreeDir } });
192+
await x("git", ["commit", "-m", "worktree commit"], {
193+
nodeOptions: { cwd: worktreeDir },
194+
});
195+
196+
// Read the edit commit message from the worktree
197+
const expected = ["worktree commit\n\n"];
198+
const actual = await read({ edit: true, cwd: worktreeDir });
199+
expect(actual).toEqual(expected);
200+
});

@commitlint/top-level/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@
3636
},
3737
"license": "MIT",
3838
"devDependencies": {
39-
"@commitlint/utils": "^20.0.0"
39+
"@commitlint/utils": "^20.0.0",
40+
"@types/fs-extra": "^11.0.3",
41+
"@types/tmp": "^0.2.5",
42+
"fs-extra": "^11.0.0",
43+
"tmp": "^0.2.1"
4044
},
4145
"gitHead": "e82f05a737626bb69979d14564f5ff601997f679"
4246
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { test, expect, describe } from "vitest";
2+
import path from "node:path";
3+
import fs from "fs-extra";
4+
import tmp from "tmp";
5+
import { execFile } from "node:child_process";
6+
import { promisify } from "node:util";
7+
8+
import toplevel from "./index.js";
9+
10+
const execFileAsync = promisify(execFile);
11+
12+
async function initGitRepo(cwd: string): Promise<void> {
13+
await execFileAsync("git", ["init"], { cwd });
14+
await execFileAsync("git", ["config", "user.email", "test@example.com"], {
15+
cwd,
16+
});
17+
await execFileAsync("git", ["config", "user.name", "test"], { cwd });
18+
await execFileAsync("git", ["config", "commit.gpgsign", "false"], { cwd });
19+
}
20+
21+
describe("toplevel", () => {
22+
test("should return git root for a regular repository", async () => {
23+
const tmpDir = tmp.dirSync({ keep: false, unsafeCleanup: true });
24+
const repoDir = tmpDir.name;
25+
26+
await initGitRepo(repoDir);
27+
28+
const result = await toplevel(repoDir);
29+
expect(result).toBe(repoDir);
30+
});
31+
32+
test("should return git root from a subdirectory", async () => {
33+
const tmpDir = tmp.dirSync({ keep: false, unsafeCleanup: true });
34+
const repoDir = tmpDir.name;
35+
36+
await initGitRepo(repoDir);
37+
38+
const subDir = path.join(repoDir, "sub", "dir");
39+
await fs.mkdirp(subDir);
40+
41+
const result = await toplevel(subDir);
42+
expect(result).toBe(repoDir);
43+
});
44+
45+
test("should return undefined for a non-git directory", async () => {
46+
const tmpDir = tmp.dirSync({ keep: false, unsafeCleanup: true });
47+
48+
const result = await toplevel(tmpDir.name);
49+
expect(result).toBeUndefined();
50+
});
51+
52+
test("should work with git worktrees", async () => {
53+
const tmpDir = tmp.dirSync({ keep: false, unsafeCleanup: true });
54+
const mainRepoDir = path.join(tmpDir.name, "main");
55+
const worktreeDir = path.join(tmpDir.name, "worktree");
56+
57+
await fs.mkdirp(mainRepoDir);
58+
await initGitRepo(mainRepoDir);
59+
60+
// Create an initial commit (required for worktree)
61+
await fs.writeFile(path.join(mainRepoDir, "file.txt"), "content");
62+
await execFileAsync("git", ["add", "."], { cwd: mainRepoDir });
63+
await execFileAsync("git", ["commit", "-m", "initial"], {
64+
cwd: mainRepoDir,
65+
});
66+
67+
// Create a new branch for the worktree
68+
await execFileAsync("git", ["branch", "worktree-branch"], {
69+
cwd: mainRepoDir,
70+
});
71+
72+
// Create the worktree
73+
await execFileAsync(
74+
"git",
75+
["worktree", "add", worktreeDir, "worktree-branch"],
76+
{ cwd: mainRepoDir },
77+
);
78+
79+
// toplevel should return the worktree directory, not the main repo
80+
const result = await toplevel(worktreeDir);
81+
expect(result).toBe(worktreeDir);
82+
});
83+
84+
test("should work from a subdirectory of a git worktree", async () => {
85+
const tmpDir = tmp.dirSync({ keep: false, unsafeCleanup: true });
86+
const mainRepoDir = path.join(tmpDir.name, "main");
87+
const worktreeDir = path.join(tmpDir.name, "worktree");
88+
89+
await fs.mkdirp(mainRepoDir);
90+
await initGitRepo(mainRepoDir);
91+
92+
// Create an initial commit
93+
await fs.writeFile(path.join(mainRepoDir, "file.txt"), "content");
94+
await execFileAsync("git", ["add", "."], { cwd: mainRepoDir });
95+
await execFileAsync("git", ["commit", "-m", "initial"], {
96+
cwd: mainRepoDir,
97+
});
98+
99+
// Create a new branch and worktree
100+
await execFileAsync("git", ["branch", "worktree-branch"], {
101+
cwd: mainRepoDir,
102+
});
103+
await execFileAsync(
104+
"git",
105+
["worktree", "add", worktreeDir, "worktree-branch"],
106+
{ cwd: mainRepoDir },
107+
);
108+
109+
// Create a subdirectory in the worktree
110+
const subDir = path.join(worktreeDir, "sub", "dir");
111+
await fs.mkdirp(subDir);
112+
113+
// toplevel from subdirectory should return the worktree root
114+
const result = await toplevel(subDir);
115+
expect(result).toBe(worktreeDir);
116+
});
117+
});

0 commit comments

Comments
 (0)