git-stack-cli 0.8.5 → 0.8.7
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.
- package/dist/app/LocalMergeRebase.js +5 -5
- package/dist/app/ManualRebase.js +1 -1
- package/dist/app/SelectCommitRanges.js +1 -1
- package/dist/core/CommitMetadata.js +6 -6
- package/dist/core/Metadata.js +6 -6
- package/dist/core/Metadata.test.js +34 -0
- package/dist/core/StackSummaryTable.js +65 -21
- package/dist/core/StackSummaryTable.test.js +134 -0
- package/package.json +8 -5
|
@@ -49,7 +49,7 @@ async function run() {
|
|
|
49
49
|
if (merged_pr) {
|
|
50
50
|
if (actions.isDebug()) {
|
|
51
51
|
actions.output(React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, { color: colors.yellow, wrap: "truncate-end" }), message: "Dropping {commit_message} {pr_status}", values: {
|
|
52
|
-
commit_message: React.createElement(Brackets, null, commit.
|
|
52
|
+
commit_message: React.createElement(Brackets, null, commit.subject_line),
|
|
53
53
|
pr_status: React.createElement(Parens, null, "MERGED"),
|
|
54
54
|
} }));
|
|
55
55
|
}
|
|
@@ -58,7 +58,7 @@ async function run() {
|
|
|
58
58
|
// cherry-pick and amend commits one by one
|
|
59
59
|
if (actions.isDebug()) {
|
|
60
60
|
actions.output(React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, { color: colors.yellow, wrap: "truncate-end" }), message: "Picking {commit_message}", values: {
|
|
61
|
-
commit_message: React.createElement(Brackets, null, commit.
|
|
61
|
+
commit_message: React.createElement(Brackets, null, commit.subject_line),
|
|
62
62
|
} }));
|
|
63
63
|
}
|
|
64
64
|
// ensure clean base to avoid conflicts when applying patch
|
|
@@ -71,10 +71,10 @@ async function run() {
|
|
|
71
71
|
await cli(`git add --all`);
|
|
72
72
|
let new_message;
|
|
73
73
|
if (commit.branch_id) {
|
|
74
|
-
new_message = await Metadata.write(commit.
|
|
74
|
+
new_message = await Metadata.write(commit.full_message, commit.branch_id);
|
|
75
75
|
}
|
|
76
76
|
else {
|
|
77
|
-
new_message = commit.
|
|
77
|
+
new_message = commit.full_message;
|
|
78
78
|
}
|
|
79
79
|
const git_commit_comand = [`git commit -m "${new_message}"`];
|
|
80
80
|
if (argv.verify === false) {
|
|
@@ -88,7 +88,7 @@ async function run() {
|
|
|
88
88
|
} }));
|
|
89
89
|
}
|
|
90
90
|
// missing PR, clear branch id from commit
|
|
91
|
-
const new_message = await Metadata.remove(commit.
|
|
91
|
+
const new_message = await Metadata.remove(commit.full_message);
|
|
92
92
|
await cli(`git commit --amend -m "${new_message}"`);
|
|
93
93
|
}
|
|
94
94
|
}
|
package/dist/app/ManualRebase.js
CHANGED
|
@@ -76,7 +76,7 @@ async function run(props) {
|
|
|
76
76
|
await cli(`rm ${PATCH_FILE}`);
|
|
77
77
|
// add all changes to stage
|
|
78
78
|
await cli(`git add --all`);
|
|
79
|
-
const new_message = await Metadata.write(commit.
|
|
79
|
+
const new_message = await Metadata.write(commit.full_message, group.id);
|
|
80
80
|
const git_commit_comand = [`git commit -m "${new_message}"`];
|
|
81
81
|
if (argv.verify === false) {
|
|
82
82
|
git_commit_comand.push("--no-verify");
|
|
@@ -138,17 +138,17 @@ async function get_commit_list() {
|
|
|
138
138
|
return commit_metadata_list;
|
|
139
139
|
}
|
|
140
140
|
export async function commit(sha) {
|
|
141
|
-
const
|
|
142
|
-
const branch_id = await Metadata.read(
|
|
143
|
-
const
|
|
141
|
+
const full_message = (await cli(`git show -s --format=%B ${sha}`)).stdout;
|
|
142
|
+
const branch_id = await Metadata.read(full_message);
|
|
143
|
+
const subject_line = get_subject_line(full_message);
|
|
144
144
|
return {
|
|
145
145
|
sha,
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
full_message,
|
|
147
|
+
subject_line,
|
|
148
148
|
branch_id,
|
|
149
149
|
};
|
|
150
150
|
}
|
|
151
|
-
function
|
|
151
|
+
function get_subject_line(message) {
|
|
152
152
|
const line_list = lines(message);
|
|
153
153
|
const first_line = line_list[0];
|
|
154
154
|
return Metadata.remove(first_line);
|
package/dist/core/Metadata.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { invariant } from "../core/invariant.js";
|
|
2
2
|
import { safe_quote } from "../core/safe_quote.js";
|
|
3
|
-
export function write(message,
|
|
3
|
+
export function write(message, stack_id) {
|
|
4
4
|
let result = message;
|
|
5
5
|
// escape double-quote for cli
|
|
6
6
|
result = safe_quote(result);
|
|
7
7
|
// remove any previous metadata lines
|
|
8
8
|
result = remove(result);
|
|
9
|
-
const line_list = [result, "", TEMPLATE.
|
|
9
|
+
const line_list = [result, "", TEMPLATE.stack_id(stack_id)];
|
|
10
10
|
const new_message = line_list.join("\n");
|
|
11
11
|
return new_message;
|
|
12
12
|
}
|
|
13
13
|
export function read(message) {
|
|
14
|
-
const match = message.match(RE.
|
|
14
|
+
const match = message.match(RE.stack_id);
|
|
15
15
|
if (!match?.groups) {
|
|
16
16
|
return null;
|
|
17
17
|
}
|
|
@@ -22,15 +22,15 @@ export function read(message) {
|
|
|
22
22
|
export function remove(message) {
|
|
23
23
|
let result = message;
|
|
24
24
|
// remove metadata
|
|
25
|
-
result = result.replace(new RegExp(RE.
|
|
25
|
+
result = result.replace(new RegExp(RE.stack_id, "gmi"), "");
|
|
26
26
|
result = result.trimEnd();
|
|
27
27
|
return result;
|
|
28
28
|
}
|
|
29
29
|
const TEMPLATE = {
|
|
30
|
-
|
|
30
|
+
stack_id(id) {
|
|
31
31
|
return `git-stack-id: ${id}`;
|
|
32
32
|
},
|
|
33
33
|
};
|
|
34
34
|
const RE = {
|
|
35
|
-
|
|
35
|
+
stack_id: new RegExp(TEMPLATE.stack_id("(?<id>[a-z0-9-+=]+)"), "i"),
|
|
36
36
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { test, expect } from "bun:test";
|
|
2
|
+
import * as Metadata from "./Metadata.js";
|
|
3
|
+
test("read handles bulleted lists", () => {
|
|
4
|
+
const body = [
|
|
5
|
+
"[feat] implement various features",
|
|
6
|
+
"",
|
|
7
|
+
"- keyboard modality escape key",
|
|
8
|
+
"- centralize settings",
|
|
9
|
+
"- move logic inside if branch",
|
|
10
|
+
"",
|
|
11
|
+
"git-stack-id: DdKIFyufW",
|
|
12
|
+
].join("\n");
|
|
13
|
+
expect(Metadata.read(body)).toEqual("DdKIFyufW");
|
|
14
|
+
});
|
|
15
|
+
test("write handles bulleted lists", () => {
|
|
16
|
+
const body = [
|
|
17
|
+
"[feat] implement various features",
|
|
18
|
+
"",
|
|
19
|
+
"- keyboard modality escape key",
|
|
20
|
+
"- centralize settings",
|
|
21
|
+
"- move logic inside if branch",
|
|
22
|
+
"",
|
|
23
|
+
"git-stack-id: DdKIFyufW",
|
|
24
|
+
].join("\n");
|
|
25
|
+
expect(Metadata.write(body, "abcd1234")).toEqual([
|
|
26
|
+
"[feat] implement various features",
|
|
27
|
+
"",
|
|
28
|
+
"- keyboard modality escape key",
|
|
29
|
+
"- centralize settings",
|
|
30
|
+
"- move logic inside if branch",
|
|
31
|
+
"",
|
|
32
|
+
"git-stack-id: abcd1234",
|
|
33
|
+
].join("\n"));
|
|
34
|
+
});
|
|
@@ -1,28 +1,9 @@
|
|
|
1
1
|
export function write(args) {
|
|
2
|
-
const
|
|
3
|
-
const digits = String(args.pr_url_list.length).length;
|
|
4
|
-
for (let i = 0; i < args.pr_url_list.length; i++) {
|
|
5
|
-
const pr_url = args.pr_url_list[i];
|
|
6
|
-
if (pr_url) {
|
|
7
|
-
const selected = args.selected_url === pr_url;
|
|
8
|
-
const icon = selected ? "👉" : "⏳";
|
|
9
|
-
const num = String(i + 1).padStart(digits, "0");
|
|
10
|
-
stack_list.push(`- ${icon} \`${num}\` ${pr_url}`);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
// reverse order of pr list to match the order of git stack
|
|
14
|
-
stack_list.reverse();
|
|
15
|
-
let stack_table;
|
|
16
|
-
if (stack_list.length > 1) {
|
|
17
|
-
stack_table = TEMPLATE.stack_table(["", ...stack_list, "", ""].join("\n"));
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
stack_table = "";
|
|
21
|
-
}
|
|
2
|
+
const stack_table = table(args);
|
|
22
3
|
let result = args.body;
|
|
23
4
|
if (RE.stack_table.test(result)) {
|
|
24
5
|
// replace stack table
|
|
25
|
-
result = result.replace(
|
|
6
|
+
result = result.replace(RE.stack_table, stack_table);
|
|
26
7
|
}
|
|
27
8
|
else {
|
|
28
9
|
// append stack table
|
|
@@ -31,12 +12,75 @@ export function write(args) {
|
|
|
31
12
|
result = result.trimEnd();
|
|
32
13
|
return result;
|
|
33
14
|
}
|
|
15
|
+
export function table(args) {
|
|
16
|
+
const stack_pr_url_list = [...args.pr_url_list];
|
|
17
|
+
const old_stack = parse(args.body);
|
|
18
|
+
// remove existing stack pr urls from the old stack pr urls
|
|
19
|
+
for (const pr_url of stack_pr_url_list) {
|
|
20
|
+
old_stack.delete(pr_url);
|
|
21
|
+
}
|
|
22
|
+
// add remaining old stack pr urls to the front of stack pr url list
|
|
23
|
+
for (const pr_url of old_stack.keys()) {
|
|
24
|
+
stack_pr_url_list.unshift(pr_url);
|
|
25
|
+
}
|
|
26
|
+
const stack_list = [];
|
|
27
|
+
const num_digits = String(stack_pr_url_list.length).length;
|
|
28
|
+
for (let i = 0; i < stack_pr_url_list.length; i++) {
|
|
29
|
+
const pr_url = stack_pr_url_list[i];
|
|
30
|
+
const selected = args.selected_url === pr_url;
|
|
31
|
+
let icon;
|
|
32
|
+
if (old_stack.has(pr_url)) {
|
|
33
|
+
icon = "✅";
|
|
34
|
+
}
|
|
35
|
+
else if (selected) {
|
|
36
|
+
icon = "👉";
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
icon = "⏳";
|
|
40
|
+
}
|
|
41
|
+
const num = String(i + 1).padStart(num_digits, "0");
|
|
42
|
+
stack_list.push(TEMPLATE.row({ icon, num, pr_url }));
|
|
43
|
+
}
|
|
44
|
+
if (!stack_list.length) {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
// reverse order of pr list to match the order of git stack
|
|
48
|
+
stack_list.reverse();
|
|
49
|
+
return TEMPLATE.stack_table(["", ...stack_list, "", ""].join("\n"));
|
|
50
|
+
}
|
|
51
|
+
export function parse(body) {
|
|
52
|
+
const stack_table_match = body.match(RE.stack_table);
|
|
53
|
+
if (!stack_table_match?.groups) {
|
|
54
|
+
return new Map();
|
|
55
|
+
}
|
|
56
|
+
const rows_string = stack_table_match.groups["rows"];
|
|
57
|
+
const row_list = rows_string.split("\n");
|
|
58
|
+
const result = new Map();
|
|
59
|
+
for (const row of row_list) {
|
|
60
|
+
const row_match = row.match(RE.row);
|
|
61
|
+
const parsed_row = row_match?.groups;
|
|
62
|
+
if (!parsed_row) {
|
|
63
|
+
// skip invalid row
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
result.set(parsed_row.pr_url, parsed_row);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
34
70
|
const TEMPLATE = {
|
|
35
71
|
stack_table(rows) {
|
|
36
72
|
return `#### git stack${rows}`;
|
|
37
73
|
},
|
|
74
|
+
row(args) {
|
|
75
|
+
return `- ${args.icon} \`${args.num}\` ${args.pr_url}`;
|
|
76
|
+
},
|
|
38
77
|
};
|
|
39
78
|
const RE = {
|
|
40
79
|
// https://regex101.com/r/kqB9Ft/1
|
|
41
80
|
stack_table: new RegExp(TEMPLATE.stack_table("\\s+(?<rows>(?:- [^\r^\n]*(?:[\r\n]+)?)+)")),
|
|
81
|
+
row: new RegExp(TEMPLATE.row({
|
|
82
|
+
icon: "(?<icon>.+)",
|
|
83
|
+
num: "(?<num>\\d+)",
|
|
84
|
+
pr_url: "(?<pr_url>.+)",
|
|
85
|
+
})),
|
|
42
86
|
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { test, expect } from "bun:test";
|
|
2
|
+
import * as StackSummaryTable from "./StackSummaryTable.js";
|
|
3
|
+
test("blank", () => {
|
|
4
|
+
const output = StackSummaryTable.write({
|
|
5
|
+
body: "",
|
|
6
|
+
pr_url_list: [],
|
|
7
|
+
selected_url: "",
|
|
8
|
+
});
|
|
9
|
+
expect(output).toBe("");
|
|
10
|
+
});
|
|
11
|
+
test("no prs does not modify body", () => {
|
|
12
|
+
const args = {
|
|
13
|
+
body: [
|
|
14
|
+
"## Problem,",
|
|
15
|
+
"",
|
|
16
|
+
",Description of the problem,",
|
|
17
|
+
"",
|
|
18
|
+
",## Solution,",
|
|
19
|
+
"",
|
|
20
|
+
",Solved problem by doing x, y, z.",
|
|
21
|
+
].join("\n"),
|
|
22
|
+
pr_url_list: [],
|
|
23
|
+
selected_url: "",
|
|
24
|
+
};
|
|
25
|
+
const output = StackSummaryTable.write(args);
|
|
26
|
+
expect(output).toBe(args.body);
|
|
27
|
+
});
|
|
28
|
+
test("handles bulleted lists", () => {
|
|
29
|
+
const body = [
|
|
30
|
+
"## Problem",
|
|
31
|
+
"",
|
|
32
|
+
"Description of the problem",
|
|
33
|
+
"",
|
|
34
|
+
"## Solution",
|
|
35
|
+
"",
|
|
36
|
+
"- keyboard modality escape key",
|
|
37
|
+
"- centralize settings",
|
|
38
|
+
"- move logic inside if branch",
|
|
39
|
+
].join("\n");
|
|
40
|
+
const args = {
|
|
41
|
+
body,
|
|
42
|
+
pr_url_list: [],
|
|
43
|
+
selected_url: "",
|
|
44
|
+
};
|
|
45
|
+
const output = StackSummaryTable.write(args);
|
|
46
|
+
expect(output).toBe(args.body);
|
|
47
|
+
});
|
|
48
|
+
test("builds list of prs with selected emoji", () => {
|
|
49
|
+
const args = {
|
|
50
|
+
body: [
|
|
51
|
+
"## Problem,",
|
|
52
|
+
"",
|
|
53
|
+
",Description of the problem,",
|
|
54
|
+
"",
|
|
55
|
+
",## Solution,",
|
|
56
|
+
"",
|
|
57
|
+
",Solved problem by doing x, y, z.",
|
|
58
|
+
].join("\n"),
|
|
59
|
+
pr_url_list: [
|
|
60
|
+
"https://github.com/magus/git-multi-diff-playground/pull/43",
|
|
61
|
+
"https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
62
|
+
],
|
|
63
|
+
selected_url: "https://github.com/magus/git-multi-diff-playground/pull/43",
|
|
64
|
+
};
|
|
65
|
+
const output = StackSummaryTable.write(args);
|
|
66
|
+
expect(output.split("\n")).toEqual([
|
|
67
|
+
...args.body.split("\n"),
|
|
68
|
+
"",
|
|
69
|
+
"#### git stack",
|
|
70
|
+
"- ⏳ `2` https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
71
|
+
"- 👉 `1` https://github.com/magus/git-multi-diff-playground/pull/43",
|
|
72
|
+
]);
|
|
73
|
+
});
|
|
74
|
+
test("can parse stack table from body", () => {
|
|
75
|
+
const body_line_list = [
|
|
76
|
+
"",
|
|
77
|
+
"",
|
|
78
|
+
"#### git stack",
|
|
79
|
+
"- invalid line that will be dropped",
|
|
80
|
+
"- ⏳ `2` https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
81
|
+
"- 👉 `1` https://github.com/magus/git-multi-diff-playground/pull/43",
|
|
82
|
+
];
|
|
83
|
+
const parsed = StackSummaryTable.parse(body_line_list.join("\n"));
|
|
84
|
+
expect(Array.from(parsed.entries())).toEqual([
|
|
85
|
+
[
|
|
86
|
+
"https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
87
|
+
{
|
|
88
|
+
icon: "⏳",
|
|
89
|
+
num: "2",
|
|
90
|
+
pr_url: "https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"https://github.com/magus/git-multi-diff-playground/pull/43",
|
|
95
|
+
{
|
|
96
|
+
icon: "👉",
|
|
97
|
+
num: "1",
|
|
98
|
+
pr_url: "https://github.com/magus/git-multi-diff-playground/pull/43",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
]);
|
|
102
|
+
});
|
|
103
|
+
test("persists removed pr urls from previous stack table", () => {
|
|
104
|
+
const args = {
|
|
105
|
+
body: [
|
|
106
|
+
"Summary of problem",
|
|
107
|
+
"",
|
|
108
|
+
"#### git stack",
|
|
109
|
+
"- 👉 `3` https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
110
|
+
"- ⏳ `2` https://github.com/magus/git-multi-diff-playground/pull/44",
|
|
111
|
+
"- ⏳ `1` https://github.com/magus/git-multi-diff-playground/pull/43",
|
|
112
|
+
].join("\n"),
|
|
113
|
+
pr_url_list: [
|
|
114
|
+
"https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
115
|
+
"https://github.com/magus/git-multi-diff-playground/pull/54",
|
|
116
|
+
"https://github.com/magus/git-multi-diff-playground/pull/61",
|
|
117
|
+
],
|
|
118
|
+
selected_url: "https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
119
|
+
};
|
|
120
|
+
const output = StackSummaryTable.write(args);
|
|
121
|
+
expect(output.split("\n")).toEqual([
|
|
122
|
+
"Summary of problem",
|
|
123
|
+
"",
|
|
124
|
+
"#### git stack",
|
|
125
|
+
"- ⏳ `5` https://github.com/magus/git-multi-diff-playground/pull/61",
|
|
126
|
+
"- ⏳ `4` https://github.com/magus/git-multi-diff-playground/pull/54",
|
|
127
|
+
"- 👉 `3` https://github.com/magus/git-multi-diff-playground/pull/47",
|
|
128
|
+
"- ✅ `2` https://github.com/magus/git-multi-diff-playground/pull/44",
|
|
129
|
+
"- ✅ `1` https://github.com/magus/git-multi-diff-playground/pull/43",
|
|
130
|
+
]);
|
|
131
|
+
// run again on the output to make sure it doesn't change
|
|
132
|
+
const rerun_output = StackSummaryTable.write({ ...args, body: output });
|
|
133
|
+
expect(rerun_output).toBe(output);
|
|
134
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-stack-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "magus",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,12 +19,14 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsc",
|
|
21
21
|
"dev": "tsc --watch",
|
|
22
|
-
"lint:check": "eslint .",
|
|
22
|
+
"lint:check": "eslint . --cache",
|
|
23
23
|
"lint": "npm run lint:check -- --fix",
|
|
24
|
-
"prettier:check": "prettier ./src --check",
|
|
24
|
+
"prettier:check": "prettier ./src --check --cache",
|
|
25
25
|
"prettier": "npm run prettier:check -- --write",
|
|
26
|
-
"test": "
|
|
27
|
-
"
|
|
26
|
+
"test": "bun test src",
|
|
27
|
+
"test:watch": "npm run test -- --watch",
|
|
28
|
+
"test:all": "npm run prettier:check && npm run lint:check && npm run build",
|
|
29
|
+
"prepublishOnly": "npm run test:all"
|
|
28
30
|
},
|
|
29
31
|
"dependencies": {
|
|
30
32
|
"chalk": "^5.3.0",
|
|
@@ -42,6 +44,7 @@
|
|
|
42
44
|
"@types/yargs": "^17.0.29",
|
|
43
45
|
"@typescript-eslint/eslint-plugin": "^6.9.0",
|
|
44
46
|
"@typescript-eslint/parser": "^6.9.0",
|
|
47
|
+
"bun-types": "^1.0.21",
|
|
45
48
|
"eslint": "^8.52.0",
|
|
46
49
|
"eslint-import-resolver-typescript": "^3.6.1",
|
|
47
50
|
"eslint-plugin-import": "^2.29.0",
|