@mcuste/pi-herdr-worktree 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ import { normalize } from "node:path";
2
+ import { type CommandRunner, runChecked } from "./process.js";
3
+
4
+ export interface GitWorktreeEntry {
5
+ readonly path: string;
6
+ readonly head: string | null;
7
+ readonly branch: string | null;
8
+ }
9
+
10
+ function comparablePath(value: string): string {
11
+ return normalize(value).replace(/\/+$/u, "");
12
+ }
13
+
14
+ /**
15
+ * Reads the worktrees Git itself knows about. Herdr reports what it did; this is the
16
+ * independent check that the repository really changed the same way.
17
+ *
18
+ * `-z` ends every record with a NUL, so a path that contains a newline cannot pretend to be
19
+ * another record. It needs Git 2.36 or newer.
20
+ */
21
+ export async function readGitWorktrees(
22
+ runner: CommandRunner,
23
+ cwd: string,
24
+ signal: AbortSignal | undefined,
25
+ ): Promise<readonly GitWorktreeEntry[]> {
26
+ const result = await runChecked(
27
+ runner,
28
+ "git",
29
+ ["worktree", "list", "--porcelain", "-z"],
30
+ { cwd, signal },
31
+ "Unable to list the Git worktrees",
32
+ );
33
+
34
+ const entries: GitWorktreeEntry[] = [];
35
+ let path: string | undefined;
36
+ let head: string | null = null;
37
+ let branch: string | null = null;
38
+
39
+ const flush = () => {
40
+ if (path) {
41
+ entries.push({ path: comparablePath(path), head, branch });
42
+ }
43
+ path = undefined;
44
+ head = null;
45
+ branch = null;
46
+ };
47
+
48
+ // An unrecognised record, such as `bare` or `locked`, is ignored rather than read as a path.
49
+ for (const record of result.stdout.split("\0")) {
50
+ if (record.startsWith("worktree ")) {
51
+ flush();
52
+ path = record.slice("worktree ".length);
53
+ } else if (record.startsWith("HEAD ")) {
54
+ head = record.slice("HEAD ".length);
55
+ } else if (record.startsWith("branch refs/heads/")) {
56
+ branch = record.slice("branch refs/heads/".length);
57
+ }
58
+ }
59
+ flush();
60
+ return entries;
61
+ }
62
+
63
+ export function findGitWorktree(
64
+ entries: readonly GitWorktreeEntry[],
65
+ path: string,
66
+ ): GitWorktreeEntry | undefined {
67
+ const wanted = comparablePath(path);
68
+ return entries.find((entry) => entry.path === wanted);
69
+ }