git-stack-cli 2.5.3 → 2.6.1
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 +437 -321
- package/package.json +2 -2
- package/scripts/bun-build.ts +5 -0
- package/src/app/App.tsx +8 -5
- package/src/app/AutoUpdate.tsx +72 -54
- package/src/app/GatherMetadata.tsx +2 -24
- package/src/app/Main.tsx +0 -4
- package/src/app/ManualRebase.tsx +5 -0
- package/src/app/MultiSelect.tsx +8 -12
- package/src/app/RequireBranch.tsx +67 -0
- package/src/app/SelectCommitRanges.tsx +233 -118
- package/src/app/Status.tsx +1 -5
- package/src/app/Store.tsx +2 -1
- package/src/app/SyncGithub.tsx +9 -4
- package/src/app/TextInput.tsx +8 -1
- package/src/commands/Rebase.tsx +50 -36
- package/src/components/ColorTest.tsx +49 -0
- package/src/core/CommitMetadata.ts +6 -5
- package/src/core/GitReviseTodo.test.ts +195 -0
- package/src/core/GitReviseTodo.ts +20 -17
- package/src/core/cli.ts +2 -0
- package/src/core/colors.ts +4 -2
- package/src/app/PreSelectCommitRanges.tsx +0 -29
|
@@ -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"
|
|
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.white}>
|
|
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,116 +342,76 @@ 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"
|
|
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
|
|
|
351
411
|
<Ink.Box>
|
|
352
412
|
<FormatText
|
|
353
413
|
wrapper={<Ink.Text color={colors.gray} />}
|
|
354
|
-
message="Press {left} and {right} to view
|
|
414
|
+
message="Press {left} and {right} to view PRs"
|
|
355
415
|
values={{
|
|
356
416
|
left: (
|
|
357
417
|
<Ink.Text bold color={colors.green}>
|
|
@@ -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
|
|
466
|
+
message="Created new PR {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/Status.tsx
CHANGED
|
@@ -42,11 +42,7 @@ async function run() {
|
|
|
42
42
|
Store.setState((state) => {
|
|
43
43
|
state.step = "pre-local-merge-rebase";
|
|
44
44
|
});
|
|
45
|
-
} else if (needs_update) {
|
|
46
|
-
Store.setState((state) => {
|
|
47
|
-
state.step = "pre-select-commit-ranges";
|
|
48
|
-
});
|
|
49
|
-
} else if (argv.force) {
|
|
45
|
+
} else if (needs_update || argv.force) {
|
|
50
46
|
Store.setState((state) => {
|
|
51
47
|
state.step = "select-commit-ranges";
|
|
52
48
|
});
|
package/src/app/Store.tsx
CHANGED
|
@@ -51,6 +51,7 @@ export type State = {
|
|
|
51
51
|
master_branch: string;
|
|
52
52
|
head: null | string;
|
|
53
53
|
branch_name: null | string;
|
|
54
|
+
merge_base: null | string;
|
|
54
55
|
commit_range: null | CommitMetadata.CommitRange;
|
|
55
56
|
commit_map: null | CommitMap;
|
|
56
57
|
pr_templates: Array<string>;
|
|
@@ -66,7 +67,6 @@ export type State = {
|
|
|
66
67
|
| "status"
|
|
67
68
|
| "pre-local-merge-rebase"
|
|
68
69
|
| "local-merge-rebase"
|
|
69
|
-
| "pre-select-commit-ranges"
|
|
70
70
|
| "select-commit-ranges"
|
|
71
71
|
| "pre-manual-rebase"
|
|
72
72
|
| "manual-rebase"
|
|
@@ -122,6 +122,7 @@ const BaseStore = createStore<State>()(
|
|
|
122
122
|
master_branch: "origin/master",
|
|
123
123
|
head: null,
|
|
124
124
|
branch_name: null,
|
|
125
|
+
merge_base: null,
|
|
125
126
|
commit_range: null,
|
|
126
127
|
commit_map: null,
|
|
127
128
|
pr_templates: [],
|
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;
|
package/src/app/TextInput.tsx
CHANGED
|
@@ -10,6 +10,7 @@ type Props = {
|
|
|
10
10
|
value?: string;
|
|
11
11
|
onChange?: (value: string) => void;
|
|
12
12
|
onSubmit?: (value: string) => void;
|
|
13
|
+
onCancel?: () => void;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export function TextInput(props: Props) {
|
|
@@ -44,7 +45,13 @@ export function TextInput(props: Props) {
|
|
|
44
45
|
|
|
45
46
|
// console.debug("[useInput]", { input, key });
|
|
46
47
|
|
|
47
|
-
if (key.
|
|
48
|
+
if (key.escape) {
|
|
49
|
+
if (value === "") {
|
|
50
|
+
props.onCancel?.();
|
|
51
|
+
} else {
|
|
52
|
+
next_value = "";
|
|
53
|
+
}
|
|
54
|
+
} else if (key.backspace || key.delete) {
|
|
48
55
|
next_value = value.slice(0, -1);
|
|
49
56
|
} else if (key.return) {
|
|
50
57
|
props.onSubmit?.(next_value);
|
package/src/commands/Rebase.tsx
CHANGED
|
@@ -22,7 +22,7 @@ type Props = {
|
|
|
22
22
|
export function Rebase(props: Props) {
|
|
23
23
|
return (
|
|
24
24
|
<Await
|
|
25
|
-
fallback={<Ink.Text color={colors.yellow}>Rebasing
|
|
25
|
+
fallback={<Ink.Text color={colors.yellow}>Rebasing…</Ink.Text>}
|
|
26
26
|
function={() => Rebase.run(props)}
|
|
27
27
|
/>
|
|
28
28
|
);
|
|
@@ -48,6 +48,7 @@ Rebase.run = async function run(props: Props) {
|
|
|
48
48
|
return 19;
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
const master_branch_name = master_branch.replace(/^origin\//, "");
|
|
51
52
|
const temp_branch_name = `${branch_name}_${short_id()}`;
|
|
52
53
|
|
|
53
54
|
try {
|
|
@@ -58,9 +59,56 @@ Rebase.run = async function run(props: Props) {
|
|
|
58
59
|
await cli(`pwd`);
|
|
59
60
|
|
|
60
61
|
// fetch origin master branch for latest sha
|
|
61
|
-
const master_branch_name = master_branch.replace(/^origin\//, "");
|
|
62
62
|
await cli(`git fetch --no-tags -v origin ${master_branch_name}`);
|
|
63
63
|
|
|
64
|
+
if (branch_name === master_branch_name) {
|
|
65
|
+
await rebase_master();
|
|
66
|
+
} else {
|
|
67
|
+
await rebase_branch();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
actions.unregister_abort_handler();
|
|
71
|
+
} catch (err) {
|
|
72
|
+
actions.error("Unable to rebase.");
|
|
73
|
+
|
|
74
|
+
if (err instanceof Error) {
|
|
75
|
+
actions.error(err.message);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
actions.exit(20);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const next_commit_range = await CommitMetadata.range();
|
|
82
|
+
|
|
83
|
+
actions.output(
|
|
84
|
+
<FormatText
|
|
85
|
+
wrapper={<Ink.Text color={colors.green} />}
|
|
86
|
+
message="✅ {branch_name} in sync with {origin_branch}"
|
|
87
|
+
values={{
|
|
88
|
+
branch_name: <Brackets>{branch_name}</Brackets>,
|
|
89
|
+
origin_branch: <Brackets>{master_branch}</Brackets>,
|
|
90
|
+
}}
|
|
91
|
+
/>,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
actions.set((state) => {
|
|
95
|
+
state.commit_range = next_commit_range;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (props.onComplete) {
|
|
99
|
+
props.onComplete();
|
|
100
|
+
} else {
|
|
101
|
+
actions.output(<Status />);
|
|
102
|
+
actions.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function rebase_master() {
|
|
106
|
+
await cli(`git switch -C "${master_branch_name}" "${master_branch}"`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function rebase_branch() {
|
|
110
|
+
invariant(commit_range, "commit_range must exist");
|
|
111
|
+
|
|
64
112
|
const master_sha = (await cli(`git rev-parse ${master_branch}`)).stdout;
|
|
65
113
|
const rebase_merge_base = master_sha;
|
|
66
114
|
|
|
@@ -120,40 +168,6 @@ Rebase.run = async function run(props: Props) {
|
|
|
120
168
|
await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
|
|
121
169
|
|
|
122
170
|
restore_git();
|
|
123
|
-
|
|
124
|
-
actions.unregister_abort_handler();
|
|
125
|
-
} catch (err) {
|
|
126
|
-
actions.error("Unable to rebase.");
|
|
127
|
-
|
|
128
|
-
if (err instanceof Error) {
|
|
129
|
-
actions.error(err.message);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
actions.exit(20);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const next_commit_range = await CommitMetadata.range();
|
|
136
|
-
|
|
137
|
-
actions.output(
|
|
138
|
-
<FormatText
|
|
139
|
-
wrapper={<Ink.Text color={colors.green} />}
|
|
140
|
-
message="✅ {branch_name} in sync with {origin_branch}"
|
|
141
|
-
values={{
|
|
142
|
-
branch_name: <Brackets>{branch_name}</Brackets>,
|
|
143
|
-
origin_branch: <Brackets>{master_branch}</Brackets>,
|
|
144
|
-
}}
|
|
145
|
-
/>,
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
actions.set((state) => {
|
|
149
|
-
state.commit_range = next_commit_range;
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
if (props.onComplete) {
|
|
153
|
-
props.onComplete();
|
|
154
|
-
} else {
|
|
155
|
-
actions.output(<Status />);
|
|
156
|
-
actions.exit(0);
|
|
157
171
|
}
|
|
158
172
|
|
|
159
173
|
// cleanup git operations if cancelled during manual rebase
|