@procedures/git 0.2.3 → 0.4.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.
Files changed (157) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +125 -17
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/index.d.ts +2 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/module.d.ts.map +1 -1
  9. package/dist/module.js +314 -4
  10. package/dist/module.js.map +1 -1
  11. package/dist/procedures/git/blame.d.ts +17 -0
  12. package/dist/procedures/git/blame.d.ts.map +1 -0
  13. package/dist/procedures/git/blame.js +69 -0
  14. package/dist/procedures/git/blame.js.map +1 -0
  15. package/dist/procedures/git/branch/add.d.ts +12 -0
  16. package/dist/procedures/git/branch/add.d.ts.map +1 -0
  17. package/dist/procedures/git/branch/add.js +21 -0
  18. package/dist/procedures/git/branch/add.js.map +1 -0
  19. package/dist/procedures/git/branch/delete.d.ts +13 -0
  20. package/dist/procedures/git/branch/delete.d.ts.map +1 -0
  21. package/dist/procedures/git/branch/delete.js +24 -0
  22. package/dist/procedures/git/branch/delete.js.map +1 -0
  23. package/dist/procedures/git/branch/list.d.ts +13 -0
  24. package/dist/procedures/git/branch/list.d.ts.map +1 -0
  25. package/dist/procedures/git/branch/list.js +37 -0
  26. package/dist/procedures/git/branch/list.js.map +1 -0
  27. package/dist/procedures/git/branch.d.ts +8 -2
  28. package/dist/procedures/git/branch.d.ts.map +1 -1
  29. package/dist/procedures/git/branch.js +18 -37
  30. package/dist/procedures/git/branch.js.map +1 -1
  31. package/dist/procedures/git/commit.d.ts +2 -1
  32. package/dist/procedures/git/commit.d.ts.map +1 -1
  33. package/dist/procedures/git/commit.js +21 -12
  34. package/dist/procedures/git/commit.js.map +1 -1
  35. package/dist/procedures/git/index.d.ts +19 -0
  36. package/dist/procedures/git/index.d.ts.map +1 -1
  37. package/dist/procedures/git/index.js +19 -0
  38. package/dist/procedures/git/index.js.map +1 -1
  39. package/dist/procedures/git/init.d.ts +6 -2
  40. package/dist/procedures/git/init.d.ts.map +1 -1
  41. package/dist/procedures/git/init.js +8 -4
  42. package/dist/procedures/git/init.js.map +1 -1
  43. package/dist/procedures/git/merge/abort.d.ts +14 -0
  44. package/dist/procedures/git/merge/abort.d.ts.map +1 -0
  45. package/dist/procedures/git/merge/abort.js +18 -0
  46. package/dist/procedures/git/merge/abort.js.map +1 -0
  47. package/dist/procedures/git/merge.d.ts +16 -0
  48. package/dist/procedures/git/merge.d.ts.map +1 -0
  49. package/dist/procedures/git/merge.js +41 -0
  50. package/dist/procedures/git/merge.js.map +1 -0
  51. package/dist/procedures/git/porcelain.d.ts +27 -0
  52. package/dist/procedures/git/porcelain.d.ts.map +1 -0
  53. package/dist/procedures/git/porcelain.js +66 -0
  54. package/dist/procedures/git/porcelain.js.map +1 -0
  55. package/dist/procedures/git/push.d.ts.map +1 -1
  56. package/dist/procedures/git/push.js +4 -2
  57. package/dist/procedures/git/push.js.map +1 -1
  58. package/dist/procedures/git/rebase/abort.d.ts +13 -0
  59. package/dist/procedures/git/rebase/abort.d.ts.map +1 -0
  60. package/dist/procedures/git/rebase/abort.js +17 -0
  61. package/dist/procedures/git/rebase/abort.js.map +1 -0
  62. package/dist/procedures/git/rebase/continue.d.ts +13 -0
  63. package/dist/procedures/git/rebase/continue.d.ts.map +1 -0
  64. package/dist/procedures/git/rebase/continue.js +23 -0
  65. package/dist/procedures/git/rebase/continue.js.map +1 -0
  66. package/dist/procedures/git/rebase.d.ts +22 -0
  67. package/dist/procedures/git/rebase.d.ts.map +1 -0
  68. package/dist/procedures/git/rebase.js +46 -0
  69. package/dist/procedures/git/rebase.js.map +1 -0
  70. package/dist/procedures/git/refs.d.ts +31 -0
  71. package/dist/procedures/git/refs.d.ts.map +1 -0
  72. package/dist/procedures/git/refs.js +52 -0
  73. package/dist/procedures/git/refs.js.map +1 -0
  74. package/dist/procedures/git/reset.d.ts +13 -0
  75. package/dist/procedures/git/reset.d.ts.map +1 -0
  76. package/dist/procedures/git/reset.js +31 -0
  77. package/dist/procedures/git/reset.js.map +1 -0
  78. package/dist/procedures/git/restore.d.ts +13 -0
  79. package/dist/procedures/git/restore.d.ts.map +1 -0
  80. package/dist/procedures/git/restore.js +26 -0
  81. package/dist/procedures/git/restore.js.map +1 -0
  82. package/dist/procedures/git/revParse.d.ts +13 -0
  83. package/dist/procedures/git/revParse.d.ts.map +1 -0
  84. package/dist/procedures/git/revParse.js +23 -0
  85. package/dist/procedures/git/revParse.js.map +1 -0
  86. package/dist/procedures/git/run.d.ts +2 -0
  87. package/dist/procedures/git/run.d.ts.map +1 -1
  88. package/dist/procedures/git/run.js +1 -0
  89. package/dist/procedures/git/run.js.map +1 -1
  90. package/dist/procedures/git/show.d.ts +15 -0
  91. package/dist/procedures/git/show.d.ts.map +1 -0
  92. package/dist/procedures/git/show.js +49 -0
  93. package/dist/procedures/git/show.js.map +1 -0
  94. package/dist/procedures/git/status.d.ts +3 -1
  95. package/dist/procedures/git/status.d.ts.map +1 -1
  96. package/dist/procedures/git/status.js +11 -24
  97. package/dist/procedures/git/status.js.map +1 -1
  98. package/dist/procedures/git/tag/add.d.ts +12 -0
  99. package/dist/procedures/git/tag/add.d.ts.map +1 -0
  100. package/dist/procedures/git/tag/add.js +25 -0
  101. package/dist/procedures/git/tag/add.js.map +1 -0
  102. package/dist/procedures/git/tag/delete.d.ts +12 -0
  103. package/dist/procedures/git/tag/delete.d.ts.map +1 -0
  104. package/dist/procedures/git/tag/delete.js +19 -0
  105. package/dist/procedures/git/tag/delete.js.map +1 -0
  106. package/dist/procedures/git/tag/list.d.ts +12 -0
  107. package/dist/procedures/git/tag/list.d.ts.map +1 -0
  108. package/dist/procedures/git/tag/list.js +34 -0
  109. package/dist/procedures/git/tag/list.js.map +1 -0
  110. package/dist/procedures/git/worktree/add.d.ts +13 -0
  111. package/dist/procedures/git/worktree/add.d.ts.map +1 -0
  112. package/dist/procedures/git/worktree/add.js +35 -0
  113. package/dist/procedures/git/worktree/add.js.map +1 -0
  114. package/dist/procedures/git/worktree/list.d.ts +17 -0
  115. package/dist/procedures/git/worktree/list.d.ts.map +1 -0
  116. package/dist/procedures/git/worktree/list.js +52 -0
  117. package/dist/procedures/git/worktree/list.js.map +1 -0
  118. package/dist/procedures/git/worktree/remove.d.ts +13 -0
  119. package/dist/procedures/git/worktree/remove.d.ts.map +1 -0
  120. package/dist/procedures/git/worktree/remove.js +20 -0
  121. package/dist/procedures/git/worktree/remove.js.map +1 -0
  122. package/dist/types.d.ts +315 -0
  123. package/dist/types.d.ts.map +1 -1
  124. package/dist/types.js +280 -0
  125. package/dist/types.js.map +1 -1
  126. package/package.json +4 -4
  127. package/src/index.ts +2 -1
  128. package/src/module.ts +413 -4
  129. package/src/procedures/git/blame.ts +68 -0
  130. package/src/procedures/git/branch/add.ts +24 -0
  131. package/src/procedures/git/branch/delete.ts +29 -0
  132. package/src/procedures/git/branch/list.ts +40 -0
  133. package/src/procedures/git/branch.ts +19 -40
  134. package/src/procedures/git/commit.ts +20 -12
  135. package/src/procedures/git/index.ts +19 -0
  136. package/src/procedures/git/init.ts +11 -10
  137. package/src/procedures/git/merge/abort.ts +20 -0
  138. package/src/procedures/git/merge.ts +42 -0
  139. package/src/procedures/git/porcelain.ts +63 -0
  140. package/src/procedures/git/push.ts +4 -2
  141. package/src/procedures/git/rebase/abort.ts +19 -0
  142. package/src/procedures/git/rebase/continue.ts +25 -0
  143. package/src/procedures/git/rebase.ts +48 -0
  144. package/src/procedures/git/refs.ts +55 -0
  145. package/src/procedures/git/reset.ts +34 -0
  146. package/src/procedures/git/restore.ts +27 -0
  147. package/src/procedures/git/revParse.ts +24 -0
  148. package/src/procedures/git/run.ts +3 -0
  149. package/src/procedures/git/show.ts +57 -0
  150. package/src/procedures/git/status.ts +9 -17
  151. package/src/procedures/git/tag/add.ts +27 -0
  152. package/src/procedures/git/tag/delete.ts +22 -0
  153. package/src/procedures/git/tag/list.ts +36 -0
  154. package/src/procedures/git/worktree/add.ts +35 -0
  155. package/src/procedures/git/worktree/list.ts +48 -0
  156. package/src/procedures/git/worktree/remove.ts +21 -0
  157. package/src/types.ts +641 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * git.show procedure
