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.
- package/README.md +17 -17
- package/dist/cjs/index.cjs +3319 -420
- package/package.json +3 -1
- package/src/app/App.tsx +33 -13
- package/src/app/CherryPickCheck.tsx +3 -3
- package/src/app/DetectInitialPR.tsx +189 -0
- package/src/app/DirtyCheck.tsx +3 -3
- package/src/app/FormatText.tsx +1 -1
- package/src/app/GatherMetadata.tsx +2 -2
- package/src/app/LocalCommitStatus.tsx +2 -3
- package/src/app/ManualRebase.tsx +25 -16
- package/src/app/PreManualRebase.tsx +0 -1
- package/src/app/RebaseCheck.tsx +3 -3
- package/src/app/SelectCommitRanges.tsx +2 -2
- package/src/app/Store.tsx +2 -0
- package/src/command.ts +133 -93
- package/src/commands/Fixup.tsx +121 -0
- package/src/commands/Log.tsx +72 -0
- package/src/core/CommitMetadata.ts +1 -1
- package/src/core/GitReviseTodo.test.ts +390 -398
- package/src/core/GitReviseTodo.ts +79 -18
- package/src/core/Metadata.test.ts +85 -0
- package/src/core/Metadata.ts +5 -5
- package/src/core/gs_short_id.ts +5 -0
- package/src/index.tsx +1 -0
|
@@ -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
|
|
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
|
-
|
|
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 =
|
|
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",
|
package/src/core/Metadata.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
};
|