git-stack-cli 1.14.0 → 1.15.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/README.md +2 -4
- package/dist/cjs/index.cjs +310 -180
- package/package.json +2 -1
- package/scripts/link.ts +14 -0
- package/src/app/App.tsx +41 -30
- package/src/app/AutoUpdate.tsx +9 -24
- package/src/app/CherryPickCheck.tsx +1 -2
- package/src/app/Debug.tsx +5 -6
- package/src/app/DependencyCheck.tsx +6 -6
- package/src/app/DetectInitialPR.tsx +2 -8
- package/src/app/DirtyCheck.tsx +51 -26
- package/src/app/Exit.tsx +41 -8
- package/src/app/FormatText.tsx +1 -5
- package/src/app/GatherMetadata.tsx +6 -13
- package/src/app/GithubApiError.tsx +1 -1
- package/src/app/HandleCtrlCSigint.tsx +36 -0
- package/src/app/LocalCommitStatus.tsx +1 -3
- package/src/app/LogTimestamp.tsx +1 -5
- package/src/app/ManualRebase.tsx +15 -37
- package/src/app/MultiSelect.tsx +2 -2
- package/src/app/PostRebaseStatus.tsx +2 -0
- package/src/app/PreManualRebase.tsx +3 -5
- package/src/app/RebaseCheck.tsx +1 -2
- package/src/app/SelectCommitRanges.tsx +6 -10
- package/src/app/Status.tsx +1 -1
- package/src/app/StatusTable.tsx +1 -4
- package/src/app/Store.tsx +29 -3
- package/src/app/SyncGithub.tsx +15 -45
- package/src/app/Table.tsx +4 -14
- package/src/app/TextInput.tsx +2 -7
- package/src/app/VerboseDebugInfo.tsx +1 -5
- package/src/app/YesNoPrompt.tsx +42 -31
- package/src/command.ts +8 -17
- package/src/commands/Fixup.tsx +17 -24
- package/src/commands/Log.tsx +3 -7
- package/src/commands/Rebase.tsx +18 -38
- package/src/components/ErrorBoundary.tsx +79 -0
- package/src/components/ExitingGate.tsx +27 -0
- package/src/core/CommitMetadata.ts +1 -1
- package/src/core/GitReviseTodo.test.ts +3 -3
- package/src/core/GitReviseTodo.ts +6 -8
- package/src/core/Metadata.test.ts +4 -4
- package/src/core/StackSummaryTable.ts +3 -3
- package/src/core/chalk.ts +1 -5
- package/src/core/cli.ts +2 -2
- package/src/core/github.tsx +15 -14
- package/src/core/pretty_json.ts +7 -0
- package/src/github/gh.auth_status.test.ts +2 -6
- package/src/index.tsx +42 -6
package/src/app/ManualRebase.tsx
CHANGED
|
@@ -15,35 +15,12 @@ import { invariant } from "~/core/invariant";
|
|
|
15
15
|
import { short_id } from "~/core/short_id";
|
|
16
16
|
|
|
17
17
|
export function ManualRebase() {
|
|
18
|
-
const abort_handler = React.useRef(() => {});
|
|
19
|
-
|
|
20
|
-
React.useEffect(function listen_sigint() {
|
|
21
|
-
process.once("SIGINT", sigint_handler);
|
|
22
|
-
|
|
23
|
-
return function cleanup() {
|
|
24
|
-
process.removeListener("SIGINT", sigint_handler);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
async function sigint_handler() {
|
|
28
|
-
abort_handler.current();
|
|
29
|
-
}
|
|
30
|
-
}, []);
|
|
31
|
-
|
|
32
18
|
return (
|
|
33
|
-
<Await
|
|
34
|
-
fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>}
|
|
35
|
-
function={async function () {
|
|
36
|
-
await run({ abort_handler });
|
|
37
|
-
}}
|
|
38
|
-
/>
|
|
19
|
+
<Await fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>} function={run} />
|
|
39
20
|
);
|
|
40
21
|
}
|
|
41
22
|
|
|
42
|
-
|
|
43
|
-
abort_handler: React.MutableRefObject<() => void>;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
async function run(args: Args) {
|
|
23
|
+
async function run() {
|
|
47
24
|
const state = Store.getState();
|
|
48
25
|
const actions = state.actions;
|
|
49
26
|
const argv = state.argv;
|
|
@@ -57,18 +34,18 @@ async function run(args: Args) {
|
|
|
57
34
|
invariant(commit_map, "commit_map must exist");
|
|
58
35
|
invariant(repo_root, "repo_root must exist");
|
|
59
36
|
|
|
60
|
-
//
|
|
61
|
-
|
|
37
|
+
// immediately register abort_handler in case of ctrl+c exit
|
|
38
|
+
actions.register_abort_handler(async function abort_manual_rebase() {
|
|
62
39
|
actions.output(<Ink.Text color={colors.red}>🚨 Abort</Ink.Text>);
|
|
63
|
-
handle_exit(
|
|
64
|
-
|
|
40
|
+
handle_exit();
|
|
41
|
+
return 15;
|
|
42
|
+
});
|
|
65
43
|
|
|
66
44
|
const temp_branch_name = `${branch_name}_${short_id()}`;
|
|
67
45
|
|
|
68
46
|
try {
|
|
69
47
|
// get latest merge_base relative to local master
|
|
70
|
-
const merge_base = (await cli(`git merge-base HEAD ${master_branch}`))
|
|
71
|
-
.stdout;
|
|
48
|
+
const merge_base = (await cli(`git merge-base HEAD ${master_branch}`)).stdout;
|
|
72
49
|
|
|
73
50
|
// immediately paint all commit to preserve selected commit ranges
|
|
74
51
|
let commit_range = await CommitMetadata.range(commit_map);
|
|
@@ -137,6 +114,8 @@ async function run(args: Args) {
|
|
|
137
114
|
|
|
138
115
|
restore_git();
|
|
139
116
|
|
|
117
|
+
actions.unregister_abort_handler();
|
|
118
|
+
|
|
140
119
|
if (argv.sync) {
|
|
141
120
|
actions.set((state) => {
|
|
142
121
|
state.step = "sync-github";
|
|
@@ -157,7 +136,8 @@ async function run(args: Args) {
|
|
|
157
136
|
actions.error("Try again with `--verbose` to see more information.");
|
|
158
137
|
}
|
|
159
138
|
|
|
160
|
-
handle_exit(
|
|
139
|
+
handle_exit();
|
|
140
|
+
actions.exit(16);
|
|
161
141
|
}
|
|
162
142
|
|
|
163
143
|
// cleanup git operations if cancelled during manual rebase
|
|
@@ -185,11 +165,11 @@ async function run(args: Args) {
|
|
|
185
165
|
cli.sync(`pwd`, spawn_options);
|
|
186
166
|
}
|
|
187
167
|
|
|
188
|
-
function handle_exit(
|
|
168
|
+
function handle_exit() {
|
|
189
169
|
actions.output(
|
|
190
170
|
<Ink.Text color={colors.yellow}>
|
|
191
171
|
Restoring <Brackets>{branch_name}</Brackets>…
|
|
192
|
-
</Ink.Text
|
|
172
|
+
</Ink.Text>,
|
|
193
173
|
);
|
|
194
174
|
|
|
195
175
|
restore_git();
|
|
@@ -197,9 +177,7 @@ async function run(args: Args) {
|
|
|
197
177
|
actions.output(
|
|
198
178
|
<Ink.Text color={colors.yellow}>
|
|
199
179
|
Restored <Brackets>{branch_name}</Brackets>.
|
|
200
|
-
</Ink.Text
|
|
180
|
+
</Ink.Text>,
|
|
201
181
|
);
|
|
202
|
-
|
|
203
|
-
actions.exit(code);
|
|
204
182
|
}
|
|
205
183
|
}
|
package/src/app/MultiSelect.tsx
CHANGED
|
@@ -51,7 +51,7 @@ export function MultiSelect<T>(props: Props<T>) {
|
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
return set;
|
|
54
|
-
}
|
|
54
|
+
},
|
|
55
55
|
);
|
|
56
56
|
|
|
57
57
|
// clamp index to keep in item range
|
|
@@ -81,7 +81,7 @@ export function MultiSelect<T>(props: Props<T>) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
return 0;
|
|
84
|
-
}
|
|
84
|
+
},
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
const selectRef = React.useRef(false);
|
|
@@ -51,7 +51,7 @@ async function run() {
|
|
|
51
51
|
values={{
|
|
52
52
|
pr_filepath: <Brackets>{pr_template_fn("")}</Brackets>,
|
|
53
53
|
}}
|
|
54
|
-
|
|
54
|
+
/>,
|
|
55
55
|
);
|
|
56
56
|
|
|
57
57
|
break;
|
|
@@ -75,12 +75,10 @@ async function run() {
|
|
|
75
75
|
wrapper={<Ink.Text color={colors.yellow} />}
|
|
76
76
|
message="{count} queryable templates found under {dir}, but not supported."
|
|
77
77
|
values={{
|
|
78
|
-
count:
|
|
79
|
-
<Ink.Text color={colors.blue}>{pr_templates.length}</Ink.Text>
|
|
80
|
-
),
|
|
78
|
+
count: <Ink.Text color={colors.blue}>{pr_templates.length}</Ink.Text>,
|
|
81
79
|
dir: <Brackets>{PR_TEMPLATE.TemplateDir("")}</Brackets>,
|
|
82
80
|
}}
|
|
83
|
-
|
|
81
|
+
/>,
|
|
84
82
|
);
|
|
85
83
|
}
|
|
86
84
|
|
package/src/app/RebaseCheck.tsx
CHANGED
|
@@ -40,8 +40,7 @@ export function RebaseCheck(props: Props) {
|
|
|
40
40
|
<YesNoPrompt
|
|
41
41
|
message={
|
|
42
42
|
<Ink.Text color={colors.yellow}>
|
|
43
|
-
<Command>git rebase</Command> detected, would you like to abort
|
|
44
|
-
it?
|
|
43
|
+
<Command>git rebase</Command> detected, would you like to abort it?
|
|
45
44
|
</Ink.Text>
|
|
46
45
|
}
|
|
47
46
|
onYes={async () => {
|
|
@@ -37,7 +37,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
37
37
|
|
|
38
38
|
const [selected_group_id, set_selected_group_id] = React.useState(() => {
|
|
39
39
|
const first_group = props.commit_range.group_list.find(
|
|
40
|
-
(g) => g.id !== props.commit_range.UNASSIGNED
|
|
40
|
+
(g) => g.id !== props.commit_range.UNASSIGNED,
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
if (first_group) {
|
|
@@ -54,14 +54,11 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
54
54
|
const next_group_list = group_list.concat(group);
|
|
55
55
|
return next_group_list;
|
|
56
56
|
},
|
|
57
|
-
[]
|
|
57
|
+
[],
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
const [commit_map, update_commit_map] = React.useReducer(
|
|
61
|
-
(
|
|
62
|
-
map: Map<string, null | string>,
|
|
63
|
-
args: { key: string; value: null | string }
|
|
64
|
-
) => {
|
|
61
|
+
(map: Map<string, null | string>, args: { key: string; value: null | string }) => {
|
|
65
62
|
map.set(args.key, args.value);
|
|
66
63
|
|
|
67
64
|
// console.debug("update_commit_map", map, args);
|
|
@@ -74,7 +71,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
return new Map(map);
|
|
77
|
-
}
|
|
74
|
+
},
|
|
78
75
|
);
|
|
79
76
|
|
|
80
77
|
const group_list: Array<SimpleGroup> = [];
|
|
@@ -88,8 +85,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
88
85
|
}
|
|
89
86
|
}
|
|
90
87
|
|
|
91
|
-
const total_group_count =
|
|
92
|
-
new_group_list.length + props.commit_range.group_list.length;
|
|
88
|
+
const total_group_count = new_group_list.length + props.commit_range.group_list.length;
|
|
93
89
|
|
|
94
90
|
for (let i = 0; i < props.commit_range.group_list.length; i++) {
|
|
95
91
|
const index = props.commit_range.group_list.length - i - 1;
|
|
@@ -411,7 +407,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
411
407
|
group: <Brackets>{title}</Brackets>,
|
|
412
408
|
note: <Parens>{id}</Parens>,
|
|
413
409
|
}}
|
|
414
|
-
|
|
410
|
+
/>,
|
|
415
411
|
);
|
|
416
412
|
|
|
417
413
|
// console.debug("submit_group_input", { title, id });
|
package/src/app/Status.tsx
CHANGED
package/src/app/StatusTable.tsx
CHANGED
|
@@ -84,10 +84,7 @@ function StatusColumn(props: TableColumnProps<Row>) {
|
|
|
84
84
|
const value = props.row[props.column];
|
|
85
85
|
|
|
86
86
|
return (
|
|
87
|
-
<Ink.Text
|
|
88
|
-
color={get_status_color(props.row)}
|
|
89
|
-
bold={get_status_bold(props.row)}
|
|
90
|
-
>
|
|
87
|
+
<Ink.Text color={get_status_color(props.row)} bold={get_status_bold(props.row)}>
|
|
91
88
|
{get_status_icon(props.row)} {value}
|
|
92
89
|
</Ink.Text>
|
|
93
90
|
);
|
package/src/app/Store.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import { immer } from "zustand/middleware/immer";
|
|
|
7
7
|
import { Exit } from "~/app/Exit";
|
|
8
8
|
import { LogTimestamp } from "~/app/LogTimestamp";
|
|
9
9
|
import { colors } from "~/core/colors";
|
|
10
|
+
import { pretty_json } from "~/core/pretty_json";
|
|
10
11
|
|
|
11
12
|
import type { Instance as InkInstance } from "ink-cjs";
|
|
12
13
|
import type { Argv } from "~/command";
|
|
@@ -29,6 +30,9 @@ type SyncGithubState = {
|
|
|
29
30
|
rebase_group_index: number;
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
// async function that returns exit code
|
|
34
|
+
type AbortHandler = () => Promise<number>;
|
|
35
|
+
|
|
32
36
|
export type State = {
|
|
33
37
|
// set immediately in `index.tsx` so no `null` scenario
|
|
34
38
|
process_argv: Array<string>;
|
|
@@ -47,6 +51,9 @@ export type State = {
|
|
|
47
51
|
pr_templates: Array<string>;
|
|
48
52
|
pr_template_body: null | string;
|
|
49
53
|
sync_github: null | SyncGithubState;
|
|
54
|
+
is_dirty_check_stash: boolean;
|
|
55
|
+
abort_handler: null | AbortHandler;
|
|
56
|
+
is_exiting: boolean;
|
|
50
57
|
|
|
51
58
|
step:
|
|
52
59
|
| "github-api-error"
|
|
@@ -71,7 +78,7 @@ export type State = {
|
|
|
71
78
|
clear(): void;
|
|
72
79
|
unmount(): void;
|
|
73
80
|
newline(): void;
|
|
74
|
-
json(value:
|
|
81
|
+
json(value: pretty_json.JSONValue): void;
|
|
75
82
|
error(message: string): void;
|
|
76
83
|
output(node: React.ReactNode): void;
|
|
77
84
|
debug(node: React.ReactNode, id?: string): void;
|
|
@@ -79,6 +86,8 @@ export type State = {
|
|
|
79
86
|
isDebug(): boolean;
|
|
80
87
|
|
|
81
88
|
reset_pr(): void;
|
|
89
|
+
register_abort_handler(abort_handler: AbortHandler): void;
|
|
90
|
+
unregister_abort_handler(): void;
|
|
82
91
|
|
|
83
92
|
set(setter: Setter): void;
|
|
84
93
|
};
|
|
@@ -113,6 +122,9 @@ const BaseStore = createStore<State>()(
|
|
|
113
122
|
pr_templates: [],
|
|
114
123
|
pr_template_body: null,
|
|
115
124
|
sync_github: null,
|
|
125
|
+
is_dirty_check_stash: false,
|
|
126
|
+
abort_handler: null,
|
|
127
|
+
is_exiting: false,
|
|
116
128
|
|
|
117
129
|
step: "loading",
|
|
118
130
|
|
|
@@ -124,6 +136,8 @@ const BaseStore = createStore<State>()(
|
|
|
124
136
|
actions: {
|
|
125
137
|
exit(code, clear = true) {
|
|
126
138
|
set((state) => {
|
|
139
|
+
state.is_exiting = true;
|
|
140
|
+
|
|
127
141
|
const node = <Exit clear={clear} code={code} />;
|
|
128
142
|
state.mutate.output(state, { node });
|
|
129
143
|
});
|
|
@@ -146,7 +160,7 @@ const BaseStore = createStore<State>()(
|
|
|
146
160
|
|
|
147
161
|
json(value) {
|
|
148
162
|
set((state) => {
|
|
149
|
-
const node =
|
|
163
|
+
const node = pretty_json(value);
|
|
150
164
|
state.mutate.output(state, { node });
|
|
151
165
|
});
|
|
152
166
|
},
|
|
@@ -189,6 +203,18 @@ const BaseStore = createStore<State>()(
|
|
|
189
203
|
});
|
|
190
204
|
},
|
|
191
205
|
|
|
206
|
+
register_abort_handler(abort_handler) {
|
|
207
|
+
set((state) => {
|
|
208
|
+
state.abort_handler = abort_handler;
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
unregister_abort_handler() {
|
|
213
|
+
set((state) => {
|
|
214
|
+
state.abort_handler = null;
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
|
|
192
218
|
set(setter) {
|
|
193
219
|
set((state) => {
|
|
194
220
|
setter(state);
|
|
@@ -238,7 +264,7 @@ const BaseStore = createStore<State>()(
|
|
|
238
264
|
return state.argv?.verbose || false;
|
|
239
265
|
},
|
|
240
266
|
},
|
|
241
|
-
}))
|
|
267
|
+
})),
|
|
242
268
|
);
|
|
243
269
|
|
|
244
270
|
function renderOutputArgs(args: MutateOutputArgs) {
|
package/src/app/SyncGithub.tsx
CHANGED
|
@@ -14,35 +14,10 @@ import { invariant } from "~/core/invariant";
|
|
|
14
14
|
import type * as CommitMetadata from "~/core/CommitMetadata";
|
|
15
15
|
|
|
16
16
|
export function SyncGithub() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
React.useEffect(function listen_sigint() {
|
|
20
|
-
process.once("SIGINT", sigint_handler);
|
|
21
|
-
|
|
22
|
-
return function cleanup() {
|
|
23
|
-
process.removeListener("SIGINT", sigint_handler);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function sigint_handler() {
|
|
27
|
-
abort_handler.current();
|
|
28
|
-
}
|
|
29
|
-
}, []);
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<Await
|
|
33
|
-
fallback={<Ink.Text color={colors.yellow}>Syncing…</Ink.Text>}
|
|
34
|
-
function={async function () {
|
|
35
|
-
await run({ abort_handler });
|
|
36
|
-
}}
|
|
37
|
-
/>
|
|
38
|
-
);
|
|
17
|
+
return <Await fallback={<Ink.Text color={colors.yellow}>Syncing…</Ink.Text>} function={run} />;
|
|
39
18
|
}
|
|
40
19
|
|
|
41
|
-
|
|
42
|
-
abort_handler: React.MutableRefObject<() => void>;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
async function run(args: Args) {
|
|
20
|
+
async function run() {
|
|
46
21
|
const state = Store.getState();
|
|
47
22
|
const actions = state.actions;
|
|
48
23
|
const argv = state.argv;
|
|
@@ -60,11 +35,12 @@ async function run(args: Args) {
|
|
|
60
35
|
const commit_range = sync_github.commit_range;
|
|
61
36
|
const rebase_group_index = sync_github.rebase_group_index;
|
|
62
37
|
|
|
63
|
-
//
|
|
64
|
-
|
|
38
|
+
// immediately register abort_handler in case of ctrl+c exit
|
|
39
|
+
actions.register_abort_handler(async function abort_sync_github() {
|
|
65
40
|
actions.output(<Ink.Text color={colors.red}>🚨 Abort</Ink.Text>);
|
|
66
|
-
handle_exit(
|
|
67
|
-
|
|
41
|
+
handle_exit();
|
|
42
|
+
return 17;
|
|
43
|
+
});
|
|
68
44
|
|
|
69
45
|
let DEFAULT_PR_BODY = "";
|
|
70
46
|
if (state.pr_template_body) {
|
|
@@ -151,6 +127,8 @@ async function run(args: Args) {
|
|
|
151
127
|
|
|
152
128
|
await Promise.all(update_pr_body_tasks);
|
|
153
129
|
|
|
130
|
+
actions.unregister_abort_handler();
|
|
131
|
+
|
|
154
132
|
actions.set((state) => {
|
|
155
133
|
state.step = "post-rebase-status";
|
|
156
134
|
});
|
|
@@ -164,7 +142,8 @@ async function run(args: Args) {
|
|
|
164
142
|
actions.error("Try again with `--verbose` to see more information.");
|
|
165
143
|
}
|
|
166
144
|
|
|
167
|
-
|
|
145
|
+
handle_exit();
|
|
146
|
+
actions.exit(18);
|
|
168
147
|
}
|
|
169
148
|
|
|
170
149
|
function get_push_group_list() {
|
|
@@ -218,10 +197,7 @@ async function run(args: Args) {
|
|
|
218
197
|
}
|
|
219
198
|
}
|
|
220
199
|
|
|
221
|
-
async function after_push(args: {
|
|
222
|
-
group: CommitMetadataGroup;
|
|
223
|
-
pr_url_list: Array<string>;
|
|
224
|
-
}) {
|
|
200
|
+
async function after_push(args: { group: CommitMetadataGroup; pr_url_list: Array<string> }) {
|
|
225
201
|
const { group, pr_url_list } = args;
|
|
226
202
|
|
|
227
203
|
invariant(group.base, "group.base must exist");
|
|
@@ -305,10 +281,8 @@ async function run(args: Args) {
|
|
|
305
281
|
}
|
|
306
282
|
}
|
|
307
283
|
|
|
308
|
-
function handle_exit(
|
|
309
|
-
actions.output(
|
|
310
|
-
<Ink.Text color={colors.yellow}>Restoring PR state…</Ink.Text>
|
|
311
|
-
);
|
|
284
|
+
function handle_exit() {
|
|
285
|
+
actions.output(<Ink.Text color={colors.yellow}>Restoring PR state…</Ink.Text>);
|
|
312
286
|
|
|
313
287
|
for (const group of push_group_list) {
|
|
314
288
|
// we may temporarily mark PR as a draft before editing it
|
|
@@ -326,11 +300,7 @@ async function run(args: Args) {
|
|
|
326
300
|
}
|
|
327
301
|
}
|
|
328
302
|
|
|
329
|
-
actions.output(
|
|
330
|
-
<Ink.Text color={colors.yellow}>Restored PR state.</Ink.Text>
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
actions.exit(code);
|
|
303
|
+
actions.output(<Ink.Text color={colors.yellow}>Restored PR state.</Ink.Text>);
|
|
334
304
|
}
|
|
335
305
|
}
|
|
336
306
|
|
package/src/app/Table.tsx
CHANGED
|
@@ -70,10 +70,7 @@ export function Table<T extends BaseRow>(props: Props<T>) {
|
|
|
70
70
|
remaining_space -= breathing_room;
|
|
71
71
|
|
|
72
72
|
if (props.fillColumn) {
|
|
73
|
-
max_col_width[props.fillColumn] = Math.min(
|
|
74
|
-
max_col_width[props.fillColumn],
|
|
75
|
-
remaining_space
|
|
76
|
-
);
|
|
73
|
+
max_col_width[props.fillColumn] = Math.min(max_col_width[props.fillColumn], remaining_space);
|
|
77
74
|
}
|
|
78
75
|
}
|
|
79
76
|
|
|
@@ -91,9 +88,7 @@ export function Table<T extends BaseRow>(props: Props<T>) {
|
|
|
91
88
|
width={available_width}
|
|
92
89
|
>
|
|
93
90
|
{RowColumnList.map((column) => {
|
|
94
|
-
const ColumnComponent = props.columns[
|
|
95
|
-
column
|
|
96
|
-
] as ColumnComponent<T>;
|
|
91
|
+
const ColumnComponent = props.columns[column] as ColumnComponent<T>;
|
|
97
92
|
|
|
98
93
|
return (
|
|
99
94
|
<Ink.Box key={String(column)} width={max_col_width[column]}>
|
|
@@ -122,14 +117,9 @@ type BaseRow = Record<string, string | number>;
|
|
|
122
117
|
|
|
123
118
|
type Column<T extends BaseRow> = keyof T;
|
|
124
119
|
|
|
125
|
-
type ColumnComponent<T extends BaseRow> = (
|
|
126
|
-
props: TableColumnProps<T>
|
|
127
|
-
) => React.ReactNode;
|
|
120
|
+
type ColumnComponent<T extends BaseRow> = (props: TableColumnProps<T>) => React.ReactNode;
|
|
128
121
|
|
|
129
|
-
type ColumnComponentMap<T extends BaseRow> = Record<
|
|
130
|
-
Column<T>,
|
|
131
|
-
ColumnComponent<T>
|
|
132
|
-
>;
|
|
122
|
+
type ColumnComponentMap<T extends BaseRow> = Record<Column<T>, ColumnComponent<T>>;
|
|
133
123
|
|
|
134
124
|
export type TableColumnProps<T extends BaseRow> = {
|
|
135
125
|
row: T;
|
package/src/app/TextInput.tsx
CHANGED
|
@@ -19,7 +19,7 @@ export function TextInput(props: Props) {
|
|
|
19
19
|
function sync_value_prop() {
|
|
20
20
|
set_value(get_value(props));
|
|
21
21
|
},
|
|
22
|
-
[props.value]
|
|
22
|
+
[props.value],
|
|
23
23
|
);
|
|
24
24
|
|
|
25
25
|
const [caret_visible, set_caret_visible] = React.useState(false);
|
|
@@ -69,12 +69,7 @@ export function TextInput(props: Props) {
|
|
|
69
69
|
// console.debug("[TextInput]", { value });
|
|
70
70
|
|
|
71
71
|
return (
|
|
72
|
-
<Ink.Box
|
|
73
|
-
borderStyle="single"
|
|
74
|
-
minHeight={1}
|
|
75
|
-
borderColor={colors.yellow}
|
|
76
|
-
borderDimColor
|
|
77
|
-
>
|
|
72
|
+
<Ink.Box borderStyle="single" minHeight={1} borderColor={colors.yellow} borderDimColor>
|
|
78
73
|
<Ink.Text>{value || ""}</Ink.Text>
|
|
79
74
|
|
|
80
75
|
<Ink.Text color={colors.yellow} dimColor inverse={caret_visible}>
|
|
@@ -12,11 +12,7 @@ type Props = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export function VerboseDebugInfo(props: Props) {
|
|
15
|
-
const fallback =
|
|
16
|
-
<Ink.Text color={colors.yellow}>
|
|
17
|
-
Logging verbose debug information…
|
|
18
|
-
</Ink.Text>
|
|
19
|
-
);
|
|
15
|
+
const fallback = <Ink.Text color={colors.yellow}>Logging verbose debug information…</Ink.Text>;
|
|
20
16
|
|
|
21
17
|
return (
|
|
22
18
|
<Await fallback={fallback} function={run}>
|
package/src/app/YesNoPrompt.tsx
CHANGED
|
@@ -2,56 +2,67 @@ import * as React from "react";
|
|
|
2
2
|
|
|
3
3
|
import * as Ink from "ink-cjs";
|
|
4
4
|
|
|
5
|
+
import { FormatText } from "~/app/FormatText";
|
|
5
6
|
import { Parens } from "~/app/Parens";
|
|
6
7
|
import { colors } from "~/core/colors";
|
|
7
8
|
|
|
9
|
+
type Handler = () => void;
|
|
10
|
+
|
|
8
11
|
type Props = {
|
|
9
12
|
message: React.ReactNode;
|
|
10
|
-
onYes
|
|
11
|
-
onNo
|
|
13
|
+
onYes: Handler;
|
|
14
|
+
onNo: Handler;
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
export function YesNoPrompt(props: Props) {
|
|
15
18
|
const [answer, set_answer] = React.useState("");
|
|
16
19
|
|
|
20
|
+
const answered_ref = React.useRef(false);
|
|
21
|
+
|
|
17
22
|
Ink.useInput((input) => {
|
|
18
|
-
|
|
23
|
+
// prevent answering multiple times
|
|
24
|
+
if (answered_ref.current) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
const input_lower = input.toLowerCase();
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
let handler: undefined | Handler;
|
|
31
|
+
|
|
32
|
+
switch (input_lower) {
|
|
23
33
|
case "n":
|
|
24
|
-
|
|
34
|
+
handler = props.onNo;
|
|
35
|
+
break;
|
|
25
36
|
|
|
26
37
|
case "y":
|
|
27
|
-
|
|
38
|
+
handler = props.onYes;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// handler if valid answer (y or n)
|
|
43
|
+
if (handler) {
|
|
44
|
+
answered_ref.current = true;
|
|
45
|
+
set_answer(input_lower);
|
|
46
|
+
handler();
|
|
28
47
|
}
|
|
29
48
|
});
|
|
30
49
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
choices = (
|
|
48
|
-
<React.Fragment>
|
|
49
|
-
{y}
|
|
50
|
-
<Ink.Text>/</Ink.Text>
|
|
51
|
-
{n}
|
|
52
|
-
</React.Fragment>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
50
|
+
const choices = (function get_choices() {
|
|
51
|
+
// prettier-ignore
|
|
52
|
+
const y = <Ink.Text bold color={colors.green}>Y</Ink.Text>;
|
|
53
|
+
const n = <Ink.Text color={colors.red}>n</Ink.Text>;
|
|
54
|
+
|
|
55
|
+
switch (answer) {
|
|
56
|
+
case "y":
|
|
57
|
+
return y;
|
|
58
|
+
|
|
59
|
+
case "n":
|
|
60
|
+
return n;
|
|
61
|
+
|
|
62
|
+
default:
|
|
63
|
+
return <FormatText message="{y}/{n}" values={{ y, n }} />;
|
|
64
|
+
}
|
|
65
|
+
})();
|
|
55
66
|
|
|
56
67
|
return (
|
|
57
68
|
<Ink.Box flexDirection="column">
|
package/src/command.ts
CHANGED
|
@@ -11,26 +11,22 @@ export async function command() {
|
|
|
11
11
|
yargs(hideBin(process.argv))
|
|
12
12
|
.usage("Usage: git stack [command] [options]")
|
|
13
13
|
|
|
14
|
-
.command("$0", "Sync commit ranges to Github", (yargs) =>
|
|
15
|
-
yargs.options(DefaultOptions)
|
|
16
|
-
)
|
|
14
|
+
.command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions))
|
|
17
15
|
|
|
18
|
-
.command(
|
|
19
|
-
"
|
|
20
|
-
"Amend staged changes to a specific commit in history",
|
|
21
|
-
(yargs) => yargs.positional("commit", FixupOptions.commit)
|
|
16
|
+
.command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) =>
|
|
17
|
+
yargs.positional("commit", FixupOptions.commit),
|
|
22
18
|
)
|
|
23
19
|
|
|
24
20
|
.command(
|
|
25
21
|
"log [args...]",
|
|
26
22
|
"Print an abbreviated log with numbered commits, useful for git stack fixup",
|
|
27
|
-
(yargs) => yargs.strict(false)
|
|
23
|
+
(yargs) => yargs.strict(false),
|
|
28
24
|
)
|
|
29
25
|
|
|
30
26
|
.command(
|
|
31
27
|
"rebase",
|
|
32
28
|
"Update local branch via rebase with latest changes from origin master branch",
|
|
33
|
-
(yargs) => yargs
|
|
29
|
+
(yargs) => yargs,
|
|
34
30
|
)
|
|
35
31
|
|
|
36
32
|
.option("verbose", GlobalOptions.verbose)
|
|
@@ -43,12 +39,8 @@ export async function command() {
|
|
|
43
39
|
// disallow unknown options
|
|
44
40
|
.strict()
|
|
45
41
|
.version(process.env.CLI_VERSION || "unknown")
|
|
46
|
-
.showHidden(
|
|
47
|
-
|
|
48
|
-
"Show hidden options via `git stack help --show-hidden`"
|
|
49
|
-
)
|
|
50
|
-
.help("help", "Show usage via `git stack help`")
|
|
51
|
-
.argv as unknown as Promise<Argv>
|
|
42
|
+
.showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
|
|
43
|
+
.help("help", "Show usage via `git stack help`").argv as unknown as Promise<Argv>
|
|
52
44
|
);
|
|
53
45
|
}
|
|
54
46
|
|
|
@@ -86,8 +78,7 @@ const DefaultOptions = {
|
|
|
86
78
|
"verify": {
|
|
87
79
|
type: "boolean",
|
|
88
80
|
default: true,
|
|
89
|
-
description:
|
|
90
|
-
"Run git hooks such as pre-commit and pre-push, disable with --no-verify",
|
|
81
|
+
description: "Run git hooks such as pre-commit and pre-push, disable with --no-verify",
|
|
91
82
|
},
|
|
92
83
|
|
|
93
84
|
"update": {
|