3
+ *
4
+ * Show a commit, or a file at a commit (roadmap R40).
5
+ *
6
+ * The procedure first resolves the ref to its commit, so a tag gives its commit and not the text
7
+ * of the tag object. A merge commit gives its changes against its first parent.
8
+ */
9
+
10
+ import { parseNumstat } from "./diff.js";
11
+ import { git, type GitContext } from "./run.js";
12
+ import { commitOf } from "./refs.js";
13
+ import type { GitShowInput, GitShowOutput } from "../../types.js";
14
+
15
+ // Control characters that do not occur in a name, an email, a date or a subject
16
+ const FIELD = "\x1f";
17
+
18
+ /**
19
+ * Show a commit, or a file at a commit
20
+ */
21
+ export async function gitShow(input: GitShowInput, ctx: GitContext = {}): Promise<GitShowOutput> {
22
+ const ref = input.ref ?? "HEAD";
23
+ const run = { cwd: input.cwd, signal: ctx.signal };
24
+ const hash = await commitOf(ref, run);
25
+
26
+ // A file at the commit: `<hash>:<path>` starts with the hash, so a path that starts with "-"
27
+ // stays a path
28
+ if (input.path !== undefined) {
29
+ const content = await git(["show", `${hash}:${input.path}`], run);
30
+ return { ref, hash, path: input.path, content };
31
+ }
32
+
33
+ const format = `--format=${["%H", "%h", "%an", "%ae", "%ci", "%s", "%b"].join("%x1f")}`;
34
+ const [, shortHash = "", author = "", email = "", date = "", message = "", body = ""] = (
35
+ await git(["show", "-s", format, hash], run)
36
+ ).split(FIELD);
37
+
38
+ const changes = ["show", "--format=", "--diff-merges=first-parent"];
39
+ const files = parseNumstat(await git([...changes, "--numstat", "-z", hash], run));
40
+ const result: GitShowOutput = {
41
+ ref,
42
+ hash,
43
+ shortHash,
44
+ author,
45
+ email,
46
+ date,
47
+ message,
48
+ body: body.replace(/\n+$/, ""),
49
+ files,
50
+ additions: files.reduce((sum, file) => sum + file.additions, 0),
51
+ deletions: files.reduce((sum, file) => sum + file.deletions, 0),
52
+ };
53
+ if (!input.stat) {
54
+ result.diff = (await git([...changes, "--patch", hash], run)).replace(/^\n/, "");
55
+ }
56
+ return result;
57
+ }
@@ -10,23 +10,9 @@
10
10
  */
