git-stack-cli 2.5.2 → 2.6.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/dist/js/index.js +259 -142
- package/package.json +2 -2
- package/src/app/App.tsx +11 -4
- package/src/app/AutoUpdate.tsx +19 -5
- package/src/app/ManualRebase.tsx +5 -0
- package/src/app/MultiSelect.tsx +7 -4
- package/src/app/SelectCommitRanges.tsx +232 -117
- package/src/app/SyncGithub.tsx +9 -4
- package/src/app/TextInput.tsx +8 -1
- package/src/command.ts +6 -7
- package/src/commands/Update.tsx +15 -0
- package/src/core/CommitMetadata.ts +6 -5
- package/src/core/GitReviseTodo.test.ts +197 -2
- package/src/core/GitReviseTodo.ts +20 -17
- package/src/core/Metadata.test.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-stack-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "magus",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"test": "bun test",
|
|
37
37
|
"test:watch": "pnpm run test --watch",
|
|
38
38
|
"test:types": "tsc",
|
|
39
|
-
"test:all": "pnpm run prettier:check && pnpm run lint:check && pnpm run test:types",
|
|
39
|
+
"test:all": "pnpm run prettier:check && pnpm run lint:check && pnpm run test:types && pnpm run test",
|
|
40
40
|
"prepublishOnly": "bun run scripts/npm-prepublishOnly.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
package/src/app/App.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import { VerboseDebugInfo } from "~/app/VerboseDebugInfo";
|
|
|
19
19
|
import { Fixup } from "~/commands/Fixup";
|
|
20
20
|
import { Log } from "~/commands/Log";
|
|
21
21
|
import { Rebase } from "~/commands/Rebase";
|
|
22
|
+
import { Update } from "~/commands/Update";
|
|
22
23
|
import { ErrorBoundary } from "~/components/ErrorBoundary";
|
|
23
24
|
import { ExitingGate } from "~/components/ExitingGate";
|
|
24
25
|
|
|
@@ -28,7 +29,7 @@ export function App() {
|
|
|
28
29
|
const ink = Store.useState((state) => state.ink);
|
|
29
30
|
const argv = Store.useState((state) => state.argv);
|
|
30
31
|
|
|
31
|
-
if (!ink || !argv) {
|
|
32
|
+
if (!ink || !argv || !argv.$0) {
|
|
32
33
|
return null;
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -42,6 +43,9 @@ export function App() {
|
|
|
42
43
|
// </React.Fragment>
|
|
43
44
|
// );
|
|
44
45
|
|
|
46
|
+
const positional_list = new Set(argv["_"]);
|
|
47
|
+
const is_update = positional_list.has("update") || positional_list.has("upgrade");
|
|
48
|
+
|
|
45
49
|
return (
|
|
46
50
|
<Providers>
|
|
47
51
|
<ErrorBoundary>
|
|
@@ -51,11 +55,12 @@ export function App() {
|
|
|
51
55
|
<ExitingGate>
|
|
52
56
|
<AutoUpdate
|
|
53
57
|
name="git-stack-cli"
|
|
54
|
-
verbose={argv.verbose
|
|
55
|
-
|
|
58
|
+
verbose={argv.verbose}
|
|
59
|
+
force={is_update}
|
|
60
|
+
timeoutMs={is_update ? 30 * 1000 : 2 * 1000}
|
|
56
61
|
onOutput={actions.output}
|
|
57
62
|
onDone={() => {
|
|
58
|
-
if (
|
|
63
|
+
if (is_update) {
|
|
59
64
|
actions.exit(0);
|
|
60
65
|
}
|
|
61
66
|
}}
|
|
@@ -84,6 +89,8 @@ function MaybeMain() {
|
|
|
84
89
|
return <Fixup />;
|
|
85
90
|
} else if (positional_list.has("log")) {
|
|
86
91
|
return <Log />;
|
|
92
|
+
} else if (positional_list.has("update")) {
|
|
93
|
+
return <Update />;
|
|
87
94
|
} else if (positional_list.has("rebase")) {
|
|
88
95
|
return (
|
|
89
96
|
<DependencyCheck>
|
package/src/app/AutoUpdate.tsx
CHANGED
|
@@ -17,6 +17,7 @@ type Props = {
|
|
|
17
17
|
name: string;
|
|
18
18
|
children: React.ReactNode;
|
|
19
19
|
verbose?: boolean;
|
|
20
|
+
force?: boolean;
|
|
20
21
|
timeoutMs?: number;
|
|
21
22
|
onError?: (error: Error) => void;
|
|
22
23
|
onOutput?: (output: React.ReactNode) => void;
|
|
@@ -65,13 +66,14 @@ export function AutoUpdate(props: Props) {
|
|
|
65
66
|
let is_brew_bun_standalone = false;
|
|
66
67
|
|
|
67
68
|
const local_version = process.env.CLI_VERSION;
|
|
69
|
+
const is_output = props_ref.current.verbose || props_ref.current.force;
|
|
68
70
|
|
|
69
71
|
async function auto_update() {
|
|
70
72
|
if (!local_version) {
|
|
71
73
|
throw new Error("Auto update requires process.env.CLI_VERSION to be set");
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
if (
|
|
76
|
+
if (is_output) {
|
|
75
77
|
handle_output(<Ink.Text key="init">Checking for latest version...</Ink.Text>);
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -125,9 +127,14 @@ export function AutoUpdate(props: Props) {
|
|
|
125
127
|
|
|
126
128
|
const semver_result = semver_compare(latest_version, local_version);
|
|
127
129
|
|
|
128
|
-
status = "prompt";
|
|
129
|
-
|
|
130
130
|
if (semver_result === 0) {
|
|
131
|
+
if (is_output) {
|
|
132
|
+
handle_output(
|
|
133
|
+
<Ink.Text>
|
|
134
|
+
✅ Everything up to date. <Brackets>{latest_version}</Brackets>
|
|
135
|
+
</Ink.Text>,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
131
138
|
return;
|
|
132
139
|
}
|
|
133
140
|
|
|
@@ -175,7 +182,7 @@ export function AutoUpdate(props: Props) {
|
|
|
175
182
|
if (state.is_brew_bun_standalone) {
|
|
176
183
|
install_command = `npm install -g ${props.name}@latest`;
|
|
177
184
|
} else {
|
|
178
|
-
install_command =
|
|
185
|
+
install_command = "brew upgrade magus/git-stack/git-stack";
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
return (
|
|
@@ -183,7 +190,14 @@ export function AutoUpdate(props: Props) {
|
|
|
183
190
|
message={
|
|
184
191
|
<Ink.Box flexDirection="column">
|
|
185
192
|
<Ink.Text color={colors.yellow}>
|
|
186
|
-
|
|
193
|
+
<FormatText
|
|
194
|
+
wrapper={<Ink.Text />}
|
|
195
|
+
message="New version available {latest_version}, would you like to update?"
|
|
196
|
+
values={{
|
|
197
|
+
latest_version: <Brackets>{state.latest_version}</Brackets>,
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
,
|
|
187
201
|
</Ink.Text>
|
|
188
202
|
<Ink.Text> </Ink.Text>
|
|
189
203
|
<Command>{install_command}</Command>
|
package/src/app/ManualRebase.tsx
CHANGED
|
@@ -59,6 +59,11 @@ async function run() {
|
|
|
59
59
|
commit.title = group_from_map.title;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// // capture commit_range for GitReviseTodo test
|
|
63
|
+
// // doc-link capture-git-revise-todo
|
|
64
|
+
// console.debug(JSON.stringify(commit_range, null, 2));
|
|
65
|
+
// throw new Error("STOP");
|
|
66
|
+
|
|
62
67
|
await GitReviseTodo.execute({
|
|
63
68
|
rebase_group_index: 0,
|
|
64
69
|
rebase_merge_base: merge_base,
|
package/src/app/MultiSelect.tsx
CHANGED
|
@@ -14,6 +14,7 @@ type Props<T> = {
|
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
disableSelect?: boolean;
|
|
16
16
|
maxWidth?: number;
|
|
17
|
+
startIndex?: number;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
type Item<T> = {
|
|
@@ -67,11 +68,13 @@ export function MultiSelect<T>(props: Props<T>) {
|
|
|
67
68
|
for (let i = props.items.length - 1; i >= 0; i--) {
|
|
68
69
|
const item = props.items[i];
|
|
69
70
|
|
|
70
|
-
if (
|
|
71
|
-
|
|
71
|
+
if (item.disabled) {
|
|
72
|
+
continue;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
last_enabled = i;
|
|
76
|
+
|
|
77
|
+
if (!item.selected) {
|
|
75
78
|
return i;
|
|
76
79
|
}
|
|
77
80
|
}
|
|
@@ -80,7 +83,7 @@ export function MultiSelect<T>(props: Props<T>) {
|
|
|
80
83
|
return last_enabled;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
return 0;
|
|
86
|
+
return props.startIndex || 0;
|
|
84
87
|
},
|
|
85
88
|
);
|
|
86
89
|
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import * as Ink from "ink-cjs";
|
|
4
4
|
|
|
5
5
|
import { Brackets } from "~/app/Brackets";
|
|
6
|
+
import { Command } from "~/app/Command";
|
|
6
7
|
import { FormatText } from "~/app/FormatText";
|
|
7
8
|
import { MultiSelect } from "~/app/MultiSelect";
|
|
8
9
|
import { Parens } from "~/app/Parens";
|
|
@@ -78,10 +79,13 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
78
79
|
|
|
79
80
|
// detect if there are unassigned commits
|
|
80
81
|
let unassigned_count = 0;
|
|
82
|
+
let assigned_count = 0;
|
|
81
83
|
for (const [, group_id] of commit_map.entries()) {
|
|
82
84
|
if (group_id === null) {
|
|
83
85
|
// console.debug("unassigned commit detected", sha);
|
|
84
86
|
unassigned_count++;
|
|
87
|
+
} else {
|
|
88
|
+
assigned_count++;
|
|
85
89
|
}
|
|
86
90
|
}
|
|
87
91
|
|
|
@@ -118,36 +122,54 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
118
122
|
current_index = 0;
|
|
119
123
|
}
|
|
120
124
|
|
|
125
|
+
const has_unassigned_commits = unassigned_count > 0;
|
|
126
|
+
const has_assigned_commits = assigned_count > 0;
|
|
127
|
+
|
|
128
|
+
const sync_status = detect_sync_status();
|
|
129
|
+
// console.debug({ sync_status });
|
|
130
|
+
|
|
121
131
|
Ink.useInput((input, key) => {
|
|
122
|
-
const
|
|
132
|
+
const input_lower = input.toLowerCase();
|
|
123
133
|
|
|
124
|
-
|
|
134
|
+
if (input_lower === SYMBOL.s) {
|
|
135
|
+
// do not allow sync when inputting group title
|
|
136
|
+
if (group_input) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (sync_status === "disabled") {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
125
143
|
|
|
126
|
-
if (!hasUnassignedCommits && inputLower === "s") {
|
|
127
144
|
actions.set((state) => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
for (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
const state_commit_map: Record<string, SimpleGroup> = {};
|
|
146
|
+
|
|
147
|
+
for (let [sha, id] of commit_map.entries()) {
|
|
148
|
+
// console.debug({ sha, id });
|
|
149
|
+
|
|
150
|
+
// handle allow_unassigned case
|
|
151
|
+
if (!id) {
|
|
152
|
+
id = props.commit_range.UNASSIGNED;
|
|
153
|
+
const title = "allow_unassigned";
|
|
154
|
+
state_commit_map[sha] = { id, title };
|
|
155
|
+
continue;
|
|
137
156
|
}
|
|
138
|
-
}
|
|
139
157
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
158
|
+
const group = group_list.find((g) => g.id === id);
|
|
159
|
+
invariant(group, "group must exist");
|
|
160
|
+
// console.debug({ group });
|
|
161
|
+
state_commit_map[sha] = group;
|
|
144
162
|
}
|
|
163
|
+
|
|
164
|
+
state.commit_map = state_commit_map;
|
|
165
|
+
state.step = "pre-manual-rebase";
|
|
145
166
|
});
|
|
167
|
+
|
|
146
168
|
return;
|
|
147
169
|
}
|
|
148
170
|
|
|
149
171
|
// only allow create when on unassigned group
|
|
150
|
-
if (
|
|
172
|
+
if (has_unassigned_commits && input_lower === SYMBOL.c) {
|
|
151
173
|
set_group_input(true);
|
|
152
174
|
return;
|
|
153
175
|
}
|
|
@@ -170,6 +192,10 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
170
192
|
const multiselect_disabled = group_input;
|
|
171
193
|
const multiselect_disableSelect = group.id === props.commit_range.UNASSIGNED;
|
|
172
194
|
|
|
195
|
+
const max_width = 80;
|
|
196
|
+
const [focused, set_focused] = React.useState("");
|
|
197
|
+
const has_groups = group.id !== props.commit_range.UNASSIGNED;
|
|
198
|
+
|
|
173
199
|
const items = props.commit_range.commit_list.map((commit) => {
|
|
174
200
|
const commit_metadata_id = commit_map.get(commit.sha);
|
|
175
201
|
|
|
@@ -179,6 +205,8 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
179
205
|
|
|
180
206
|
if (group_input) {
|
|
181
207
|
disabled = true;
|
|
208
|
+
} else if (!has_groups) {
|
|
209
|
+
disabled = true;
|
|
182
210
|
} else {
|
|
183
211
|
disabled = Boolean(selected && commit_metadata_id !== group.id);
|
|
184
212
|
}
|
|
@@ -193,30 +221,102 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
193
221
|
|
|
194
222
|
items.reverse();
|
|
195
223
|
|
|
196
|
-
|
|
224
|
+
return (
|
|
225
|
+
<Ink.Box flexDirection="column">
|
|
226
|
+
<Ink.Box height={1} />
|
|
227
|
+
|
|
228
|
+
{has_groups || group_input ? null : (
|
|
229
|
+
<Ink.Box flexDirection="column">
|
|
230
|
+
<Ink.Text bold color={colors.blue}>
|
|
231
|
+
👋 Welcome to <Command>git stack</Command>!
|
|
232
|
+
</Ink.Text>
|
|
233
|
+
<Ink.Text color={colors.blue}>
|
|
234
|
+
<FormatText
|
|
235
|
+
message="Press {c} to {create} a new PR group"
|
|
236
|
+
values={{
|
|
237
|
+
c: (
|
|
238
|
+
<Ink.Text bold color={colors.green}>
|
|
239
|
+
c
|
|
240
|
+
</Ink.Text>
|
|
241
|
+
),
|
|
242
|
+
create: (
|
|
243
|
+
<Ink.Text bold color={colors.green}>
|
|
244
|
+
<Parens>c</Parens>reate
|
|
245
|
+
</Ink.Text>
|
|
246
|
+
),
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
</Ink.Text>
|
|
250
|
+
</Ink.Box>
|
|
251
|
+
)}
|
|
197
252
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
253
|
+
{!has_groups || group_input ? null : (
|
|
254
|
+
<React.Fragment>
|
|
255
|
+
<Ink.Box width={max_width} flexDirection="row">
|
|
256
|
+
<Ink.Box flexDirection="row">
|
|
257
|
+
<Ink.Text bold color={colors.green}>
|
|
258
|
+
{SYMBOL.left}
|
|
259
|
+
</Ink.Text>
|
|
260
|
+
<Ink.Box width={1} />
|
|
261
|
+
<Ink.Text color={colors.gray}>Pull request</Ink.Text>
|
|
262
|
+
<Ink.Box width={1} />
|
|
263
|
+
<Ink.Text color={colors.gray}>
|
|
264
|
+
{`(${current_index + 1}/${group_list.length})`}
|
|
265
|
+
</Ink.Text>
|
|
266
|
+
<Ink.Box width={1} />
|
|
267
|
+
<Ink.Text bold color={colors.green}>
|
|
268
|
+
{SYMBOL.right}
|
|
269
|
+
</Ink.Text>
|
|
270
|
+
</Ink.Box>
|
|
271
|
+
</Ink.Box>
|
|
272
|
+
|
|
273
|
+
<Ink.Box width={max_width}>
|
|
274
|
+
<Ink.Text wrap="truncate-end" bold color={colors.green}>
|
|
275
|
+
{group.title}
|
|
276
|
+
</Ink.Text>
|
|
277
|
+
</Ink.Box>
|
|
278
|
+
</React.Fragment>
|
|
279
|
+
)}
|
|
201
280
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
group_title_width -= left_arrow.length + right_arrow.length;
|
|
206
|
-
group_title_width = Math.min(group.title.length, group_title_width);
|
|
281
|
+
{!group_input ? null : (
|
|
282
|
+
<React.Fragment>
|
|
283
|
+
<Ink.Box height={1} />
|
|
207
284
|
|
|
208
|
-
|
|
209
|
-
|
|
285
|
+
<FormatText
|
|
286
|
+
wrapper={<Ink.Text color={colors.gray} />}
|
|
287
|
+
message="Enter a title for the PR {note}"
|
|
288
|
+
values={{
|
|
289
|
+
note: (
|
|
290
|
+
<Parens>
|
|
291
|
+
<FormatText
|
|
292
|
+
message="press {enter} to submit"
|
|
293
|
+
values={{
|
|
294
|
+
enter: (
|
|
295
|
+
<Ink.Text bold color={colors.green}>
|
|
296
|
+
{SYMBOL.enter}
|
|
297
|
+
</Ink.Text>
|
|
298
|
+
),
|
|
299
|
+
}}
|
|
300
|
+
/>
|
|
301
|
+
</Parens>
|
|
302
|
+
),
|
|
303
|
+
}}
|
|
304
|
+
/>
|
|
210
305
|
|
|
211
|
-
|
|
306
|
+
<TextInput
|
|
307
|
+
defaultValue={focused}
|
|
308
|
+
onSubmit={submit_group_input}
|
|
309
|
+
onCancel={() => set_group_input(false)}
|
|
310
|
+
/>
|
|
311
|
+
</React.Fragment>
|
|
312
|
+
)}
|
|
212
313
|
|
|
213
|
-
return (
|
|
214
|
-
<Ink.Box flexDirection="column">
|
|
215
314
|
<Ink.Box height={1} />
|
|
216
315
|
|
|
217
316
|
<MultiSelect
|
|
317
|
+
startIndex={items.length - 1}
|
|
218
318
|
items={items}
|
|
219
|
-
maxWidth={
|
|
319
|
+
maxWidth={max_width}
|
|
220
320
|
disabled={multiselect_disabled}
|
|
221
321
|
disableSelect={multiselect_disableSelect}
|
|
222
322
|
onFocus={(args) => {
|
|
@@ -242,109 +342,69 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
242
342
|
|
|
243
343
|
<Ink.Box height={1} />
|
|
244
344
|
|
|
245
|
-
|
|
246
|
-
<Ink.Text>{left_arrow}</Ink.Text>
|
|
247
|
-
<Ink.Text>{group_position}</Ink.Text>
|
|
248
|
-
|
|
249
|
-
<Ink.Box width={group_title_width} justifyContent="center">
|
|
250
|
-
<Ink.Text wrap="truncate-end">{group.title}</Ink.Text>
|
|
251
|
-
</Ink.Box>
|
|
252
|
-
|
|
253
|
-
<Ink.Text>{right_arrow}</Ink.Text>
|
|
254
|
-
</Ink.Box>
|
|
255
|
-
|
|
256
|
-
<Ink.Box height={1} />
|
|
257
|
-
|
|
258
|
-
{unassigned_count > 0 ? (
|
|
259
|
-
<FormatText
|
|
260
|
-
wrapper={<Ink.Text color={colors.gray} />}
|
|
261
|
-
message="{count} unassigned commits, press {c} to {create} a new group"
|
|
262
|
-
values={{
|
|
263
|
-
count: (
|
|
264
|
-
<Ink.Text color={colors.yellow} bold>
|
|
265
|
-
{unassigned_count}
|
|
266
|
-
</Ink.Text>
|
|
267
|
-
),
|
|
268
|
-
c: (
|
|
269
|
-
<Ink.Text bold color={colors.green}>
|
|
270
|
-
c
|
|
271
|
-
</Ink.Text>
|
|
272
|
-
),
|
|
273
|
-
create: (
|
|
274
|
-
<Ink.Text bold color={colors.green}>
|
|
275
|
-
<Parens>c</Parens>reate
|
|
276
|
-
</Ink.Text>
|
|
277
|
-
),
|
|
278
|
-
}}
|
|
279
|
-
/>
|
|
280
|
-
) : (
|
|
345
|
+
{has_unassigned_commits ? (
|
|
281
346
|
<React.Fragment>
|
|
282
|
-
|
|
347
|
+
<FormatText
|
|
348
|
+
wrapper={<Ink.Text color={colors.gray} />}
|
|
349
|
+
message="{count} unassigned commits"
|
|
350
|
+
values={{
|
|
351
|
+
count: (
|
|
352
|
+
<Ink.Text color={colors.yellow} bold>
|
|
353
|
+
{unassigned_count}
|
|
354
|
+
</Ink.Text>
|
|
355
|
+
),
|
|
356
|
+
}}
|
|
357
|
+
/>
|
|
358
|
+
|
|
359
|
+
{group_input ? null : (
|
|
283
360
|
<FormatText
|
|
284
|
-
wrapper={<Ink.Text />}
|
|
285
|
-
message="
|
|
361
|
+
wrapper={<Ink.Text color={colors.gray} />}
|
|
362
|
+
message="Press {c} to {create} a new PR group"
|
|
286
363
|
values={{
|
|
287
|
-
|
|
364
|
+
c: (
|
|
288
365
|
<Ink.Text bold color={colors.green}>
|
|
289
|
-
|
|
366
|
+
c
|
|
290
367
|
</Ink.Text>
|
|
291
368
|
),
|
|
292
|
-
|
|
369
|
+
create: (
|
|
293
370
|
<Ink.Text bold color={colors.green}>
|
|
294
|
-
<Parens>
|
|
371
|
+
<Parens>c</Parens>reate
|
|
295
372
|
</Ink.Text>
|
|
296
373
|
),
|
|
297
374
|
}}
|
|
298
375
|
/>
|
|
299
|
-
)
|
|
376
|
+
)}
|
|
377
|
+
|
|
378
|
+
{sync_status !== "allow_unassigned" ? null : (
|
|
300
379
|
<FormatText
|
|
301
|
-
wrapper={<Ink.Text />}
|
|
302
|
-
message="
|
|
380
|
+
wrapper={<Ink.Text color={colors.gray} />}
|
|
381
|
+
message="Press {s} to {sync} the {count} assigned commits to Github"
|
|
303
382
|
values={{
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
),
|
|
309
|
-
save: (
|
|
310
|
-
<Ink.Text bold color={colors.green}>
|
|
311
|
-
<Parens>s</Parens>save
|
|
383
|
+
...S_TO_SYNC_VALUES,
|
|
384
|
+
count: (
|
|
385
|
+
<Ink.Text color={colors.yellow} bold>
|
|
386
|
+
{assigned_count}
|
|
312
387
|
</Ink.Text>
|
|
313
388
|
),
|
|
314
389
|
}}
|
|
315
390
|
/>
|
|
316
391
|
)}
|
|
317
392
|
</React.Fragment>
|
|
318
|
-
)
|
|
319
|
-
|
|
320
|
-
{!group_input ? null : (
|
|
393
|
+
) : (
|
|
321
394
|
<React.Fragment>
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
{SYMBOL.enter}
|
|
336
|
-
</Ink.Text>
|
|
337
|
-
),
|
|
338
|
-
}}
|
|
339
|
-
/>
|
|
340
|
-
</Parens>
|
|
341
|
-
),
|
|
342
|
-
}}
|
|
343
|
-
/>
|
|
344
|
-
|
|
345
|
-
<TextInput defaultValue={focused} onSubmit={submit_group_input} />
|
|
346
|
-
|
|
347
|
-
<Ink.Box height={1} />
|
|
395
|
+
{argv.sync ? (
|
|
396
|
+
<FormatText
|
|
397
|
+
wrapper={<Ink.Text />}
|
|
398
|
+
message="🎉 Done! Press {s} to {sync} the commits to Github"
|
|
399
|
+
values={S_TO_SYNC_VALUES}
|
|
400
|
+
/>
|
|
401
|
+
) : (
|
|
402
|
+
<FormatText
|
|
403
|
+
wrapper={<Ink.Text />}
|
|
404
|
+
message="🎉 Done! Press {s} to {save} the commits locally"
|
|
405
|
+
values={S_TO_SYNC_VALUES}
|
|
406
|
+
/>
|
|
407
|
+
)}
|
|
348
408
|
</React.Fragment>
|
|
349
409
|
)}
|
|
350
410
|
|
|
@@ -396,13 +456,14 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
396
456
|
|
|
397
457
|
return `${branch_prefix}${gs_short_id()}`;
|
|
398
458
|
}
|
|
459
|
+
|
|
399
460
|
function submit_group_input(title: string) {
|
|
400
461
|
const id = get_group_id();
|
|
401
462
|
|
|
402
463
|
actions.output(
|
|
403
464
|
<FormatText
|
|
404
465
|
wrapper={<Ink.Text dimColor />}
|
|
405
|
-
message="Created new group {group} {note}"
|
|
466
|
+
message="Created new PR group {group} {note}"
|
|
406
467
|
values={{
|
|
407
468
|
group: <Brackets>{title}</Brackets>,
|
|
408
469
|
note: <Parens>{id}</Parens>,
|
|
@@ -415,10 +476,64 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
415
476
|
set_selected_group_id(id);
|
|
416
477
|
set_group_input(false);
|
|
417
478
|
}
|
|
479
|
+
|
|
480
|
+
function detect_sync_status() {
|
|
481
|
+
if (!has_unassigned_commits) {
|
|
482
|
+
return "allow";
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (!has_assigned_commits) {
|
|
486
|
+
return "disabled";
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
let allow_unassigned_sync = null;
|
|
490
|
+
|
|
491
|
+
for (let i = 0; i < props.commit_range.commit_list.length; i++) {
|
|
492
|
+
const commit = props.commit_range.commit_list[i];
|
|
493
|
+
const group_id = commit_map.get(commit.sha);
|
|
494
|
+
// console.debug(commit.sha, group_id);
|
|
495
|
+
|
|
496
|
+
// before detecting unassigned we are null
|
|
497
|
+
if (allow_unassigned_sync === null) {
|
|
498
|
+
if (group_id === null) {
|
|
499
|
+
// console.debug("allow_unassigned_sync TRUE", { i });
|
|
500
|
+
allow_unassigned_sync = true;
|
|
501
|
+
}
|
|
502
|
+
} else {
|
|
503
|
+
// after detecting unassigned we assume we can unassigned sync
|
|
504
|
+
// unless we detect an invariant violation, i.e. commit assigned to group
|
|
505
|
+
if (group_id) {
|
|
506
|
+
// console.debug("allow_unassigned_sync FALSE", { i });
|
|
507
|
+
allow_unassigned_sync = false;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (allow_unassigned_sync) {
|
|
513
|
+
return "allow_unassigned";
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return "disabled";
|
|
517
|
+
}
|
|
418
518
|
}
|
|
419
519
|
|
|
420
520
|
const SYMBOL = {
|
|
421
521
|
left: "←",
|
|
422
522
|
right: "→",
|
|
423
523
|
enter: "Enter",
|
|
524
|
+
c: "c",
|
|
525
|
+
s: "s",
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const S_TO_SYNC_VALUES = {
|
|
529
|
+
s: (
|
|
530
|
+
<Ink.Text bold color={colors.green}>
|
|
531
|
+
s
|
|
532
|
+
</Ink.Text>
|
|
533
|
+
),
|
|
534
|
+
sync: (
|
|
535
|
+
<Ink.Text bold color={colors.green}>
|
|
536
|
+
<Parens>s</Parens>ync
|
|
537
|
+
</Ink.Text>
|
|
538
|
+
),
|
|
424
539
|
};
|
package/src/app/SyncGithub.tsx
CHANGED
|
@@ -42,6 +42,9 @@ async function run() {
|
|
|
42
42
|
|
|
43
43
|
const push_group_list = get_push_group_list();
|
|
44
44
|
|
|
45
|
+
// console.debug({ push_group_list });
|
|
46
|
+
// throw new Error("STOP");
|
|
47
|
+
|
|
45
48
|
// for all push targets in push_group_list
|
|
46
49
|
// things that can be done in parallel are grouped by numbers
|
|
47
50
|
//
|
|
@@ -92,7 +95,7 @@ async function run() {
|
|
|
92
95
|
|
|
93
96
|
await cli(git_push_command);
|
|
94
97
|
|
|
95
|
-
const pr_url_list =
|
|
98
|
+
const pr_url_list = push_group_list.map(get_group_url);
|
|
96
99
|
|
|
97
100
|
const after_push_tasks = [];
|
|
98
101
|
for (const group of push_group_list) {
|
|
@@ -105,8 +108,8 @@ async function run() {
|
|
|
105
108
|
// this step must come after the after_push since that step may create new PRs
|
|
106
109
|
// we need the urls for all prs at this step so we run it after the after_push
|
|
107
110
|
const update_pr_body_tasks = [];
|
|
108
|
-
for (let i = 0; i <
|
|
109
|
-
const group =
|
|
111
|
+
for (let i = 0; i < push_group_list.length; i++) {
|
|
112
|
+
const group = push_group_list[i];
|
|
110
113
|
|
|
111
114
|
// use the updated pr_url_list to get the actual selected_url
|
|
112
115
|
const selected_url = pr_url_list[i];
|
|
@@ -149,7 +152,9 @@ async function run() {
|
|
|
149
152
|
|
|
150
153
|
const group = commit_range.group_list[index];
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
if (group.id !== commit_range.UNASSIGNED) {
|
|
156
|
+
push_group_list.unshift(group);
|
|
157
|
+
}
|
|
153
158
|
}
|
|
154
159
|
|
|
155
160
|
return push_group_list;
|