git-stack-cli 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/cjs/index.cjs +154 -203
- package/package.json +1 -1
- package/src/app/AutoUpdate.tsx +5 -3
- package/src/app/CherryPickCheck.tsx +4 -4
- package/src/app/Debug.tsx +12 -9
- package/src/app/DependencyCheck.tsx +0 -6
- package/src/app/ManualRebase.tsx +114 -183
- package/src/app/PreManualRebase.tsx +6 -5
- package/src/app/RebaseCheck.tsx +3 -3
- package/src/command.ts +0 -16
- package/src/commands/Rebase.tsx +2 -15
- package/src/core/GitReviseTodo.ts +3 -3
- package/src/core/github.tsx +18 -9
- package/src/core/read_json.ts +3 -3
- package/src/core/safe_exists.ts +10 -0
- package/src/core/safe_rm.ts +10 -0
package/src/app/AutoUpdate.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import fs from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
6
6
|
import * as Ink from "ink-cjs";
|
|
@@ -90,7 +90,8 @@ export function AutoUpdate(props: Props) {
|
|
|
90
90
|
throw new Error("Unable to retrieve latest version from npm");
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
const
|
|
93
|
+
const script_path = await fs.realpath(process.argv[1]);
|
|
94
|
+
const script_dir = path.dirname(script_path);
|
|
94
95
|
|
|
95
96
|
// dist/ts/index.js
|
|
96
97
|
const package_json_path = path.join(
|
|
@@ -100,7 +101,8 @@ export function AutoUpdate(props: Props) {
|
|
|
100
101
|
"package.json"
|
|
101
102
|
);
|
|
102
103
|
|
|
103
|
-
|
|
104
|
+
type PackageJson = { version: string };
|
|
105
|
+
const package_json = await read_json<PackageJson>(package_json_path);
|
|
104
106
|
|
|
105
107
|
if (!package_json) {
|
|
106
108
|
// unable to find read package.json, skip auto update
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
|
|
6
5
|
import * as Ink from "ink-cjs";
|
|
@@ -11,6 +10,7 @@ import { Store } from "~/app/Store";
|
|
|
11
10
|
import { YesNoPrompt } from "~/app/YesNoPrompt";
|
|
12
11
|
import { cli } from "~/core/cli";
|
|
13
12
|
import { colors } from "~/core/colors";
|
|
13
|
+
import { safe_exists } from "~/core/safe_exists";
|
|
14
14
|
|
|
15
15
|
type Props = {
|
|
16
16
|
children: React.ReactNode;
|
|
@@ -73,11 +73,11 @@ export function CherryPickCheck(props: Props) {
|
|
|
73
73
|
try {
|
|
74
74
|
const git_dir = (await cli(`git rev-parse --absolute-git-dir`)).stdout;
|
|
75
75
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
);
|
|
76
|
+
const cherry_pick_file = path.join(git_dir, "CHERRY_PICK_HEAD");
|
|
77
|
+
const is_cherry_pick = await safe_exists(cherry_pick_file);
|
|
79
78
|
|
|
80
79
|
const status = is_cherry_pick ? "prompt" : "done";
|
|
80
|
+
|
|
81
81
|
patch({ status });
|
|
82
82
|
} catch (err) {
|
|
83
83
|
actions.error("Must be run from within a git repository.");
|
package/src/app/Debug.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import fs from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
6
6
|
import * as Ink from "ink-cjs";
|
|
@@ -8,6 +8,7 @@ import * as Ink from "ink-cjs";
|
|
|
8
8
|
import { Store } from "~/app/Store";
|
|
9
9
|
import { colors } from "~/core/colors";
|
|
10
10
|
import * as json from "~/core/json";
|
|
11
|
+
import { safe_rm } from "~/core/safe_rm";
|
|
11
12
|
|
|
12
13
|
export function Debug() {
|
|
13
14
|
const actions = Store.useActions();
|
|
@@ -27,20 +28,22 @@ export function Debug() {
|
|
|
27
28
|
);
|
|
28
29
|
|
|
29
30
|
React.useEffect(
|
|
30
|
-
function
|
|
31
|
+
function sync_state_json() {
|
|
31
32
|
if (!argv?.["write-state-json"]) {
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
sync().catch(actions.error);
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
async function sync() {
|
|
39
|
+
const output_file = path.join(state.cwd, "git-stack-state.json");
|
|
40
|
+
|
|
41
|
+
await safe_rm(output_file);
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
const serialized = json.serialize(state);
|
|
44
|
+
const content = JSON.stringify(serialized, null, 2);
|
|
45
|
+
await fs.writeFile(output_file, content);
|
|
46
|
+
}
|
|
44
47
|
},
|
|
45
48
|
[argv, state]
|
|
46
49
|
);
|
|
@@ -163,12 +163,6 @@ function CheckGithubCliAuth(props: Props) {
|
|
|
163
163
|
|
|
164
164
|
function CheckGitRevise(props: Props) {
|
|
165
165
|
const actions = Store.useActions();
|
|
166
|
-
const argv = Store.useState((state) => state.argv);
|
|
167
|
-
|
|
168
|
-
// skip git revise check when `rebase` is not git-revise
|
|
169
|
-
if (argv?.["rebase"] !== "git-revise") {
|
|
170
|
-
return props.children;
|
|
171
|
-
}
|
|
172
166
|
|
|
173
167
|
return (
|
|
174
168
|
<Await
|
package/src/app/ManualRebase.tsx
CHANGED
|
@@ -10,7 +10,6 @@ import { FormatText } from "~/app/FormatText";
|
|
|
10
10
|
import { Store } from "~/app/Store";
|
|
11
11
|
import * as CommitMetadata from "~/core/CommitMetadata";
|
|
12
12
|
import { GitReviseTodo } from "~/core/GitReviseTodo";
|
|
13
|
-
import * as Metadata from "~/core/Metadata";
|
|
14
13
|
import * as StackSummaryTable from "~/core/StackSummaryTable";
|
|
15
14
|
import { cli } from "~/core/cli";
|
|
16
15
|
import { colors } from "~/core/colors";
|
|
@@ -107,37 +106,6 @@ async function run() {
|
|
|
107
106
|
process.chdir(repo_root);
|
|
108
107
|
await cli(`pwd`);
|
|
109
108
|
|
|
110
|
-
if (argv["rebase"] === "git-revise") {
|
|
111
|
-
await rebase_git_revise();
|
|
112
|
-
} else {
|
|
113
|
-
await rebase_cherry_pick();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// after all commits have been cherry-picked and amended
|
|
117
|
-
// move the branch pointer to the newly created temporary branch
|
|
118
|
-
// now we are in locally in sync with github and on the original branch
|
|
119
|
-
await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
|
|
120
|
-
|
|
121
|
-
restore_git();
|
|
122
|
-
|
|
123
|
-
actions.set((state) => {
|
|
124
|
-
state.step = "post-rebase-status";
|
|
125
|
-
});
|
|
126
|
-
} catch (err) {
|
|
127
|
-
actions.error("Unable to rebase.");
|
|
128
|
-
|
|
129
|
-
if (err instanceof Error) {
|
|
130
|
-
if (actions.isDebug()) {
|
|
131
|
-
actions.error(err.message);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
handle_exit();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async function rebase_git_revise() {
|
|
139
|
-
actions.debug(`rebase_git_revise`);
|
|
140
|
-
|
|
141
109
|
actions.output(
|
|
142
110
|
<Ink.Text color={colors.yellow} wrap="truncate-end">
|
|
143
111
|
Rebasing…
|
|
@@ -153,11 +121,33 @@ async function run() {
|
|
|
153
121
|
commit_range,
|
|
154
122
|
});
|
|
155
123
|
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
124
|
+
// after all commits have been modified move the pointer
|
|
125
|
+
// of original branch to the newly created temporary branch
|
|
126
|
+
await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
|
|
127
|
+
|
|
128
|
+
if (argv.sync) {
|
|
129
|
+
await sync_github();
|
|
159
130
|
}
|
|
160
131
|
|
|
132
|
+
restore_git();
|
|
133
|
+
|
|
134
|
+
actions.set((state) => {
|
|
135
|
+
state.step = "post-rebase-status";
|
|
136
|
+
});
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (err instanceof Error) {
|
|
139
|
+
actions.error(err.message);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
actions.error("Unable to rebase.");
|
|
143
|
+
if (!argv.verbose) {
|
|
144
|
+
actions.error("Try again with `--verbose` to see more information.");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
handle_exit();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function sync_github() {
|
|
161
151
|
// in order to sync we walk from rebase_group_index to HEAD
|
|
162
152
|
// checking out each group and syncing to github
|
|
163
153
|
|
|
@@ -186,113 +176,110 @@ async function run() {
|
|
|
186
176
|
push_group_list.unshift({ group, lookback_index });
|
|
187
177
|
}
|
|
188
178
|
|
|
189
|
-
|
|
179
|
+
actions.output(
|
|
180
|
+
<FormatText
|
|
181
|
+
wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
|
|
182
|
+
message="Syncing {group_list}…"
|
|
183
|
+
values={{
|
|
184
|
+
group_list: (
|
|
185
|
+
<React.Fragment>
|
|
186
|
+
{push_group_list.map((push_group) => {
|
|
187
|
+
const group = push_group.group;
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<Brackets key={group.id}>
|
|
191
|
+
{group.pr?.title || group.title || group.id}
|
|
192
|
+
</Brackets>
|
|
193
|
+
);
|
|
194
|
+
})}
|
|
195
|
+
</React.Fragment>
|
|
196
|
+
),
|
|
197
|
+
}}
|
|
198
|
+
/>
|
|
199
|
+
);
|
|
190
200
|
|
|
191
|
-
//
|
|
201
|
+
// for all push targets in push_group_list
|
|
202
|
+
// things that can be done in parallel are grouped by numbers
|
|
203
|
+
//
|
|
204
|
+
// -----------------------------------
|
|
205
|
+
// 1 (before_push) temp mark draft
|
|
206
|
+
// --------------------------------------
|
|
207
|
+
// 2 push simultaneously to github
|
|
208
|
+
// --------------------------------------
|
|
209
|
+
// 2 create PR / edit PR
|
|
210
|
+
// 2 (after_push) undo temp mark draft
|
|
211
|
+
// --------------------------------------
|
|
212
|
+
|
|
213
|
+
const before_push_tasks = [];
|
|
192
214
|
for (const push_group of push_group_list) {
|
|
193
|
-
|
|
215
|
+
before_push_tasks.push(before_push(push_group));
|
|
216
|
+
}
|
|
194
217
|
|
|
195
|
-
|
|
196
|
-
await cli(`git checkout -b ${group.id}`);
|
|
218
|
+
await Promise.all(before_push_tasks);
|
|
197
219
|
|
|
198
|
-
|
|
199
|
-
|
|
220
|
+
const push_target_list = push_group_list.map((push_group) => {
|
|
221
|
+
return `HEAD~${push_group.lookback_index}:${push_group.group.id}`;
|
|
222
|
+
});
|
|
200
223
|
|
|
201
|
-
|
|
224
|
+
const push_target_args = push_target_list.join(" ");
|
|
202
225
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
226
|
+
const git_push_command = [`git push -f origin ${push_target_args}`];
|
|
227
|
+
|
|
228
|
+
if (argv.verify === false) {
|
|
229
|
+
git_push_command.push("--no-verify");
|
|
206
230
|
}
|
|
207
231
|
|
|
208
|
-
|
|
209
|
-
await update_pr_tables(pr_url_list);
|
|
210
|
-
}
|
|
232
|
+
await cli(git_push_command);
|
|
211
233
|
|
|
212
|
-
|
|
213
|
-
actions.debug("rebase_cherry_pick");
|
|
234
|
+
const pr_url_list = commit_range.group_list.map(get_group_url);
|
|
214
235
|
|
|
215
|
-
|
|
216
|
-
|
|
236
|
+
const after_push_tasks = [];
|
|
237
|
+
for (const push_group of push_group_list) {
|
|
238
|
+
const group = push_group.group;
|
|
239
|
+
after_push_tasks.push(after_push({ group, pr_url_list }));
|
|
240
|
+
}
|
|
217
241
|
|
|
218
|
-
|
|
242
|
+
await Promise.all(after_push_tasks);
|
|
219
243
|
|
|
220
|
-
|
|
244
|
+
// finally, ensure all prs have the updated stack table from updated pr_url_list
|
|
245
|
+
for (let i = 0; i < commit_range.group_list.length; i++) {
|
|
221
246
|
const group = commit_range.group_list[i];
|
|
222
247
|
|
|
223
|
-
|
|
248
|
+
// use the updated pr_url_list to get the actual selected_url
|
|
249
|
+
const selected_url = pr_url_list[i];
|
|
224
250
|
|
|
225
|
-
|
|
226
|
-
<FormatText
|
|
227
|
-
wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
|
|
228
|
-
message="Rebasing {group}…"
|
|
229
|
-
values={{
|
|
230
|
-
group: (
|
|
231
|
-
<Brackets>{group.pr?.title || group.title || group.id}</Brackets>
|
|
232
|
-
),
|
|
233
|
-
}}
|
|
234
|
-
/>
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
// cherry-pick and amend commits one by one
|
|
238
|
-
for (const commit of group.commits) {
|
|
239
|
-
// ensure clean base to avoid conflicts when applying patch
|
|
240
|
-
await cli(`git clean -fd`);
|
|
241
|
-
|
|
242
|
-
// create, apply and cleanup patch
|
|
243
|
-
await cli(`git format-patch -1 ${commit.sha} --stdout > ${PATCH_FILE}`);
|
|
244
|
-
await cli(`git apply ${PATCH_FILE}`);
|
|
245
|
-
await cli(`rm ${PATCH_FILE}`);
|
|
246
|
-
|
|
247
|
-
// add all changes to stage
|
|
248
|
-
await cli(`git add --all`);
|
|
249
|
-
|
|
250
|
-
const metadata = { id: group.id, title: group.title };
|
|
251
|
-
const new_message = Metadata.write(commit.full_message, metadata);
|
|
252
|
-
const git_commit_comand = [`git commit -m "${new_message}"`];
|
|
253
|
-
|
|
254
|
-
if (argv.verify === false) {
|
|
255
|
-
git_commit_comand.push("--no-verify");
|
|
256
|
-
}
|
|
251
|
+
invariant(group.base, "group.base must exist");
|
|
257
252
|
|
|
258
|
-
|
|
259
|
-
}
|
|
253
|
+
const body = group.pr?.body || DEFAULT_PR_BODY;
|
|
260
254
|
|
|
261
|
-
|
|
262
|
-
|
|
255
|
+
const update_body = StackSummaryTable.write({
|
|
256
|
+
body,
|
|
257
|
+
pr_url_list,
|
|
258
|
+
selected_url,
|
|
259
|
+
});
|
|
263
260
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
261
|
+
if (update_body === body) {
|
|
262
|
+
actions.debug(`Skipping body update for ${selected_url}`);
|
|
263
|
+
} else {
|
|
264
|
+
actions.debug(`Update body for ${selected_url}`);
|
|
267
265
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
return;
|
|
266
|
+
await github.pr_edit({
|
|
267
|
+
branch: group.id,
|
|
268
|
+
base: group.base,
|
|
269
|
+
body: update_body,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
275
272
|
}
|
|
273
|
+
}
|
|
276
274
|
|
|
277
|
-
|
|
275
|
+
async function before_push(args: { group: CommitMetadataGroup }) {
|
|
276
|
+
const { group } = args;
|
|
278
277
|
|
|
279
278
|
invariant(group.base, "group.base must exist");
|
|
280
279
|
|
|
281
|
-
actions.output(
|
|
282
|
-
<FormatText
|
|
283
|
-
wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
|
|
284
|
-
message="Syncing {group}…"
|
|
285
|
-
values={{
|
|
286
|
-
group: (
|
|
287
|
-
<Brackets>{group.pr?.title || group.title || group.id}</Brackets>
|
|
288
|
-
),
|
|
289
|
-
}}
|
|
290
|
-
/>
|
|
291
|
-
);
|
|
292
|
-
|
|
293
280
|
// we may temporarily mark PR as a draft before editing it
|
|
294
281
|
// if it is not already a draft PR, to avoid notification spam
|
|
295
|
-
let is_temp_draft =
|
|
282
|
+
let is_temp_draft = !group.pr?.isDraft;
|
|
296
283
|
|
|
297
284
|
// before pushing reset base to master temporarily
|
|
298
285
|
// avoid accidentally pointing to orphaned parent commit
|
|
@@ -314,15 +301,15 @@ async function run() {
|
|
|
314
301
|
base: master_branch,
|
|
315
302
|
});
|
|
316
303
|
}
|
|
304
|
+
}
|
|
317
305
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
306
|
+
async function after_push(args: {
|
|
307
|
+
group: CommitMetadataGroup;
|
|
308
|
+
pr_url_list: Array<string>;
|
|
309
|
+
}) {
|
|
310
|
+
const { group, pr_url_list } = args;
|
|
324
311
|
|
|
325
|
-
|
|
312
|
+
invariant(group.base, "group.base must exist");
|
|
326
313
|
|
|
327
314
|
const selected_url = get_group_url(group);
|
|
328
315
|
|
|
@@ -338,6 +325,10 @@ async function run() {
|
|
|
338
325
|
}),
|
|
339
326
|
});
|
|
340
327
|
|
|
328
|
+
// we may temporarily mark PR as a draft before editing it
|
|
329
|
+
// if it is not already a draft PR, to avoid notification spam
|
|
330
|
+
let is_temp_draft = !group.pr?.isDraft;
|
|
331
|
+
|
|
341
332
|
if (is_temp_draft) {
|
|
342
333
|
// mark pr as ready for review again
|
|
343
334
|
await github.pr_draft({
|
|
@@ -346,14 +337,6 @@ async function run() {
|
|
|
346
337
|
});
|
|
347
338
|
}
|
|
348
339
|
} else {
|
|
349
|
-
if (!args.skip_checkout) {
|
|
350
|
-
// delete local group branch if leftover
|
|
351
|
-
await cli(`git branch -D ${group.id}`, { ignoreExitCode: true });
|
|
352
|
-
|
|
353
|
-
// move to temporary branch for creating pr
|
|
354
|
-
await cli(`git checkout -b ${group.id}`);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
340
|
// create pr in github
|
|
358
341
|
const pr_url = await github.pr_create({
|
|
359
342
|
branch: group.id,
|
|
@@ -374,46 +357,6 @@ async function run() {
|
|
|
374
357
|
pr_url_list[i] = pr_url;
|
|
375
358
|
}
|
|
376
359
|
}
|
|
377
|
-
|
|
378
|
-
// move back to temp branch
|
|
379
|
-
if (!args.skip_checkout) {
|
|
380
|
-
await cli(`git checkout ${temp_branch_name}`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
async function update_pr_tables(pr_url_list: Array<string>) {
|
|
386
|
-
if (!argv.sync) {
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
for (let i = 0; i < commit_range.group_list.length; i++) {
|
|
391
|
-
const group = commit_range.group_list[i];
|
|
392
|
-
|
|
393
|
-
// use the updated pr_url_list to get the actual selected_url
|
|
394
|
-
const selected_url = pr_url_list[i];
|
|
395
|
-
|
|
396
|
-
invariant(group.base, "group.base must exist");
|
|
397
|
-
|
|
398
|
-
const body = group.pr?.body || DEFAULT_PR_BODY;
|
|
399
|
-
|
|
400
|
-
const update_body = StackSummaryTable.write({
|
|
401
|
-
body,
|
|
402
|
-
pr_url_list,
|
|
403
|
-
selected_url,
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
if (update_body === body) {
|
|
407
|
-
actions.debug(`Skipping body update for ${selected_url}`);
|
|
408
|
-
} else {
|
|
409
|
-
actions.debug(`Update body for ${selected_url}`);
|
|
410
|
-
|
|
411
|
-
await github.pr_edit({
|
|
412
|
-
branch: group.id,
|
|
413
|
-
base: group.base,
|
|
414
|
-
body: update_body,
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
360
|
}
|
|
418
361
|
}
|
|
419
362
|
|
|
@@ -424,9 +367,6 @@ async function run() {
|
|
|
424
367
|
// all children processes receive the SIGINT signal
|
|
425
368
|
const spawn_options = { ignoreExitCode: true };
|
|
426
369
|
|
|
427
|
-
// always clean up any patch files
|
|
428
|
-
cli.sync(`rm ${PATCH_FILE}`, spawn_options);
|
|
429
|
-
|
|
430
370
|
// always hard reset and clean to allow subsequent checkout
|
|
431
371
|
// if there are files checkout will fail and cascade fail subsequent commands
|
|
432
372
|
cli.sync(`git reset --hard`, spawn_options);
|
|
@@ -438,13 +378,6 @@ async function run() {
|
|
|
438
378
|
// ...and cleanup temporary branch
|
|
439
379
|
cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
|
|
440
380
|
|
|
441
|
-
if (commit_range) {
|
|
442
|
-
// ...and cleanup pr group branches
|
|
443
|
-
for (const group of commit_range.group_list) {
|
|
444
|
-
cli.sync(`git branch -D ${group.id}`, spawn_options);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
381
|
// restore back to original dir
|
|
449
382
|
if (fs.existsSync(cwd)) {
|
|
450
383
|
process.chdir(cwd);
|
|
@@ -473,5 +406,3 @@ async function run() {
|
|
|
473
406
|
|
|
474
407
|
type CommitMetadataGroup = CommitMetadata.CommitRange["group_list"][number];
|
|
475
408
|
const get_group_url = (group: CommitMetadataGroup) => group.pr?.url || group.id;
|
|
476
|
-
|
|
477
|
-
const PATCH_FILE = "git-stack-cli-patch.patch";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import fs from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
|
|
6
6
|
import * as Ink from "ink-cjs";
|
|
@@ -11,6 +11,7 @@ import { FormatText } from "~/app/FormatText";
|
|
|
11
11
|
import { Store } from "~/app/Store";
|
|
12
12
|
import { colors } from "~/core/colors";
|
|
13
13
|
import { invariant } from "~/core/invariant";
|
|
14
|
+
import { safe_exists } from "~/core/safe_exists";
|
|
14
15
|
|
|
15
16
|
export function PreManualRebase() {
|
|
16
17
|
return <Await fallback={null} function={run} />;
|
|
@@ -40,8 +41,8 @@ async function run() {
|
|
|
40
41
|
for (const key of PR_TEMPLATE_KEY_LIST) {
|
|
41
42
|
const pr_template_fn = PR_TEMPLATE[key as keyof typeof PR_TEMPLATE];
|
|
42
43
|
|
|
43
|
-
if (
|
|
44
|
-
pr_template_body = fs.
|
|
44
|
+
if (await safe_exists(pr_template_fn(repo_root))) {
|
|
45
|
+
pr_template_body = await fs.readFile(pr_template_fn(repo_root), "utf-8");
|
|
45
46
|
|
|
46
47
|
actions.output(
|
|
47
48
|
<FormatText
|
|
@@ -59,8 +60,8 @@ async function run() {
|
|
|
59
60
|
|
|
60
61
|
// ./.github/PULL_REQUEST_TEMPLATE/*.md
|
|
61
62
|
let pr_templates: Array<string> = [];
|
|
62
|
-
if (
|
|
63
|
-
pr_templates = fs.
|
|
63
|
+
if (await safe_exists(PR_TEMPLATE.TemplateDir(repo_root))) {
|
|
64
|
+
pr_templates = await fs.readdir(PR_TEMPLATE.TemplateDir(repo_root));
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
// check if repo has multiple pr templates
|
package/src/app/RebaseCheck.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import fs from "node:fs";
|
|
4
3
|
import path from "node:path";
|
|
5
4
|
|
|
6
5
|
import * as Ink from "ink-cjs";
|
|
@@ -11,6 +10,7 @@ import { Store } from "~/app/Store";
|
|
|
11
10
|
import { YesNoPrompt } from "~/app/YesNoPrompt";
|
|
12
11
|
import { cli } from "~/core/cli";
|
|
13
12
|
import { colors } from "~/core/colors";
|
|
13
|
+
import { safe_exists } from "~/core/safe_exists";
|
|
14
14
|
|
|
15
15
|
type Props = {
|
|
16
16
|
children: React.ReactNode;
|
|
@@ -74,8 +74,8 @@ export function RebaseCheck(props: Props) {
|
|
|
74
74
|
const git_dir = (await cli(`git rev-parse --absolute-git-dir`)).stdout;
|
|
75
75
|
|
|
76
76
|
let is_rebase = false;
|
|
77
|
-
is_rebase ||=
|
|
78
|
-
is_rebase ||=
|
|
77
|
+
is_rebase ||= await safe_exists(path.join(git_dir, "rebase-apply"));
|
|
78
|
+
is_rebase ||= await safe_exists(path.join(git_dir, "rebase-merge"));
|
|
79
79
|
|
|
80
80
|
const status = is_rebase ? "prompt" : "done";
|
|
81
81
|
patch({ status });
|
package/src/command.ts
CHANGED
|
@@ -52,11 +52,6 @@ export async function command() {
|
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
const Rebase = Object.freeze({
|
|
56
|
-
"git-revise": "git-revise",
|
|
57
|
-
"cherry-pick": "cherry-pick",
|
|
58
|
-
});
|
|
59
|
-
|
|
60
55
|
const GlobalOptions = {
|
|
61
56
|
verbose: {
|
|
62
57
|
type: "boolean",
|
|
@@ -95,17 +90,6 @@ const DefaultOptions = {
|
|
|
95
90
|
"Run git hooks such as pre-commit and pre-push, disable with --no-verify",
|
|
96
91
|
},
|
|
97
92
|
|
|
98
|
-
"rebase": {
|
|
99
|
-
type: "string",
|
|
100
|
-
choices: [Rebase["git-revise"], Rebase["cherry-pick"]],
|
|
101
|
-
default: Rebase["git-revise"],
|
|
102
|
-
description: [
|
|
103
|
-
"Strategy used for syncing branches",
|
|
104
|
-
`${Rebase["git-revise"]}: perform faster in-memory rebase`,
|
|
105
|
-
`${Rebase["cherry-pick"]}: use disk and incrementally rebase each commit`,
|
|
106
|
-
].join(" | "),
|
|
107
|
-
},
|
|
108
|
-
|
|
109
93
|
"update": {
|
|
110
94
|
type: "boolean",
|
|
111
95
|
alias: ["u", "upgrade"],
|
package/src/commands/Rebase.tsx
CHANGED
|
@@ -111,9 +111,8 @@ Rebase.run = async function run() {
|
|
|
111
111
|
await cli(`git cherry-pick --keep-redundant-commits ${sha_list}`);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// after all commits have been cherry-picked
|
|
115
|
-
//
|
|
116
|
-
// now we are locally in sync with github and on the original branch
|
|
114
|
+
// after all commits have been cherry-picked move the pointer
|
|
115
|
+
// of original branch to the newly created temporary branch
|
|
117
116
|
await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
|
|
118
117
|
|
|
119
118
|
restore_git();
|
|
@@ -154,9 +153,6 @@ Rebase.run = async function run() {
|
|
|
154
153
|
// all children processes receive the SIGINT signal
|
|
155
154
|
const spawn_options = { ignoreExitCode: true };
|
|
156
155
|
|
|
157
|
-
// always clean up any patch files
|
|
158
|
-
cli.sync(`rm ${PATCH_FILE}`, spawn_options);
|
|
159
|
-
|
|
160
156
|
// always hard reset and clean to allow subsequent checkout
|
|
161
157
|
// if there are files checkout will fail and cascade fail subsequent commands
|
|
162
158
|
cli.sync(`git reset --hard`, spawn_options);
|
|
@@ -168,13 +164,6 @@ Rebase.run = async function run() {
|
|
|
168
164
|
// ...and cleanup temporary branch
|
|
169
165
|
cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
|
|
170
166
|
|
|
171
|
-
if (commit_range) {
|
|
172
|
-
// ...and cleanup pr group branches
|
|
173
|
-
for (const group of commit_range.group_list) {
|
|
174
|
-
cli.sync(`git branch -D ${group.id}`, spawn_options);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
167
|
// restore back to original dir
|
|
179
168
|
if (fs.existsSync(cwd)) {
|
|
180
169
|
process.chdir(cwd);
|
|
@@ -200,5 +189,3 @@ Rebase.run = async function run() {
|
|
|
200
189
|
actions.exit(6);
|
|
201
190
|
}
|
|
202
191
|
};
|
|
203
|
-
|
|
204
|
-
const PATCH_FILE = "git-stack-cli-patch.patch";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
@@ -120,10 +120,10 @@ GitReviseTodo.execute = async function grt_execute(args: ExecuteArgs) {
|
|
|
120
120
|
const GIT_SEQUENCE_EDITOR_SCRIPT = `process.env.GIT_SEQUENCE_EDITOR_SCRIPT`;
|
|
121
121
|
|
|
122
122
|
// write script to temporary path
|
|
123
|
-
fs.
|
|
123
|
+
await fs.writeFile(tmp_git_sequence_editor_path, GIT_SEQUENCE_EDITOR_SCRIPT);
|
|
124
124
|
|
|
125
125
|
// ensure script is executable
|
|
126
|
-
fs.
|
|
126
|
+
await fs.chmod(tmp_git_sequence_editor_path, "755");
|
|
127
127
|
|
|
128
128
|
const git_revise_todo = GitReviseTodo(args);
|
|
129
129
|
|