11
11
 
12
12
  import { git, zFields, type GitContext } from "./run.js";
13
+ import { classifyStatus as classify } from "./porcelain.js";
13
14
  import type { GitStatusInput, GitStatusOutput, GitStatusFile } from "../../types.js";
14
15
 
15
- /** The status of one entry, from its two status letters (index, then work tree). */
16
- function classify(xy: string): Pick<GitStatusFile, "status" | "staged"> {
17
- const index = xy[0];
18
- const worktree = xy[1];
19
- if (index === "A") return { status: "added", staged: true };
20
- if (index === "D") return { status: "deleted", staged: true };
21
- if (index === "R") return { status: "renamed", staged: true };
22
- if (index === "C") return { status: "copied", staged: true };
23
- if (index === "M" || index === "T") return { status: "modified", staged: true };
24
- if (worktree === "D") return { status: "deleted", staged: false };
25
- if (worktree === "A") return { status: "added", staged: false };
26
- if (worktree === "R") return { status: "renamed", staged: false };
27
- return { status: "modified", staged: false };
28
- }
29
-
30
16
  /** The text after the first `count` space-separated fields of a line. */
31
17
  function afterFields(line: string, count: number): string {
32
18
  let at = 0;
@@ -35,10 +21,16 @@ function afterFields(line: string, count: number): string {
35
21
  }
36
22
 
37
23
  /**
38
- * Get git status
24
+ * This function gives the git status. With `paths`, it gives only the files of those pathspecs.
25
+ * With `untrackedFiles`, git lists the untracked files in that mode: "all" lists each file of a
26
+ * new folder.
39
27
  */
40
28
  export async function gitStatus(input: GitStatusInput, ctx: GitContext = {}): Promise<GitStatusOutput> {
41
- const output = await git(["status", "--porcelain=v2", "--branch", "-z"], { cwd: input.cwd, signal: ctx.signal });
29
+ const args = ["status", "--porcelain=v2", "--branch", "-z"];
30
+ if (input.untrackedFiles !== undefined) args.push(`--untracked-files=${input.untrackedFiles}`);
31
+ // After "--", git reads each value as a pathspec, also a value that starts with "-"
32
+ if (input.paths !== undefined && input.paths.length > 0) args.push("--", ...input.paths);
33
+ const output = await git(args, { cwd: input.cwd, signal: ctx.signal });
42
34
 
43
35
  let branch = "HEAD";
44
36
  let ahead = 0;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * git.tag.add procedure
3
+ *
4
+ * Make a tag (roadmap R40). An existing tag is an error: the procedure never moves a tag.
5
+ */
6
+
7
+ import { gitArg } from "../args.js";
8
+ import { git, type GitContext } from "../run.js";
9
+ import { commitOf } from "../refs.js";
10
+ import type { GitTagAddInput, GitTagAddOutput } from "../../../types.js";
11
+
12
+ /**
13
+ * Make a tag
14
+ */
15
+ export async function gitTagAdd(input: GitTagAddInput, ctx: GitContext = {}): Promise<GitTagAddOutput> {
16
+ const { name, ref, message, cwd } = input;
17
+ const run = { cwd, signal: ctx.signal };
18
+
19
+ const args = ["tag"];
20
+ // With -m, git makes an annotated tag and does not start an editor
21
+ if (message !== undefined) args.push("-a", "-m", message);
22
+ args.push(gitArg("name", name));
23
+ if (ref) args.push(gitArg("ref", ref));
24
+ await git(args, run);
25
+
26
+ return { name, hash: await commitOf(`refs/tags/${name}`, run), annotated: message !== undefined };
27
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * git.tag.delete procedure
3
+ *
4
+ * Delete a tag (roadmap R40).
5
+ */
6
+
7
+ import { gitArg } from "../args.js";
8
+ import { git, type GitContext } from "../run.js";
9
+ import type { GitTagDeleteInput, GitTagDeleteOutput } from "../../../types.js";
10
+
11
+ /**
12
+ * Delete a tag
13
+ */
14
+ export async function gitTagDelete(input: GitTagDeleteInput, ctx: GitContext = {}): Promise<GitTagDeleteOutput> {
15
+ const name = gitArg("name", input.name);
16
+ const run = { cwd: input.cwd, signal: ctx.signal };
17
+
18
+ // The object of the tag before the deletion: a tag object or a commit
19
+ const hash = (await git(["rev-parse", "--verify", `refs/tags/${name}`], run)).trim();
20
+ await git(["tag", "-d", name], run);
21
+ return { name, hash };
22
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * git.tag.list procedure
3
+ *
4
+ * List the tags (roadmap R40).
5
+ */
6
+
7
+ import { gitArg } from "../args.js";
8
+ import { git, type GitContext } from "../run.js";
9
+ import type { GitTagInfo, GitTagListInput, GitTagListOutput } from "../../../types.js";
10
+
11
+ /**
12
+ * The fields of each tag, separated by NUL: the name, the type of the object, the commit of an
13
+ * annotated tag, the object, and the subject of the message.
14
+ */
15
+ const FORMAT = "--format=%(refname:strip=2)%00%(objecttype)%00%(*objectname)%00%(objectname)%00%(contents:subject)";
16
+
17
+ /**
18
+ * List the tags
19
+ */
20
+ export async function gitTagList(input: GitTagListInput, ctx: GitContext = {}): Promise<GitTagListOutput> {
21
+ const args = ["tag", "--list", FORMAT];
22
+ if (input.pattern) args.push(gitArg("pattern", input.pattern));
23
+
24
+ const output = await git(args, { cwd: input.cwd, signal: ctx.signal });
25
+ const tags: GitTagInfo[] = output
26
+ .split("\n")
27
+ .filter((line) => line.length > 0)
28
+ .map((line) => {
29
+ const [name = "", type = "", peeled = "", object = "", subject = ""] = line.split("\0");
30
+ const annotated = type === "tag";
31
+ const tag: GitTagInfo = { name, hash: annotated && peeled ? peeled : object, annotated };
32
+ if (annotated) tag.message = subject;
33
+ return tag;
34
+ });
35
+ return { tags };
36
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * git.worktree.add procedure
3
+ *
4
+ * Add a working tree (roadmap R40). Git refuses a folder that exists and is not empty. Without
5
+ * `ref`, `branch` and `detach`, git makes a branch with the name of the folder.
6
+ */
7
+
8
+ import { resolve } from "node:path";
9
+ import { gitArg } from "../args.js";
10
+ import { git, type GitContext } from "../run.js";
11
+ import { currentBranch, headHash } from "../refs.js";
12
+ import type { GitWorktreeAddInput, GitWorktreeAddOutput } from "../../../types.js";
13
+
14
+ /**
15
+ * Add a working tree
16
+ */
17
+ export async function gitWorktreeAdd(input: GitWorktreeAddInput, ctx: GitContext = {}): Promise<GitWorktreeAddOutput> {
18
+ const { ref, branch, detach, cwd } = input;
19
+ const path = resolve(cwd ?? process.cwd(), input.path);
20
+
21
+ const args = ["worktree", "add"];
22
+ if (branch) args.push("-b", gitArg("branch", branch));
23
+ if (detach) args.push("--detach");
24
+ // After "--", a path that starts with "-" stays a path
25
+ args.push("--", path);
26
+ if (ref) args.push(gitArg("ref", ref));
27
+ await git(args, { cwd, signal: ctx.signal });
28
+
29
+ // The new working tree gives its own HEAD
30
+ const inTree = { cwd: path, signal: ctx.signal };
31
+ const result: GitWorktreeAddOutput = { path, head: await headHash(inTree) };
32
+ const checkedOut = await currentBranch(inTree);
33
+ if (checkedOut !== "HEAD") result.branch = checkedOut;
34
+ return result;
35
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * git.worktree.list procedure
3
+ *
4
+ * List the working trees of the repository (roadmap R40). The main working tree is first.
5
+ */
6
+
7
+ import { resolve } from "node:path";
8
+ import { git, zFields, type GitContext } from "../run.js";
9
+ import type { GitWorktreeInfo, GitWorktreeListInput, GitWorktreeListOutput } from "../../../types.js";
10
+
11
+ /**
12
+ * This function reads the output of `git worktree list --porcelain -z`: the fields of one working
13
+ * tree, then an empty field.
14
+ */
15
+ export function parseWorktrees(output: string): GitWorktreeInfo[] {
16
+ const worktrees: GitWorktreeInfo[] = [];
17
+ let current: GitWorktreeInfo | undefined;
18
+ for (const field of zFields(output)) {
19
+ if (field === "") {
20
+ current = undefined;
21
+ continue;
22
+ }
23
+ const space = field.indexOf(" ");
24
+ const key = space < 0 ? field : field.slice(0, space);
25
+ const value = space < 0 ? "" : field.slice(space + 1);
26
+ if (key === "worktree") {
27
+ // Git gives "/" on Windows too: resolve gives the path of the platform
28
+ current = { path: resolve(value), detached: false, bare: false, locked: false, prunable: false };
29
+ worktrees.push(current);
30
+ } else if (current) {
31
+ if (key === "HEAD") current.head = value;
32
+ else if (key === "branch") current.branch = value.replace(/^refs\/heads\//, "");
33
+ else if (key === "detached") current.detached = true;
34
+ else if (key === "bare") current.bare = true;
35
+ else if (key === "locked") current.locked = true;
36
+ else if (key === "prunable") current.prunable = true;
37
+ }
38
+ }
39
+ return worktrees;
40
+ }
41
+
42
+ /**
43
+ * List the working trees of the repository
44
+ */
45
+ export async function gitWorktreeList(input: GitWorktreeListInput, ctx: GitContext = {}): Promise<GitWorktreeListOutput> {
46
+ const output = await git(["worktree", "list", "--porcelain", "-z"], { cwd: input.cwd, signal: ctx.signal });
47
+ return { worktrees: parseWorktrees(output) };
48
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * git.worktree.remove procedure
3
+ *
4
+ * Remove a working tree and its folder (roadmap R40). Git refuses a working tree with changes
5
+ * that are not committed, unless `force` is true. Then the changes are lost.
6
+ */
7
+
8
+ import { git, type GitContext } from "../run.js";
9
+ import type { GitWorktreeRemoveInput, GitWorktreeRemoveOutput } from "../../../types.js";
10
+
11
+ /**
12
+ * Remove a working tree
13
+ */
14
+ export async function gitWorktreeRemove(input: GitWorktreeRemoveInput, ctx: GitContext = {}): Promise<GitWorktreeRemoveOutput> {
15
+ const args = ["worktree", "remove"];
16
+ if (input.force) args.push("--force");
17
+ // After "--", a path that starts with "-" stays a path
18
+ args.push("--", input.path);
19
+ await git(args, { cwd: input.cwd, signal: ctx.signal });
20
+ return { path: input.path };
21
+ }