git-stack-cli 2.6.0 → 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 +281 -260
- package/package.json +1 -1
- 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/MultiSelect.tsx +1 -8
- package/src/app/RequireBranch.tsx +67 -0
- package/src/app/SelectCommitRanges.tsx +5 -5
- package/src/app/Status.tsx +1 -5
- package/src/app/Store.tsx +2 -1
- package/src/commands/Rebase.tsx +50 -36
- package/src/components/ColorTest.tsx +49 -0
- package/src/core/cli.ts +2 -0
- package/src/core/colors.ts +4 -2
- package/src/app/PreSelectCommitRanges.tsx +0 -29
package/package.json
CHANGED
package/scripts/bun-build.ts
CHANGED
package/src/app/App.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import { Main } from "~/app/Main";
|
|
|
14
14
|
import { Output } from "~/app/Output";
|
|
15
15
|
import { Providers } from "~/app/Providers";
|
|
16
16
|
import { RebaseCheck } from "~/app/RebaseCheck";
|
|
17
|
+
import { RequireBranch } from "~/app/RequireBranch";
|
|
17
18
|
import { Store } from "~/app/Store";
|
|
18
19
|
import { VerboseDebugInfo } from "~/app/VerboseDebugInfo";
|
|
19
20
|
import { Fixup } from "~/commands/Fixup";
|
|
@@ -112,11 +113,13 @@ function MaybeMain() {
|
|
|
112
113
|
<DependencyCheck>
|
|
113
114
|
<DirtyCheck>
|
|
114
115
|
<GatherMetadata>
|
|
115
|
-
<
|
|
116
|
-
<
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
<RequireBranch>
|
|
117
|
+
<LocalCommitStatus>
|
|
118
|
+
<DetectInitialPR>
|
|
119
|
+
<Main />
|
|
120
|
+
</DetectInitialPR>
|
|
121
|
+
</LocalCommitStatus>
|
|
122
|
+
</RequireBranch>
|
|
120
123
|
</GatherMetadata>
|
|
121
124
|
</DirtyCheck>
|
|
122
125
|
</DependencyCheck>
|
package/src/app/AutoUpdate.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { Brackets } from "~/app/Brackets";
|
|
|
6
6
|
import { Command } from "~/app/Command";
|
|
7
7
|
import { FormatText } from "~/app/FormatText";
|
|
8
8
|
import { YesNoPrompt } from "~/app/YesNoPrompt";
|
|
9
|
+
import { assertNever } from "~/core/assertNever";
|
|
9
10
|
import { cli } from "~/core/cli";
|
|
10
11
|
import { colors } from "~/core/colors";
|
|
11
12
|
import { fetch_json } from "~/core/fetch_json";
|
|
@@ -28,7 +29,7 @@ type State = {
|
|
|
28
29
|
error: null | Error;
|
|
29
30
|
local_version: null | string;
|
|
30
31
|
latest_version: null | string;
|
|
31
|
-
status: "init" | "prompt" | "install" | "done"
|
|
32
|
+
status: "init" | "prompt" | "install" | "done";
|
|
32
33
|
is_brew_bun_standalone: boolean;
|
|
33
34
|
};
|
|
34
35
|
|
|
@@ -61,7 +62,24 @@ export function AutoUpdate(props: Props) {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
React.useEffect(() => {
|
|
64
|
-
|
|
65
|
+
switch (state.status) {
|
|
66
|
+
case "init":
|
|
67
|
+
case "prompt":
|
|
68
|
+
case "install":
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case "done": {
|
|
72
|
+
props.onDone?.();
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
default:
|
|
77
|
+
assertNever(state.status);
|
|
78
|
+
}
|
|
79
|
+
}, [state.status]);
|
|
80
|
+
|
|
81
|
+
React.useEffect(() => {
|
|
82
|
+
let status: State["status"] = "init";
|
|
65
83
|
let latest_version: string | null = null;
|
|
66
84
|
let is_brew_bun_standalone = false;
|
|
67
85
|
|
|
@@ -73,17 +91,13 @@ export function AutoUpdate(props: Props) {
|
|
|
73
91
|
throw new Error("Auto update requires process.env.CLI_VERSION to be set");
|
|
74
92
|
}
|
|
75
93
|
|
|
76
|
-
if (is_output) {
|
|
77
|
-
handle_output(<Ink.Text key="init">Checking for latest version...</Ink.Text>);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
94
|
const timeout_ms = is_finite_value(props.timeoutMs) ? props.timeoutMs : 2 * 1000;
|
|
81
95
|
|
|
82
96
|
const npm_json = await Promise.race([
|
|
83
97
|
fetch_json(`https://registry.npmjs.org/${props.name}`),
|
|
84
98
|
|
|
85
99
|
sleep(timeout_ms).then(() => {
|
|
86
|
-
throw new Error("
|
|
100
|
+
throw new Error("AutoUpdate timeout");
|
|
87
101
|
}),
|
|
88
102
|
]);
|
|
89
103
|
|
|
@@ -126,8 +140,13 @@ export function AutoUpdate(props: Props) {
|
|
|
126
140
|
}
|
|
127
141
|
|
|
128
142
|
const semver_result = semver_compare(latest_version, local_version);
|
|
143
|
+
if (props_ref.current.verbose) {
|
|
144
|
+
handle_output(<Ink.Text dimColor>{JSON.stringify({ semver_result })}</Ink.Text>);
|
|
145
|
+
}
|
|
129
146
|
|
|
130
147
|
if (semver_result === 0) {
|
|
148
|
+
status = "done";
|
|
149
|
+
|
|
131
150
|
if (is_output) {
|
|
132
151
|
handle_output(
|
|
133
152
|
<Ink.Text>
|
|
@@ -138,15 +157,12 @@ export function AutoUpdate(props: Props) {
|
|
|
138
157
|
return;
|
|
139
158
|
}
|
|
140
159
|
|
|
141
|
-
if (semver_result ===
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
`latest version < local_version, skipping auto update [${latest_version} < ${local_version}]`,
|
|
145
|
-
);
|
|
160
|
+
if (semver_result === 1) {
|
|
161
|
+
// trigger yes no prompt
|
|
162
|
+
status = "prompt";
|
|
146
163
|
}
|
|
147
164
|
|
|
148
|
-
|
|
149
|
-
status = "prompt";
|
|
165
|
+
throw new Error("AutoUpdate failed");
|
|
150
166
|
}
|
|
151
167
|
|
|
152
168
|
const onError = props_ref.current.onError || (() => {});
|
|
@@ -156,9 +172,6 @@ export function AutoUpdate(props: Props) {
|
|
|
156
172
|
patch({ status, local_version, latest_version, is_brew_bun_standalone });
|
|
157
173
|
})
|
|
158
174
|
.catch((error) => {
|
|
159
|
-
patch({ status, error, local_version, latest_version, is_brew_bun_standalone });
|
|
160
|
-
onError(error);
|
|
161
|
-
|
|
162
175
|
if (props_ref.current.verbose) {
|
|
163
176
|
handle_output(
|
|
164
177
|
<Ink.Text key="error" color={colors.red}>
|
|
@@ -166,9 +179,11 @@ export function AutoUpdate(props: Props) {
|
|
|
166
179
|
</Ink.Text>,
|
|
167
180
|
);
|
|
168
181
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
182
|
+
|
|
183
|
+
// ensure we always exit
|
|
184
|
+
status = "done";
|
|
185
|
+
patch({ status, error, local_version, latest_version, is_brew_bun_standalone });
|
|
186
|
+
onError(error);
|
|
172
187
|
});
|
|
173
188
|
}, []);
|
|
174
189
|
|
|
@@ -180,54 +195,60 @@ export function AutoUpdate(props: Props) {
|
|
|
180
195
|
case "prompt": {
|
|
181
196
|
let install_command = "";
|
|
182
197
|
if (state.is_brew_bun_standalone) {
|
|
183
|
-
install_command =
|
|
198
|
+
install_command = "brew install magus/git-stack/git-stack";
|
|
184
199
|
} else {
|
|
185
|
-
install_command =
|
|
200
|
+
install_command = `npm install -g ${props.name}@latest`;
|
|
186
201
|
}
|
|
187
202
|
|
|
188
203
|
return (
|
|
189
204
|
<YesNoPrompt
|
|
190
205
|
message={
|
|
191
206
|
<Ink.Box flexDirection="column">
|
|
192
|
-
<Ink.
|
|
207
|
+
<Ink.Box flexDirection="column">
|
|
208
|
+
<Ink.Text color={colors.yellow}>
|
|
209
|
+
<FormatText
|
|
210
|
+
wrapper={<Ink.Text />}
|
|
211
|
+
message="New version available {latest_version}"
|
|
212
|
+
values={{
|
|
213
|
+
latest_version: <Brackets>{state.latest_version}</Brackets>,
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
,
|
|
217
|
+
</Ink.Text>
|
|
218
|
+
<Ink.Text> </Ink.Text>
|
|
219
|
+
<Command>{install_command}</Command>
|
|
220
|
+
<Ink.Text> </Ink.Text>
|
|
221
|
+
</Ink.Box>
|
|
222
|
+
<Ink.Box>
|
|
193
223
|
<FormatText
|
|
194
|
-
wrapper={<Ink.Text />}
|
|
195
|
-
message="
|
|
196
|
-
values={{
|
|
197
|
-
latest_version: <Brackets>{state.latest_version}</Brackets>,
|
|
198
|
-
}}
|
|
224
|
+
wrapper={<Ink.Text color={colors.yellow} />}
|
|
225
|
+
message="Would you like to run the above command to update?"
|
|
199
226
|
/>
|
|
200
|
-
|
|
201
|
-
</Ink.Text>
|
|
202
|
-
<Ink.Text> </Ink.Text>
|
|
203
|
-
<Command>{install_command}</Command>
|
|
204
|
-
<Ink.Text> </Ink.Text>
|
|
205
|
-
<FormatText
|
|
206
|
-
wrapper={<Ink.Text color={colors.yellow} />}
|
|
207
|
-
message="Would you like to run the above command to update?"
|
|
208
|
-
/>
|
|
227
|
+
</Ink.Box>
|
|
209
228
|
</Ink.Box>
|
|
210
229
|
}
|
|
211
230
|
onYes={async () => {
|
|
212
|
-
handle_output(
|
|
213
|
-
<FormatText
|
|
214
|
-
key="install"
|
|
215
|
-
wrapper={<Ink.Text />}
|
|
216
|
-
message="Installing {name}@{version}..."
|
|
217
|
-
values={{
|
|
218
|
-
name: <Ink.Text color={colors.yellow}>{props.name}</Ink.Text>,
|
|
219
|
-
version: <Ink.Text color={colors.blue}>{state.latest_version}</Ink.Text>,
|
|
220
|
-
}}
|
|
221
|
-
/>,
|
|
222
|
-
);
|
|
231
|
+
handle_output(<Command>{install_command}</Command>);
|
|
223
232
|
|
|
224
233
|
patch({ status: "install" });
|
|
225
234
|
|
|
226
|
-
await cli(install_command
|
|
235
|
+
await cli(install_command, {
|
|
236
|
+
env: {
|
|
237
|
+
...process.env,
|
|
238
|
+
HOMEBREW_COLOR: "1",
|
|
239
|
+
},
|
|
240
|
+
onOutput: (data: string) => {
|
|
241
|
+
handle_output(<Ink.Text>{data}</Ink.Text>);
|
|
242
|
+
},
|
|
243
|
+
});
|
|
227
244
|
|
|
228
|
-
|
|
245
|
+
handle_output(
|
|
246
|
+
<Ink.Text key="done">
|
|
247
|
+
✅ Installed <Brackets>{state.latest_version}</Brackets>
|
|
248
|
+
</Ink.Text>,
|
|
249
|
+
);
|
|
229
250
|
|
|
230
|
-
|
|
251
|
+
patch({ status: "done" });
|
|
231
252
|
}}
|
|
232
253
|
onNo={() => {
|
|
233
254
|
patch({ status: "done" });
|
|
@@ -239,9 +260,6 @@ export function AutoUpdate(props: Props) {
|
|
|
239
260
|
case "install":
|
|
240
261
|
return null;
|
|
241
262
|
|
|
242
|
-
case "exit":
|
|
243
|
-
return null;
|
|
244
|
-
|
|
245
263
|
case "done":
|
|
246
264
|
return props.children;
|
|
247
265
|
}
|
|
@@ -67,33 +67,10 @@ async function run() {
|
|
|
67
67
|
|
|
68
68
|
actions.debug(`master_branch = ${master_branch}`);
|
|
69
69
|
|
|
70
|
-
const branch_name = (await cli("git rev-parse --abbrev-ref HEAD")).stdout;
|
|
71
|
-
|
|
72
|
-
// handle detahed head state
|
|
73
|
-
if (branch_name === "HEAD") {
|
|
74
|
-
actions.error("Must run within a branch.");
|
|
75
|
-
actions.exit(0);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// handle when there are no detected changes
|
|
80
|
-
if (`origin/${branch_name}` === master_branch) {
|
|
81
|
-
actions.error("Must run within a branch.");
|
|
82
|
-
actions.exit(0);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
70
|
const head = (await cli("git rev-parse HEAD")).stdout;
|
|
71
|
+
const branch_name = (await cli("git rev-parse --abbrev-ref HEAD")).stdout;
|
|
87
72
|
const merge_base = (await cli(`git merge-base HEAD ${master_branch}`)).stdout;
|
|
88
73
|
|
|
89
|
-
// handle when there are no detected changes
|
|
90
|
-
if (head === merge_base) {
|
|
91
|
-
actions.newline();
|
|
92
|
-
actions.output(<Ink.Text color={colors.gray}>No changes detected.</Ink.Text>);
|
|
93
|
-
actions.exit(0);
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
74
|
// git@github.com:magus/git-multi-diff-playground.git
|
|
98
75
|
// https://github.com/magus/git-multi-diff-playground.git
|
|
99
76
|
const origin_url = (await cli(`git config --get remote.origin.url`)).stdout;
|
|
@@ -107,6 +84,7 @@ async function run() {
|
|
|
107
84
|
state.master_branch = master_branch;
|
|
108
85
|
state.head = head;
|
|
109
86
|
state.branch_name = branch_name;
|
|
87
|
+
state.merge_base = merge_base;
|
|
110
88
|
});
|
|
111
89
|
} catch (err) {
|
|
112
90
|
actions.error("Unable to gather git metadata.");
|
package/src/app/Main.tsx
CHANGED
|
@@ -6,7 +6,6 @@ import { ManualRebase } from "~/app/ManualRebase";
|
|
|
6
6
|
import { PostRebaseStatus } from "~/app/PostRebaseStatus";
|
|
7
7
|
import { PreLocalMergeRebase } from "~/app/PreLocalMergeRebase";
|
|
8
8
|
import { PreManualRebase } from "~/app/PreManualRebase";
|
|
9
|
-
import { PreSelectCommitRanges } from "~/app/PreSelectCommitRanges";
|
|
10
9
|
import { SelectCommitRanges } from "~/app/SelectCommitRanges";
|
|
11
10
|
import { Status } from "~/app/Status";
|
|
12
11
|
import { Store } from "~/app/Store";
|
|
@@ -32,9 +31,6 @@ export function Main() {
|
|
|
32
31
|
case "pre-local-merge-rebase":
|
|
33
32
|
return <PreLocalMergeRebase />;
|
|
34
33
|
|
|
35
|
-
case "pre-select-commit-ranges":
|
|
36
|
-
return <PreSelectCommitRanges />;
|
|
37
|
-
|
|
38
34
|
case "select-commit-ranges":
|
|
39
35
|
return <SelectCommitRanges />;
|
|
40
36
|
|
package/src/app/MultiSelect.tsx
CHANGED
|
@@ -191,7 +191,6 @@ type ItemRowProps = {
|
|
|
191
191
|
|
|
192
192
|
function ItemRow(props: ItemRowProps) {
|
|
193
193
|
let color;
|
|
194
|
-
let bold;
|
|
195
194
|
let underline;
|
|
196
195
|
let dimColor;
|
|
197
196
|
|
|
@@ -200,14 +199,8 @@ function ItemRow(props: ItemRowProps) {
|
|
|
200
199
|
underline = true;
|
|
201
200
|
}
|
|
202
201
|
|
|
203
|
-
if (props.selected) {
|
|
204
|
-
// color = "";
|
|
205
|
-
bold = true;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
202
|
if (props.disabled) {
|
|
209
203
|
color = "";
|
|
210
|
-
bold = false;
|
|
211
204
|
underline = false;
|
|
212
205
|
dimColor = true;
|
|
213
206
|
}
|
|
@@ -218,7 +211,7 @@ function ItemRow(props: ItemRowProps) {
|
|
|
218
211
|
|
|
219
212
|
<Ink.Box width={props.maxWidth}>
|
|
220
213
|
<Ink.Text
|
|
221
|
-
|
|
214
|
+
// force line break
|
|
222
215
|
underline={underline}
|
|
223
216
|
color={color}
|
|
224
217
|
dimColor={dimColor}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import * as Ink from "ink-cjs";
|
|
4
|
+
|
|
5
|
+
import { Await } from "~/app/Await";
|
|
6
|
+
import { Store } from "~/app/Store";
|
|
7
|
+
import { colors } from "~/core/colors";
|
|
8
|
+
import { invariant } from "~/core/invariant";
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function RequireBranch(props: Props) {
|
|
15
|
+
const fallback = <Ink.Text color={colors.yellow}>Gathering local git information…</Ink.Text>;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Await fallback={fallback} function={run}>
|
|
19
|
+
{props.children}
|
|
20
|
+
</Await>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function run() {
|
|
25
|
+
const state = Store.getState();
|
|
26
|
+
const actions = Store.getState().actions;
|
|
27
|
+
const master_branch = state.master_branch;
|
|
28
|
+
const head = state.head;
|
|
29
|
+
const branch_name = state.branch_name;
|
|
30
|
+
const merge_base = state.merge_base;
|
|
31
|
+
|
|
32
|
+
invariant(head, "head must exist");
|
|
33
|
+
invariant(branch_name, "branch_name must exist");
|
|
34
|
+
invariant(merge_base, "merge_base must exist");
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// handle detahed head state
|
|
38
|
+
if (branch_name === "HEAD") {
|
|
39
|
+
actions.error("Must run within a branch.");
|
|
40
|
+
actions.exit(0);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// handle when there are no detected changes
|
|
45
|
+
if (`origin/${branch_name}` === master_branch) {
|
|
46
|
+
actions.error("Must run within a branch.");
|
|
47
|
+
actions.exit(0);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// handle when there are no detected changes
|
|
52
|
+
if (head === merge_base) {
|
|
53
|
+
actions.newline();
|
|
54
|
+
actions.output(<Ink.Text color={colors.gray}>No changes detected.</Ink.Text>);
|
|
55
|
+
actions.exit(0);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
actions.error("Unable to detect branch changes.");
|
|
60
|
+
|
|
61
|
+
if (err instanceof Error) {
|
|
62
|
+
actions.error(err.message);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
actions.exit(17);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -232,7 +232,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
232
232
|
</Ink.Text>
|
|
233
233
|
<Ink.Text color={colors.blue}>
|
|
234
234
|
<FormatText
|
|
235
|
-
message="Press {c} to {create} a new PR
|
|
235
|
+
message="Press {c} to {create} a new PR"
|
|
236
236
|
values={{
|
|
237
237
|
c: (
|
|
238
238
|
<Ink.Text bold color={colors.green}>
|
|
@@ -271,7 +271,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
271
271
|
</Ink.Box>
|
|
272
272
|
|
|
273
273
|
<Ink.Box width={max_width}>
|
|
274
|
-
<Ink.Text wrap="truncate-end" bold color={colors.
|
|
274
|
+
<Ink.Text wrap="truncate-end" bold color={colors.white}>
|
|
275
275
|
{group.title}
|
|
276
276
|
</Ink.Text>
|
|
277
277
|
</Ink.Box>
|
|
@@ -359,7 +359,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
359
359
|
{group_input ? null : (
|
|
360
360
|
<FormatText
|
|
361
361
|
wrapper={<Ink.Text color={colors.gray} />}
|
|
362
|
-
message="Press {c} to {create} a new PR
|
|
362
|
+
message="Press {c} to {create} a new PR"
|
|
363
363
|
values={{
|
|
364
364
|
c: (
|
|
365
365
|
<Ink.Text bold color={colors.green}>
|
|
@@ -411,7 +411,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
411
411
|
<Ink.Box>
|
|
412
412
|
<FormatText
|
|
413
413
|
wrapper={<Ink.Text color={colors.gray} />}
|
|
414
|
-
message="Press {left} and {right} to view
|
|
414
|
+
message="Press {left} and {right} to view PRs"
|
|
415
415
|
values={{
|
|
416
416
|
left: (
|
|
417
417
|
<Ink.Text bold color={colors.green}>
|
|
@@ -463,7 +463,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
463
463
|
actions.output(
|
|
464
464
|
<FormatText
|
|
465
465
|
wrapper={<Ink.Text dimColor />}
|
|
466
|
-
message="Created new PR
|
|
466
|
+
message="Created new PR {group} {note}"
|
|
467
467
|
values={{
|
|
468
468
|
group: <Brackets>{title}</Brackets>,
|
|
469
469
|
note: <Parens>{id}</Parens>,
|
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/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
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { colors } from "~/core/colors";
|
|
4
|
+
|
|
5
|
+
type RenderOptions = {
|
|
6
|
+
color: string;
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
children: (render_options: RenderOptions) => React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function ColorTest(props: Props) {
|
|
15
|
+
return (
|
|
16
|
+
<React.Fragment>
|
|
17
|
+
{Object.entries(colors).map(([key, color]) => {
|
|
18
|
+
const name = `colors:${key}`;
|
|
19
|
+
return props.children({ color, name });
|
|
20
|
+
})}
|
|
21
|
+
|
|
22
|
+
{INK_COLORS.map((color) => {
|
|
23
|
+
const name = `ink:${color}`;
|
|
24
|
+
return props.children({ color, name });
|
|
25
|
+
})}
|
|
26
|
+
</React.Fragment>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ForegroundColor
|
|
31
|
+
// https://github.com/magus/git-stack-cli/blob/master/node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/vendor/ansi-styles/index.d.ts#L75
|
|
32
|
+
const INK_COLORS = [
|
|
33
|
+
"black",
|
|
34
|
+
"red",
|
|
35
|
+
"green",
|
|
36
|
+
"yellow",
|
|
37
|
+
"blue",
|
|
38
|
+
"cyan",
|
|
39
|
+
"magenta",
|
|
40
|
+
"white",
|
|
41
|
+
"blackBright",
|
|
42
|
+
"redBright",
|
|
43
|
+
"greenBright",
|
|
44
|
+
"yellowBright",
|
|
45
|
+
"blueBright",
|
|
46
|
+
"cyanBright",
|
|
47
|
+
"magentaBright",
|
|
48
|
+
"whiteBright",
|
|
49
|
+
];
|