git-stack-cli 2.9.3 → 2.9.5
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 +77 -77
- package/package.json +1 -1
- package/src/app/DebugOutput.tsx +40 -0
- package/src/app/GithubApiError.tsx +31 -28
- package/src/app/ManualRebase.tsx +6 -1
- package/src/app/Output.tsx +8 -17
- package/src/app/Store.tsx +35 -71
- package/src/app/SyncGithub.tsx +22 -58
- package/src/app/VerboseDebugInfo.tsx +8 -0
- package/src/core/CommitMetadata.ts +88 -58
- package/src/core/cache_message.tsx +36 -0
- package/src/core/cli.ts +12 -5
- package/src/core/git.tsx +193 -0
- package/src/core/github.tsx +136 -76
- package/src/index.tsx +0 -5
- package/src/types/global.d.ts +1 -0
- package/src/app/LogTimestamp.tsx +0 -8
- package/src/core/git.ts +0 -83
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import * as Ink from "ink-cjs";
|
|
4
|
+
import { DateTime } from "luxon";
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
node: React.ReactNode;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function DebugOutput(props: Props) {
|
|
11
|
+
const { stdout } = Ink.useStdout();
|
|
12
|
+
const available_width = stdout.columns;
|
|
13
|
+
|
|
14
|
+
const timestamp = DateTime.now().toFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
|
15
|
+
const content_width = available_width - timestamp.length - 2;
|
|
16
|
+
|
|
17
|
+
const content = (function () {
|
|
18
|
+
switch (typeof props.node) {
|
|
19
|
+
case "boolean":
|
|
20
|
+
case "number":
|
|
21
|
+
case "string": {
|
|
22
|
+
return <Ink.Text dimColor>{String(props.node)}</Ink.Text>;
|
|
23
|
+
}
|
|
24
|
+
default:
|
|
25
|
+
return props.node;
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Ink.Box flexDirection="column">
|
|
31
|
+
<Ink.Box flexDirection="row" gap={1} width={available_width}>
|
|
32
|
+
<Ink.Box width={timestamp.length} flexDirection="column">
|
|
33
|
+
<Ink.Text dimColor>{timestamp}</Ink.Text>
|
|
34
|
+
</Ink.Box>
|
|
35
|
+
|
|
36
|
+
<Ink.Box width={content_width}>{content}</Ink.Box>
|
|
37
|
+
</Ink.Box>
|
|
38
|
+
</Ink.Box>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -4,6 +4,7 @@ import * as Ink from "ink-cjs";
|
|
|
4
4
|
|
|
5
5
|
import { Await } from "~/app/Await";
|
|
6
6
|
import { Brackets } from "~/app/Brackets";
|
|
7
|
+
import { FormatText } from "~/app/FormatText";
|
|
7
8
|
import { Parens } from "~/app/Parens";
|
|
8
9
|
import { Store } from "~/app/Store";
|
|
9
10
|
import { cli } from "~/core/cli";
|
|
@@ -51,34 +52,36 @@ async function run(props: Props) {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
actions.output(
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
55
|
+
<FormatText
|
|
56
|
+
message="Github {graphql} API rate limit {ratio} will reset at {reset_time} {time_until}"
|
|
57
|
+
values={{
|
|
58
|
+
graphql: <Brackets>graphql</Brackets>,
|
|
59
|
+
ratio: (
|
|
60
|
+
<Brackets>
|
|
61
|
+
<FormatText message="{used}/{limit}" values={{ used, limit }} />
|
|
62
|
+
</Brackets>
|
|
63
|
+
),
|
|
64
|
+
reset_time: (
|
|
65
|
+
<Ink.Text bold color={colors.yellow}>
|
|
66
|
+
{reset_time}
|
|
67
|
+
</Ink.Text>
|
|
68
|
+
),
|
|
69
|
+
time_until: (
|
|
70
|
+
<Parens>
|
|
71
|
+
<FormatText
|
|
72
|
+
message="in {time_until}"
|
|
73
|
+
values={{
|
|
74
|
+
time_until: (
|
|
75
|
+
<Ink.Text bold color={colors.yellow}>
|
|
76
|
+
{time_until}
|
|
77
|
+
</Ink.Text>
|
|
78
|
+
),
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
</Parens>
|
|
82
|
+
),
|
|
83
|
+
}}
|
|
84
|
+
/>,
|
|
82
85
|
);
|
|
83
86
|
|
|
84
87
|
if (props.exit) {
|
package/src/app/ManualRebase.tsx
CHANGED
|
@@ -47,6 +47,11 @@ async function run() {
|
|
|
47
47
|
// get latest merge_base relative to local master
|
|
48
48
|
const merge_base = (await cli(`git merge-base HEAD ${master_branch}`)).stdout;
|
|
49
49
|
|
|
50
|
+
// ensure merge_base is updated
|
|
51
|
+
actions.set((state) => {
|
|
52
|
+
state.merge_base = merge_base;
|
|
53
|
+
});
|
|
54
|
+
|
|
50
55
|
// immediately paint all commit to preserve selected commit ranges
|
|
51
56
|
let commit_range = await CommitMetadata.range(commit_map);
|
|
52
57
|
|
|
@@ -116,7 +121,7 @@ async function run() {
|
|
|
116
121
|
if (argv.sync) {
|
|
117
122
|
actions.set((state) => {
|
|
118
123
|
state.step = "sync-github";
|
|
119
|
-
state.sync_github = { commit_range
|
|
124
|
+
state.sync_github = { commit_range };
|
|
120
125
|
});
|
|
121
126
|
} else {
|
|
122
127
|
actions.set((state) => {
|
package/src/app/Output.tsx
CHANGED
|
@@ -2,35 +2,26 @@ import * as React from "react";
|
|
|
2
2
|
|
|
3
3
|
import * as Ink from "ink-cjs";
|
|
4
4
|
|
|
5
|
+
import { DebugOutput } from "~/app/DebugOutput";
|
|
5
6
|
import { Store } from "~/app/Store";
|
|
6
7
|
|
|
7
8
|
export function Output() {
|
|
8
9
|
const output = Store.useState((state) => state.output);
|
|
9
10
|
const pending_output = Store.useState((state) => state.pending_output);
|
|
10
|
-
const pending_output_items = Object.values(pending_output);
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
13
|
<React.Fragment>
|
|
14
14
|
<Ink.Static items={output}>
|
|
15
|
-
{(
|
|
16
|
-
|
|
15
|
+
{(entry) => {
|
|
16
|
+
const [id, node] = entry;
|
|
17
|
+
return <Ink.Box key={id}>{node}</Ink.Box>;
|
|
17
18
|
}}
|
|
18
19
|
</Ink.Static>
|
|
19
20
|
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{node_list.map((text, j) => {
|
|
25
|
-
return (
|
|
26
|
-
<React.Fragment key={j}>
|
|
27
|
-
<Ink.Text>{text}</Ink.Text>
|
|
28
|
-
</React.Fragment>
|
|
29
|
-
);
|
|
30
|
-
})}
|
|
31
|
-
</Ink.Text>
|
|
32
|
-
</Ink.Box>
|
|
33
|
-
);
|
|
21
|
+
{Object.entries(pending_output).map((entry) => {
|
|
22
|
+
const [id, content_list] = entry;
|
|
23
|
+
const content = content_list.join("");
|
|
24
|
+
return <DebugOutput key={id} node={content} />;
|
|
34
25
|
})}
|
|
35
26
|
</React.Fragment>
|
|
36
27
|
);
|
package/src/app/Store.tsx
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
|
|
3
5
|
import * as Ink from "ink-cjs";
|
|
4
6
|
import { createStore, useStore } from "zustand";
|
|
5
7
|
import { immer } from "zustand/middleware/immer";
|
|
6
8
|
|
|
9
|
+
import { DebugOutput } from "~/app/DebugOutput";
|
|
7
10
|
import { Exit } from "~/app/Exit";
|
|
8
|
-
import { LogTimestamp } from "~/app/LogTimestamp";
|
|
9
11
|
import { colors } from "~/core/colors";
|
|
10
12
|
import { pretty_json } from "~/core/pretty_json";
|
|
11
13
|
|
|
@@ -21,13 +23,10 @@ type CommitMap = Parameters<typeof CommitMetadata.range>[0];
|
|
|
21
23
|
type MutateOutputArgs = {
|
|
22
24
|
node: React.ReactNode;
|
|
23
25
|
id?: string;
|
|
24
|
-
debug?: boolean;
|
|
25
|
-
withoutTimestamp?: boolean;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
type SyncGithubState = {
|
|
29
29
|
commit_range: CommitMetadata.CommitRange;
|
|
30
|
-
rebase_group_index: number;
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
// async function that returns exit code
|
|
@@ -73,12 +72,13 @@ export type State = {
|
|
|
73
72
|
| "sync-github"
|
|
74
73
|
| "post-rebase-status";
|
|
75
74
|
|
|
76
|
-
output: Array<React.ReactNode>;
|
|
77
|
-
pending_output: Record<string, Array<
|
|
75
|
+
output: Array<[string, React.ReactNode]>;
|
|
76
|
+
pending_output: Record<string, Array<string>>;
|
|
78
77
|
|
|
79
78
|
// cache
|
|
80
79
|
pr: { [branch: string]: PullRequest };
|
|
81
|
-
|
|
80
|
+
cache_gh_cli_by_branch: { [branch: string]: { [command: string]: string } };
|
|
81
|
+
cache_diff: { [key: string]: string };
|
|
82
82
|
|
|
83
83
|
actions: {
|
|
84
84
|
exit(code: number, args?: ExitArgs): void;
|
|
@@ -88,7 +88,9 @@ export type State = {
|
|
|
88
88
|
json(value: pretty_json.JSONValue): void;
|
|
89
89
|
error(error: unknown): void;
|
|
90
90
|
output(node: React.ReactNode): void;
|
|
91
|
-
debug(node: React.ReactNode
|
|
91
|
+
debug(node: React.ReactNode): void;
|
|
92
|
+
debug_pending(id: string, content: string): void;
|
|
93
|
+
debug_pending_end(id: string): void;
|
|
92
94
|
|
|
93
95
|
isDebug(): boolean;
|
|
94
96
|
|
|
@@ -100,8 +102,6 @@ export type State = {
|
|
|
100
102
|
|
|
101
103
|
mutate: {
|
|
102
104
|
output(state: State, args: MutateOutputArgs): void;
|
|
103
|
-
pending_output(state: State, args: MutateOutputArgs): void;
|
|
104
|
-
end_pending_output(state: State, id: string): void;
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
select: {
|
|
@@ -139,7 +139,8 @@ const BaseStore = createStore<State>()(
|
|
|
139
139
|
pending_output: {},
|
|
140
140
|
|
|
141
141
|
pr: {},
|
|
142
|
-
|
|
142
|
+
cache_gh_cli_by_branch: {},
|
|
143
|
+
cache_diff: {},
|
|
143
144
|
|
|
144
145
|
actions: {
|
|
145
146
|
exit(code, args) {
|
|
@@ -207,24 +208,39 @@ const BaseStore = createStore<State>()(
|
|
|
207
208
|
|
|
208
209
|
output(node) {
|
|
209
210
|
set((state) => {
|
|
211
|
+
if (typeof node === "string") {
|
|
212
|
+
node = <Ink.Text>{node}</Ink.Text>;
|
|
213
|
+
}
|
|
210
214
|
state.mutate.output(state, { node });
|
|
211
215
|
});
|
|
212
216
|
},
|
|
213
217
|
|
|
214
|
-
debug(node
|
|
218
|
+
debug(node) {
|
|
215
219
|
if (get().actions.isDebug()) {
|
|
216
|
-
|
|
220
|
+
set((state) => {
|
|
221
|
+
state.mutate.output(state, { node: <DebugOutput node={node} /> });
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
},
|
|
217
225
|
|
|
226
|
+
debug_pending(id, content) {
|
|
227
|
+
if (get().actions.isDebug()) {
|
|
218
228
|
set((state) => {
|
|
219
|
-
if (id) {
|
|
220
|
-
state.
|
|
221
|
-
} else {
|
|
222
|
-
state.mutate.output(state, { node, debug });
|
|
229
|
+
if (!state.pending_output[id]) {
|
|
230
|
+
state.pending_output[id] = [];
|
|
223
231
|
}
|
|
232
|
+
|
|
233
|
+
state.pending_output[id].push(content);
|
|
224
234
|
});
|
|
225
235
|
}
|
|
226
236
|
},
|
|
227
237
|
|
|
238
|
+
debug_pending_end(id) {
|
|
239
|
+
set((state) => {
|
|
240
|
+
delete state.pending_output[id];
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
|
|
228
244
|
isDebug() {
|
|
229
245
|
const state = get();
|
|
230
246
|
return state.select.debug(state);
|
|
@@ -251,38 +267,8 @@ const BaseStore = createStore<State>()(
|
|
|
251
267
|
|
|
252
268
|
mutate: {
|
|
253
269
|
output(state, args) {
|
|
254
|
-
const
|
|
255
|
-
state.output.push(
|
|
256
|
-
},
|
|
257
|
-
|
|
258
|
-
pending_output(state, args) {
|
|
259
|
-
const { id } = args;
|
|
260
|
-
|
|
261
|
-
if (!id) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// set `withoutTimestamp` to skip <LogTimestamp> for all subsequent pending outputs
|
|
266
|
-
// we only want to timestamp for the first part (when we initialize the [])
|
|
267
|
-
// if we have many incremental outputs on the same line we do not want multiple timestamps
|
|
268
|
-
//
|
|
269
|
-
// await Promise.all([
|
|
270
|
-
// cli(`for i in $(seq 1 5); do echo $i; sleep 1; done`),
|
|
271
|
-
// cli(`for i in $(seq 5 1); do printf "$i "; sleep 1; done; echo`),
|
|
272
|
-
// ]);
|
|
273
|
-
//
|
|
274
|
-
let withoutTimestamp = true;
|
|
275
|
-
if (!state.pending_output[id]) {
|
|
276
|
-
withoutTimestamp = false;
|
|
277
|
-
state.pending_output[id] = [];
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const renderOutput = renderOutputArgs({ ...args, withoutTimestamp });
|
|
281
|
-
state.pending_output[id].push(renderOutput);
|
|
282
|
-
},
|
|
283
|
-
|
|
284
|
-
end_pending_output(state, id) {
|
|
285
|
-
delete state.pending_output[id];
|
|
270
|
+
const id = crypto.randomUUID();
|
|
271
|
+
state.output.push([id, args.node]);
|
|
286
272
|
},
|
|
287
273
|
},
|
|
288
274
|
|
|
@@ -294,28 +280,6 @@ const BaseStore = createStore<State>()(
|
|
|
294
280
|
})),
|
|
295
281
|
);
|
|
296
282
|
|
|
297
|
-
function renderOutputArgs(args: MutateOutputArgs) {
|
|
298
|
-
let output = args.node;
|
|
299
|
-
|
|
300
|
-
switch (typeof args.node) {
|
|
301
|
-
case "boolean":
|
|
302
|
-
case "number":
|
|
303
|
-
case "string":
|
|
304
|
-
output = <Ink.Text dimColor={args.debug}>{String(args.node)}</Ink.Text>;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (args.debug) {
|
|
308
|
-
return (
|
|
309
|
-
<React.Fragment>
|
|
310
|
-
{args.withoutTimestamp ? null : <LogTimestamp />}
|
|
311
|
-
{output}
|
|
312
|
-
</React.Fragment>
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return output;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
283
|
function useState<R>(selector: (state: State) => R): R {
|
|
320
284
|
return useStore(BaseStore, selector);
|
|
321
285
|
}
|
package/src/app/SyncGithub.tsx
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
3
|
import * as Ink from "ink-cjs";
|
|
6
4
|
import last from "lodash/last";
|
|
7
5
|
|
|
@@ -10,9 +8,9 @@ import { Store } from "~/app/Store";
|
|
|
10
8
|
import * as StackSummaryTable from "~/core/StackSummaryTable";
|
|
11
9
|
import { cli } from "~/core/cli";
|
|
12
10
|
import { colors } from "~/core/colors";
|
|
11
|
+
import * as git from "~/core/git";
|
|
13
12
|
import * as github from "~/core/github";
|
|
14
13
|
import { invariant } from "~/core/invariant";
|
|
15
|
-
import { safe_exists } from "~/core/safe_exists";
|
|
16
14
|
import { sleep } from "~/core/sleep";
|
|
17
15
|
|
|
18
16
|
import type * as CommitMetadata from "~/core/CommitMetadata";
|
|
@@ -28,16 +26,15 @@ async function run() {
|
|
|
28
26
|
const branch_name = state.branch_name;
|
|
29
27
|
const commit_map = state.commit_map;
|
|
30
28
|
const master_branch = state.master_branch;
|
|
31
|
-
const
|
|
29
|
+
const repo_path = state.repo_path;
|
|
32
30
|
const sync_github = state.sync_github;
|
|
33
31
|
|
|
34
32
|
invariant(branch_name, "branch_name must exist");
|
|
35
33
|
invariant(commit_map, "commit_map must exist");
|
|
36
|
-
invariant(
|
|
34
|
+
invariant(repo_path, "repo_path must exist");
|
|
37
35
|
invariant(sync_github, "sync_github must exist");
|
|
38
36
|
|
|
39
37
|
const commit_range = sync_github.commit_range;
|
|
40
|
-
const rebase_group_index = sync_github.rebase_group_index;
|
|
41
38
|
|
|
42
39
|
let DEFAULT_PR_BODY = "";
|
|
43
40
|
if (state.pr_template_body) {
|
|
@@ -46,9 +43,6 @@ async function run() {
|
|
|
46
43
|
|
|
47
44
|
const push_group_list = get_push_group_list();
|
|
48
45
|
|
|
49
|
-
// console.debug({ push_group_list });
|
|
50
|
-
// throw new Error("STOP");
|
|
51
|
-
|
|
52
46
|
// for all push targets in push_group_list
|
|
53
47
|
// things that can be done in parallel are grouped by numbers
|
|
54
48
|
//
|
|
@@ -165,7 +159,7 @@ async function run() {
|
|
|
165
159
|
for (const group of push_group_list) {
|
|
166
160
|
if (group.pr) {
|
|
167
161
|
delete state.pr[group.pr.headRefName];
|
|
168
|
-
delete state.
|
|
162
|
+
delete state.cache_gh_cli_by_branch[group.pr.headRefName];
|
|
169
163
|
}
|
|
170
164
|
}
|
|
171
165
|
});
|
|
@@ -192,24 +186,14 @@ async function run() {
|
|
|
192
186
|
}
|
|
193
187
|
|
|
194
188
|
function get_push_group_list() {
|
|
195
|
-
// start from HEAD and work backward to rebase_group_index
|
|
196
189
|
const push_group_list = [];
|
|
197
190
|
|
|
198
|
-
for (let
|
|
199
|
-
const index = commit_range.group_list.length - 1 - i;
|
|
200
|
-
|
|
201
|
-
// do not go past rebase_group_index
|
|
202
|
-
if (index < rebase_group_index) {
|
|
203
|
-
break;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const group = commit_range.group_list[index];
|
|
207
|
-
|
|
191
|
+
for (let group of commit_range.group_list) {
|
|
208
192
|
// skip the unassigned commits group
|
|
209
193
|
if (group.id === commit_range.UNASSIGNED) continue;
|
|
210
194
|
|
|
211
|
-
// if not --force, skip non-dirty
|
|
212
|
-
if (
|
|
195
|
+
// if not --force, skip non-dirty groups
|
|
196
|
+
if (!group.dirty && !argv.force) continue;
|
|
213
197
|
|
|
214
198
|
push_group_list.unshift(group);
|
|
215
199
|
}
|
|
@@ -240,7 +224,7 @@ async function run() {
|
|
|
240
224
|
// Unable to sync.
|
|
241
225
|
// ```
|
|
242
226
|
//
|
|
243
|
-
if (!
|
|
227
|
+
if (!is_pr_master_base(group)) {
|
|
244
228
|
await github.pr_edit({
|
|
245
229
|
branch: group.id,
|
|
246
230
|
base: master_branch,
|
|
@@ -258,7 +242,15 @@ async function run() {
|
|
|
258
242
|
invariant(group.base, "group.base must exist");
|
|
259
243
|
|
|
260
244
|
if (group.pr) {
|
|
261
|
-
|
|
245
|
+
// there are two scenarios where we should restore the base after push
|
|
246
|
+
// 1. if we aren't master base and pr is master base we should fix it
|
|
247
|
+
const base_mismatch = !group.master_base && is_pr_master_base(group);
|
|
248
|
+
// 2. if group pr was not master before the push we set it to master before pushing
|
|
249
|
+
// now we need to restore it back to how it was before the before_push
|
|
250
|
+
const was_modified_before_push = !is_pr_master_base(group);
|
|
251
|
+
|
|
252
|
+
const needs_base_fix = base_mismatch || was_modified_before_push;
|
|
253
|
+
if (needs_base_fix) {
|
|
262
254
|
// ensure base matches pr in github
|
|
263
255
|
await github.pr_edit({ branch: group.id, base: group.base });
|
|
264
256
|
}
|
|
@@ -313,47 +305,19 @@ async function run() {
|
|
|
313
305
|
}
|
|
314
306
|
}
|
|
315
307
|
|
|
316
|
-
function
|
|
308
|
+
function is_pr_master_base(group: CommitMetadataGroup) {
|
|
317
309
|
if (!group.pr) {
|
|
318
310
|
return false;
|
|
319
311
|
}
|
|
320
312
|
|
|
321
|
-
return
|
|
313
|
+
return `origin/${group.pr.baseRefName}` === master_branch;
|
|
322
314
|
}
|
|
323
315
|
|
|
324
316
|
async function push_master_group(group: CommitMetadataGroup) {
|
|
325
|
-
invariant(
|
|
326
|
-
|
|
327
|
-
const repo_rel_worktree_path = `.git/git-stack-worktrees/push_master_group`;
|
|
328
|
-
const worktree_path = path.join(repo_root, repo_rel_worktree_path);
|
|
329
|
-
|
|
330
|
-
// ensure worktree for pushing master groups
|
|
331
|
-
if (!(await safe_exists(worktree_path))) {
|
|
332
|
-
actions.output(
|
|
333
|
-
<Ink.Text color={colors.white}>
|
|
334
|
-
Creating <Ink.Text color={colors.yellow}>{repo_rel_worktree_path}</Ink.Text>
|
|
335
|
-
</Ink.Text>,
|
|
336
|
-
);
|
|
337
|
-
actions.output(
|
|
338
|
-
<Ink.Text color={colors.gray}>(this may take a moment the first time…)</Ink.Text>,
|
|
339
|
-
);
|
|
340
|
-
await cli(`git worktree add -f ${worktree_path} ${master_branch}`);
|
|
341
|
-
}
|
|
317
|
+
invariant(repo_path, "repo_path must exist");
|
|
342
318
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
// - drop local changes/untracked files (including ignored) for a truly fresh state
|
|
346
|
-
// - reset to the desired base
|
|
347
|
-
await cli(`git -C ${worktree_path} cherry-pick --abort`, { ignoreExitCode: true });
|
|
348
|
-
await cli(`git -C ${worktree_path} rebase --abort`, { ignoreExitCode: true });
|
|
349
|
-
await cli(`git -C ${worktree_path} merge --abort`, { ignoreExitCode: true });
|
|
350
|
-
await cli(`git -C ${worktree_path} checkout -f ${master_branch}`);
|
|
351
|
-
await cli(`git -C ${worktree_path} reset --hard ${master_branch}`);
|
|
352
|
-
await cli(`git -C ${worktree_path} clean -fd`);
|
|
353
|
-
|
|
354
|
-
// cherry-pick the group commits onto that base
|
|
355
|
-
const cp_commit_list = group.commits.map((c) => c.sha).join(" ");
|
|
356
|
-
await cli(`git -C ${worktree_path} cherry-pick ${cp_commit_list}`);
|
|
319
|
+
const commit_list = group.commits;
|
|
320
|
+
const { worktree_path } = await git.worktree_add({ commit_list });
|
|
357
321
|
|
|
358
322
|
const push_target = `HEAD:refs/heads/${group.id}`;
|
|
359
323
|
const git_push_command = create_git_push_command(`git -C ${worktree_path}`, push_target);
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
3
5
|
import * as Ink from "ink-cjs";
|
|
4
6
|
|
|
5
7
|
import { Await } from "~/app/Await";
|
|
6
8
|
import { Store } from "~/app/Store";
|
|
7
9
|
import { cli } from "~/core/cli";
|
|
8
10
|
import { colors } from "~/core/colors";
|
|
11
|
+
import { pretty_json } from "~/core/pretty_json";
|
|
9
12
|
|
|
10
13
|
type Props = {
|
|
11
14
|
children: React.ReactNode;
|
|
@@ -29,6 +32,11 @@ async function run() {
|
|
|
29
32
|
await cli(`echo USER=$USER`);
|
|
30
33
|
await cli(`echo GIT_AUTHOR_NAME=$GIT_AUTHOR_NAME`);
|
|
31
34
|
await cli(`echo GIT_AUTHOR_EMAIL=$GIT_AUTHOR_EMAIL`);
|
|
35
|
+
|
|
36
|
+
const PATH = process.env["PATH"];
|
|
37
|
+
const PATH_LIST = pretty_json(PATH.split(path.delimiter));
|
|
38
|
+
actions.debug(`process.env.PATH ${PATH_LIST}`);
|
|
39
|
+
|
|
32
40
|
await cli(`git config --list --show-origin`);
|
|
33
41
|
} catch (err) {
|
|
34
42
|
actions.error("Unable to log verbose debug information.");
|