git-stack-cli 1.8.3 → 1.10.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.
@@ -1,4 +1,10 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+
1
5
  import * as Metadata from "~/core/Metadata";
6
+ import { cli } from "~/core/cli";
7
+ import { invariant } from "~/core/invariant";
2
8
 
3
9
  import type * as CommitMetadata from "~/core/CommitMetadata";
4
10
 
@@ -47,7 +53,7 @@ import type * as CommitMetadata from "~/core/CommitMetadata";
47
53
  // apple sweet
48
54
  //
49
55
  export function GitReviseTodo(args: Args): string {
50
- const entry_list = [];
56
+ const commit_list = [];
51
57
 
52
58
  const group_list = args.commit_range.group_list;
53
59
 
@@ -55,29 +61,84 @@ export function GitReviseTodo(args: Args): string {
55
61
  const group = group_list[i];
56
62
 
57
63
  for (const commit of group.commits) {
58
- // update git commit message with stack id
59
- const metadata = { id: group.id, title: group.title };
60
- const unsafe_message_with_id = Metadata.write(
61
- commit.full_message,
62
- metadata
63
- );
64
- const message_with_id = unsafe_message_with_id.replace(/"/g, '\\"');
65
-
66
- // get first 12 characters of commit sha
67
- const sha = commit.sha.slice(0, 12);
68
-
69
- // generate git revise entry
70
- const entry_lines = [`++ pick ${sha}`, message_with_id];
71
- const entry = entry_lines.join("\n");
72
-
73
- entry_list.push(entry);
64
+ commit_list.push(commit);
74
65
  }
75
66
  }
76
67
 
77
- const todo = entry_list.join("\n\n");
68
+ const todo = GitReviseTodo.todo({ commit_list });
78
69
  return todo;
79
70
  }
80
71
 
72
+ type CommitListArgs = {
73
+ commit_list: CommitMetadata.CommitRange["commit_list"];
74
+ };
75
+
76
+ GitReviseTodo.todo = function todo(args: CommitListArgs) {
77
+ const entry_list = [];
78
+
79
+ for (const commit of args.commit_list) {
80
+ // update git commit message with stack id
81
+ const id = commit.branch_id;
82
+ const title = commit.title;
83
+
84
+ invariant(id, "commit.branch_id must exist");
85
+ invariant(title, "commit.title must exist");
86
+
87
+ const metadata = { id, title };
88
+
89
+ const unsafe_message_with_id = Metadata.write(
90
+ commit.full_message,
91
+ metadata
92
+ );
93
+
94
+ let message_with_id = unsafe_message_with_id;
95
+
96
+ message_with_id = message_with_id.replace(/[^\\]"/g, '\\"');
97
+
98
+ // get first 12 characters of commit sha
99
+ const sha = commit.sha.slice(0, 12);
100
+
101
+ // generate git revise entry
102
+ const entry_lines = [`++ pick ${sha}`, message_with_id];
103
+ const entry = entry_lines.join("\n");
104
+
105
+ entry_list.push(entry);
106
+ }
107
+
108
+ const todo = entry_list.join("\n\n");
109
+ return todo;
110
+ };
111
+
112
+ GitReviseTodo.execute = async function grt_execute(args: ExecuteArgs) {
113
+ // generate temporary directory and drop sequence editor script
114
+ const tmp_git_sequence_editor_path = path.join(
115
+ os.tmpdir(),
116
+ "git-sequence-editor.sh"
117
+ );
118
+
119
+ // ensure script is executable
120
+ fs.chmodSync(tmp_git_sequence_editor_path, "755");
121
+
122
+ const git_revise_todo = GitReviseTodo(args);
123
+
124
+ // execute cli with temporary git sequence editor script
125
+ // revise from merge base to pick correct commits
126
+ const command = [
127
+ `GIT_EDITOR="${tmp_git_sequence_editor_path}"`,
128
+ `GIT_REVISE_TODO="${git_revise_todo}"`,
129
+ `git`,
130
+ `revise --edit -i ${args.rebase_merge_base}`,
131
+ ];
132
+
133
+ await cli(command, { stdio: ["ignore", "ignore", "ignore"] });
134
+ };
135
+
136
+ type ExecuteArgs = {
137
+ rebase_group_index: number;
138
+ rebase_merge_base: string;
139
+ commit_range: CommitMetadata.CommitRange;
140
+ };
141
+
81
142
  type Args = {
82
143
  rebase_group_index: number;
83
144
  commit_range: CommitMetadata.CommitRange;
@@ -52,6 +52,91 @@ test("write handles bulleted lists", () => {
52
52
  );
53
53
  });
54
54
 
55
+ test("read handles slashes in branch name", () => {
56
+ const body = [
57
+ "[fix] slash in branch name",
58
+ "",
59
+ "git-stack-id: dev/noah/fix-slash-branch",
60
+ "git-stack-title: fix slash branch",
61
+ ].join("\n");
62
+
63
+ const metadata = Metadata.read(body);
64
+
65
+ expect(metadata).toEqual({
66
+ id: "dev/noah/fix-slash-branch",
67
+ title: "fix slash branch",
68
+ });
69
+ });
70
+
71
+ test("write handles bulleted lists", () => {
72
+ const body = [
73
+ "[feat] implement various features",
74
+ "",
75
+ "- keyboard modality escape key",
76
+ "- centralize settings",
77
+ "- move logic inside if branch",
78
+ "",
79
+ "git-stack-id: DdKIFyufW",
80
+ ].join("\n");
81
+
82
+ const metadata = {
83
+ id: "fix-slash-branch",
84
+ title: "fix slash branch",
85
+ };
86
+
87
+ expect(Metadata.write(body, metadata)).toEqual(
88
+ [
89
+ "[feat] implement various features",
90
+ "",
91
+ "- keyboard modality escape key",
92
+ "- centralize settings",
93
+ "- move logic inside if branch",
94
+ "",
95
+ "git-stack-id: fix-slash-branch",
96
+ "git-stack-title: fix slash branch",
97
+ ].join("\n")
98
+ );
99
+ });
100
+
101
+ test("read handles double quotes", () => {
102
+ const body = [
103
+ 'Revert "[abc / 123] subject (#1234)"',
104
+ "",
105
+ "git-stack-id: dev/noah/fix-slash-branch",
106
+ 'git-stack-title: Revert \\"[abc / 123] subject (#1234)\\"',
107
+ ].join("\n");
108
+
109
+ const metadata = Metadata.read(body);
110
+
111
+ expect(metadata).toEqual({
112
+ id: "dev/noah/fix-slash-branch",
113
+ title: 'Revert \\"[abc / 123] subject (#1234)\\"',
114
+ });
115
+ });
116
+
117
+ test("write handles double quotes", () => {
118
+ const body = [
119
+ // force line break
120
+ 'Revert "[abc / 123] subject (#1234)"',
121
+ "",
122
+ ].join("\n");
123
+
124
+ const metadata = {
125
+ id: "abc123",
126
+ title: 'Revert "[abc / 123] subject (#1234)"',
127
+ };
128
+
129
+ expect(Metadata.write(body, metadata)).toEqual(
130
+ [
131
+ // force line break
132
+ 'Revert \\"[abc / 123] subject (#1234)\\"',
133
+ "",
134
+ "git-stack-id: abc123",
135
+ 'git-stack-title: Revert \\"[abc / 123] subject (#1234)\\"',
136
+ ].join("\n")
137
+ );
138
+ });
139
+
55
140
  test("removes metadata", () => {
56
141
  const body = [
57
142
  "[feat] implement various features",
@@ -14,9 +14,6 @@ type OutputMetadataValues = {
14
14
  export function write(message: string, values: InputMetadataValues) {
15
15
  let result = message;
16
16
 
17
- // escape double-quote for cli
18
- result = safe_quote(result);
19
-
20
17
  // remove any previous metadata lines
21
18
  result = remove(result);
22
19
 
@@ -26,7 +23,9 @@ export function write(message: string, values: InputMetadataValues) {
26
23
  line_list.push(TEMPLATE.group_title(values.title));
27
24
  }
28
25
 
29
- const new_message = line_list.join("\n");
26
+ let new_message = line_list.join("\n");
27
+
28
+ new_message = safe_quote(new_message);
30
29
 
31
30
  return new_message;
32
31
  }
@@ -73,6 +72,7 @@ const TEMPLATE = {
73
72
  };
74
73
 
75
74
  const RE = {
76
- stack_id: new RegExp(TEMPLATE.stack_id("(?<id>[a-z0-9-+=]+)"), "i"),
75
+ // https://regex101.com/r/wLmGVq/1
76
+ stack_id: new RegExp(`${TEMPLATE.stack_id("(?<id>[^\\s]+)")}`, "i"),
77
77
  group_title: new RegExp(TEMPLATE.group_title("(?<title>[^\\n^\\r]+)"), "i"),
78
78
  };
@@ -0,0 +1,5 @@
1
+ import { short_id } from "~/core/short_id";
2
+
3
+ export function gs_short_id() {
4
+ return `gs-${short_id()}`;
5
+ }
package/src/index.tsx CHANGED
@@ -14,6 +14,7 @@ command()
14
14
 
15
15
  Store.setState((state) => {
16
16
  state.ink = ink;
17
+ state.process_argv = process.argv;
17
18
  state.argv = argv;
18
19
  state.cwd = process.cwd();
19
20
  });