git-stack-cli 1.10.0 → 1.11.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/cjs/index.cjs +29 -11
- package/package.json +1 -1
- package/src/app/App.tsx +9 -0
- package/src/app/DetectInitialPR.tsx +4 -0
- package/src/app/LocalMergeRebase.tsx +2 -188
- package/src/command.ts +6 -0
- package/src/commands/Rebase.tsx +204 -0
package/dist/cjs/index.cjs
CHANGED
|
@@ -30147,6 +30147,10 @@ function DetectInitialPR(props) {
|
|
|
30147
30147
|
for (const group of commit_range.group_list) {
|
|
30148
30148
|
group.id = branch_name;
|
|
30149
30149
|
group.title = state.pr?.title || "-";
|
|
30150
|
+
for (const commit of commit_range.commit_list) {
|
|
30151
|
+
commit.branch_id = group.id;
|
|
30152
|
+
commit.title = group.title;
|
|
30153
|
+
}
|
|
30150
30154
|
}
|
|
30151
30155
|
// get latest merge_base relative to local master
|
|
30152
30156
|
const rebase_group_index = 0;
|
|
@@ -30220,9 +30224,9 @@ function DirtyCheck(props) {
|
|
|
30220
30224
|
|
|
30221
30225
|
function GatherMetadata(props) {
|
|
30222
30226
|
const fallback = (reactExports.createElement(Text, { color: colors.yellow }, "Gathering local git information\u2026"));
|
|
30223
|
-
return (reactExports.createElement(Await, { fallback: fallback, function: run$
|
|
30227
|
+
return (reactExports.createElement(Await, { fallback: fallback, function: run$8 }, props.children));
|
|
30224
30228
|
}
|
|
30225
|
-
async function run$
|
|
30229
|
+
async function run$8() {
|
|
30226
30230
|
const actions = Store.getState().actions;
|
|
30227
30231
|
const argv = Store.getState().argv;
|
|
30228
30232
|
try {
|
|
@@ -30316,9 +30320,9 @@ function format_time(date) {
|
|
|
30316
30320
|
}
|
|
30317
30321
|
|
|
30318
30322
|
function GithubApiError() {
|
|
30319
|
-
return reactExports.createElement(Await, { fallback: null, function: run$
|
|
30323
|
+
return reactExports.createElement(Await, { fallback: null, function: run$7 });
|
|
30320
30324
|
}
|
|
30321
|
-
async function run$
|
|
30325
|
+
async function run$7() {
|
|
30322
30326
|
const actions = Store.getState().actions;
|
|
30323
30327
|
const res = await cli(`gh api https://api.github.com/rate_limit`);
|
|
30324
30328
|
const res_json = JSON.parse(res.stdout);
|
|
@@ -30360,7 +30364,7 @@ function LocalCommitStatus(props) {
|
|
|
30360
30364
|
if (argv["mock-metadata"]) {
|
|
30361
30365
|
return (reactExports.createElement(Await, { fallback: fallback, function: mock_metadata }, props.children));
|
|
30362
30366
|
}
|
|
30363
|
-
return (reactExports.createElement(Await, { fallback: fallback, function: run$
|
|
30367
|
+
return (reactExports.createElement(Await, { fallback: fallback, function: run$6 }, props.children));
|
|
30364
30368
|
}
|
|
30365
30369
|
async function mock_metadata() {
|
|
30366
30370
|
const module = await Promise.resolve().then(function () { return metadata; });
|
|
@@ -30370,7 +30374,7 @@ async function mock_metadata() {
|
|
|
30370
30374
|
state.step = "status";
|
|
30371
30375
|
});
|
|
30372
30376
|
}
|
|
30373
|
-
async function run$
|
|
30377
|
+
async function run$6() {
|
|
30374
30378
|
const actions = Store.getState().actions;
|
|
30375
30379
|
try {
|
|
30376
30380
|
const commit_range = await range();
|
|
@@ -30449,10 +30453,10 @@ function encode(value) {
|
|
|
30449
30453
|
return result.padStart(max_char_size, "=");
|
|
30450
30454
|
}
|
|
30451
30455
|
|
|
30452
|
-
function
|
|
30453
|
-
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026")
|
|
30456
|
+
function Rebase$1() {
|
|
30457
|
+
return (reactExports.createElement(Await, { function: Rebase$1.run, fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026") }));
|
|
30454
30458
|
}
|
|
30455
|
-
async function run
|
|
30459
|
+
Rebase$1.run = async function run() {
|
|
30456
30460
|
const state = Store.getState();
|
|
30457
30461
|
const actions = state.actions;
|
|
30458
30462
|
const branch_name = state.branch_name;
|
|
@@ -30512,6 +30516,10 @@ async function run$6() {
|
|
|
30512
30516
|
await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
|
|
30513
30517
|
restore_git();
|
|
30514
30518
|
const next_commit_range = await range();
|
|
30519
|
+
actions.output(reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.green }), message: "\u2705 {branch_name} in sync with {origin_branch}", values: {
|
|
30520
|
+
branch_name: reactExports.createElement(Brackets, null, branch_name),
|
|
30521
|
+
origin_branch: reactExports.createElement(Brackets, null, `origin/${master_branch}`),
|
|
30522
|
+
} }));
|
|
30515
30523
|
actions.set((state) => {
|
|
30516
30524
|
state.commit_range = next_commit_range;
|
|
30517
30525
|
state.step = "status";
|
|
@@ -30566,9 +30574,13 @@ async function run$6() {
|
|
|
30566
30574
|
"."));
|
|
30567
30575
|
actions.exit(6);
|
|
30568
30576
|
}
|
|
30569
|
-
}
|
|
30577
|
+
};
|
|
30570
30578
|
const PATCH_FILE$1 = "git-stack-cli-patch.patch";
|
|
30571
30579
|
|
|
30580
|
+
function LocalMergeRebase() {
|
|
30581
|
+
return reactExports.createElement(Rebase$1, null);
|
|
30582
|
+
}
|
|
30583
|
+
|
|
30572
30584
|
function write(args) {
|
|
30573
30585
|
const stack_table = table(args);
|
|
30574
30586
|
let result = args.body;
|
|
@@ -32042,6 +32054,11 @@ function MaybeMain() {
|
|
|
32042
32054
|
else if (positional_list.has("log")) {
|
|
32043
32055
|
return reactExports.createElement(Log, null);
|
|
32044
32056
|
}
|
|
32057
|
+
else if (positional_list.has("rebase")) {
|
|
32058
|
+
return (reactExports.createElement(GatherMetadata, null,
|
|
32059
|
+
reactExports.createElement(LocalCommitStatus, null,
|
|
32060
|
+
reactExports.createElement(Rebase$1, null))));
|
|
32061
|
+
}
|
|
32045
32062
|
return (reactExports.createElement(DirtyCheck, null,
|
|
32046
32063
|
!argv.verbose ? null : reactExports.createElement(GithubApiError, null),
|
|
32047
32064
|
reactExports.createElement(GatherMetadata, null,
|
|
@@ -37360,6 +37377,7 @@ async function command() {
|
|
|
37360
37377
|
.command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions))
|
|
37361
37378
|
.command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) => yargs.positional("commit", FixupOptions.commit))
|
|
37362
37379
|
.command("log [args...]", "Print an abbreviated log with numbered commits, useful for git stack fixup", (yargs) => yargs.strict(false))
|
|
37380
|
+
.command("rebase", "Update local branch via rebase with latest changes from origin master branch", (yargs) => yargs)
|
|
37363
37381
|
.option("verbose", GlobalOptions.verbose)
|
|
37364
37382
|
// yargs default wraps to 80 columns
|
|
37365
37383
|
// passing null will wrap to terminal width
|
|
@@ -37367,7 +37385,7 @@ async function command() {
|
|
|
37367
37385
|
.wrap(123)
|
|
37368
37386
|
// disallow unknown options
|
|
37369
37387
|
.strict()
|
|
37370
|
-
.version("1.
|
|
37388
|
+
.version("1.11.1" )
|
|
37371
37389
|
.showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
|
|
37372
37390
|
.help("help", "Show usage via `git stack help`")
|
|
37373
37391
|
.argv;
|
package/package.json
CHANGED
package/src/app/App.tsx
CHANGED
|
@@ -16,6 +16,7 @@ import { RebaseCheck } from "~/app/RebaseCheck";
|
|
|
16
16
|
import { Store } from "~/app/Store";
|
|
17
17
|
import { Fixup } from "~/commands/Fixup";
|
|
18
18
|
import { Log } from "~/commands/Log";
|
|
19
|
+
import { Rebase } from "~/commands/Rebase";
|
|
19
20
|
|
|
20
21
|
export function App() {
|
|
21
22
|
const actions = Store.useActions();
|
|
@@ -73,6 +74,14 @@ function MaybeMain() {
|
|
|
73
74
|
return <Fixup />;
|
|
74
75
|
} else if (positional_list.has("log")) {
|
|
75
76
|
return <Log />;
|
|
77
|
+
} else if (positional_list.has("rebase")) {
|
|
78
|
+
return (
|
|
79
|
+
<GatherMetadata>
|
|
80
|
+
<LocalCommitStatus>
|
|
81
|
+
<Rebase />
|
|
82
|
+
</LocalCommitStatus>
|
|
83
|
+
</GatherMetadata>
|
|
84
|
+
);
|
|
76
85
|
}
|
|
77
86
|
|
|
78
87
|
return (
|
|
@@ -157,6 +157,10 @@ export function DetectInitialPR(props: Props) {
|
|
|
157
157
|
for (const group of commit_range.group_list) {
|
|
158
158
|
group.id = branch_name;
|
|
159
159
|
group.title = state.pr?.title || "-";
|
|
160
|
+
for (const commit of commit_range.commit_list) {
|
|
161
|
+
commit.branch_id = group.id;
|
|
162
|
+
commit.title = group.title;
|
|
163
|
+
}
|
|
160
164
|
}
|
|
161
165
|
|
|
162
166
|
// get latest merge_base relative to local master
|
|
@@ -1,193 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
import * as Ink from "ink-cjs";
|
|
6
|
-
|
|
7
|
-
import { Await } from "~/app/Await";
|
|
8
|
-
import { Brackets } from "~/app/Brackets";
|
|
9
|
-
import { FormatText } from "~/app/FormatText";
|
|
10
|
-
import { Parens } from "~/app/Parens";
|
|
11
|
-
import { Store } from "~/app/Store";
|
|
12
|
-
import * as CommitMetadata from "~/core/CommitMetadata";
|
|
13
|
-
import { cli } from "~/core/cli";
|
|
14
|
-
import { colors } from "~/core/colors";
|
|
15
|
-
import { invariant } from "~/core/invariant";
|
|
16
|
-
import { short_id } from "~/core/short_id";
|
|
3
|
+
import { Rebase } from "~/commands/Rebase";
|
|
17
4
|
|
|
18
5
|
export function LocalMergeRebase() {
|
|
19
|
-
return
|
|
20
|
-
<Await
|
|
21
|
-
fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>}
|
|
22
|
-
function={run}
|
|
23
|
-
/>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function run() {
|
|
28
|
-
const state = Store.getState();
|
|
29
|
-
const actions = state.actions;
|
|
30
|
-
const branch_name = state.branch_name;
|
|
31
|
-
const commit_range = state.commit_range;
|
|
32
|
-
const master_branch = state.master_branch;
|
|
33
|
-
const cwd = state.cwd;
|
|
34
|
-
const repo_root = state.repo_root;
|
|
35
|
-
|
|
36
|
-
invariant(branch_name, "branch_name must exist");
|
|
37
|
-
invariant(commit_range, "commit_range must exist");
|
|
38
|
-
invariant(repo_root, "repo_root must exist");
|
|
39
|
-
|
|
40
|
-
// always listen for SIGINT event and restore git state
|
|
41
|
-
process.once("SIGINT", handle_exit);
|
|
42
|
-
|
|
43
|
-
const temp_branch_name = `${branch_name}_${short_id()}`;
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
// actions.debug(`commit_range=${JSON.stringify(commit_range, null, 2)}`);
|
|
47
|
-
|
|
48
|
-
// must perform rebase from repo root for applying git patch
|
|
49
|
-
process.chdir(repo_root);
|
|
50
|
-
await cli(`pwd`);
|
|
51
|
-
|
|
52
|
-
// update local master to match remote
|
|
53
|
-
await cli(
|
|
54
|
-
`git fetch --no-tags -v origin ${master_branch}:${master_branch}`
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
const master_sha = (await cli(`git rev-parse ${master_branch}`)).stdout;
|
|
58
|
-
const rebase_merge_base = master_sha;
|
|
59
|
-
|
|
60
|
-
// create temporary branch based on merge base
|
|
61
|
-
await cli(`git checkout -b ${temp_branch_name} ${rebase_merge_base}`);
|
|
62
|
-
|
|
63
|
-
const picked_commit_list = [];
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < commit_range.commit_list.length; i++) {
|
|
66
|
-
const commit = commit_range.commit_list[i];
|
|
67
|
-
const commit_pr = commit_range.pr_lookup[commit.branch_id || ""];
|
|
68
|
-
|
|
69
|
-
// drop commits that are in groups of merged PRs
|
|
70
|
-
const merged_pr = commit_pr?.state === "MERGED";
|
|
71
|
-
|
|
72
|
-
if (merged_pr) {
|
|
73
|
-
if (actions.isDebug()) {
|
|
74
|
-
actions.output(
|
|
75
|
-
<FormatText
|
|
76
|
-
wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
|
|
77
|
-
message="Dropping {commit_message} {pr_status}"
|
|
78
|
-
values={{
|
|
79
|
-
commit_message: <Brackets>{commit.subject_line}</Brackets>,
|
|
80
|
-
pr_status: <Parens>MERGED</Parens>,
|
|
81
|
-
}}
|
|
82
|
-
/>
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (actions.isDebug()) {
|
|
90
|
-
actions.output(
|
|
91
|
-
<FormatText
|
|
92
|
-
wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
|
|
93
|
-
message="Picking {commit_message}"
|
|
94
|
-
values={{
|
|
95
|
-
commit_message: <Brackets>{commit.subject_line}</Brackets>,
|
|
96
|
-
}}
|
|
97
|
-
/>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
picked_commit_list.push(commit);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (picked_commit_list.length > 0) {
|
|
105
|
-
// ensure clean base to avoid conflicts when applying patch
|
|
106
|
-
await cli(`git clean -fd`);
|
|
107
|
-
|
|
108
|
-
// create list of sha for cherry-pick
|
|
109
|
-
const sha_list = picked_commit_list.map((commit) => commit.sha).join(" ");
|
|
110
|
-
|
|
111
|
-
await cli(`git cherry-pick --keep-redundant-commits ${sha_list}`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// after all commits have been cherry-picked and amended
|
|
115
|
-
// move the branch pointer to the newly created temporary branch
|
|
116
|
-
// now we are locally in sync with github and on the original branch
|
|
117
|
-
await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
|
|
118
|
-
|
|
119
|
-
restore_git();
|
|
120
|
-
|
|
121
|
-
const next_commit_range = await CommitMetadata.range();
|
|
122
|
-
|
|
123
|
-
actions.set((state) => {
|
|
124
|
-
state.commit_range = next_commit_range;
|
|
125
|
-
state.step = "status";
|
|
126
|
-
});
|
|
127
|
-
} catch (err) {
|
|
128
|
-
actions.error("Unable to rebase.");
|
|
129
|
-
|
|
130
|
-
if (err instanceof Error) {
|
|
131
|
-
if (actions.isDebug()) {
|
|
132
|
-
actions.error(err.message);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
handle_exit();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// cleanup git operations if cancelled during manual rebase
|
|
140
|
-
function restore_git() {
|
|
141
|
-
// signint handler MUST run synchronously
|
|
142
|
-
// trying to use `await cli(...)` here will silently fail since
|
|
143
|
-
// all children processes receive the SIGINT signal
|
|
144
|
-
const spawn_options = { ignoreExitCode: true };
|
|
145
|
-
|
|
146
|
-
// always clean up any patch files
|
|
147
|
-
cli.sync(`rm ${PATCH_FILE}`, spawn_options);
|
|
148
|
-
|
|
149
|
-
// always hard reset and clean to allow subsequent checkout
|
|
150
|
-
// if there are files checkout will fail and cascade fail subsequent commands
|
|
151
|
-
cli.sync(`git reset --hard`, spawn_options);
|
|
152
|
-
cli.sync(`git clean -df`, spawn_options);
|
|
153
|
-
|
|
154
|
-
// always put self back in original branch
|
|
155
|
-
cli.sync(`git checkout ${branch_name}`, spawn_options);
|
|
156
|
-
|
|
157
|
-
// ...and cleanup temporary branch
|
|
158
|
-
cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
|
|
159
|
-
|
|
160
|
-
if (commit_range) {
|
|
161
|
-
// ...and cleanup pr group branches
|
|
162
|
-
for (const group of commit_range.group_list) {
|
|
163
|
-
cli.sync(`git branch -D ${group.id}`, spawn_options);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// restore back to original dir
|
|
168
|
-
if (fs.existsSync(cwd)) {
|
|
169
|
-
process.chdir(cwd);
|
|
170
|
-
}
|
|
171
|
-
cli.sync(`pwd`, spawn_options);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function handle_exit() {
|
|
175
|
-
actions.output(
|
|
176
|
-
<Ink.Text color={colors.yellow}>
|
|
177
|
-
Restoring <Brackets>{branch_name}</Brackets>…
|
|
178
|
-
</Ink.Text>
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
restore_git();
|
|
182
|
-
|
|
183
|
-
actions.output(
|
|
184
|
-
<Ink.Text color={colors.yellow}>
|
|
185
|
-
Restored <Brackets>{branch_name}</Brackets>.
|
|
186
|
-
</Ink.Text>
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
actions.exit(6);
|
|
190
|
-
}
|
|
6
|
+
return <Rebase />;
|
|
191
7
|
}
|
|
192
|
-
|
|
193
|
-
const PATCH_FILE = "git-stack-cli-patch.patch";
|
package/src/command.ts
CHANGED
|
@@ -27,6 +27,12 @@ export async function command() {
|
|
|
27
27
|
(yargs) => yargs.strict(false)
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
+
.command(
|
|
31
|
+
"rebase",
|
|
32
|
+
"Update local branch via rebase with latest changes from origin master branch",
|
|
33
|
+
(yargs) => yargs
|
|
34
|
+
)
|
|
35
|
+
|
|
30
36
|
.option("verbose", GlobalOptions.verbose)
|
|
31
37
|
|
|
32
38
|
// yargs default wraps to 80 columns
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
import * as Ink from "ink-cjs";
|
|
6
|
+
|
|
7
|
+
import { Await } from "~/app/Await";
|
|
8
|
+
import { Brackets } from "~/app/Brackets";
|
|
9
|
+
import { FormatText } from "~/app/FormatText";
|
|
10
|
+
import { Parens } from "~/app/Parens";
|
|
11
|
+
import { Store } from "~/app/Store";
|
|
12
|
+
import * as CommitMetadata from "~/core/CommitMetadata";
|
|
13
|
+
import { cli } from "~/core/cli";
|
|
14
|
+
import { colors } from "~/core/colors";
|
|
15
|
+
import { invariant } from "~/core/invariant";
|
|
16
|
+
import { short_id } from "~/core/short_id";
|
|
17
|
+
|
|
18
|
+
export function Rebase() {
|
|
19
|
+
return (
|
|
20
|
+
<Await
|
|
21
|
+
function={Rebase.run}
|
|
22
|
+
fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Rebase.run = async function run() {
|
|
28
|
+
const state = Store.getState();
|
|
29
|
+
const actions = state.actions;
|
|
30
|
+
const branch_name = state.branch_name;
|
|
31
|
+
const commit_range = state.commit_range;
|
|
32
|
+
const master_branch = state.master_branch;
|
|
33
|
+
const cwd = state.cwd;
|
|
34
|
+
const repo_root = state.repo_root;
|
|
35
|
+
|
|
36
|
+
invariant(branch_name, "branch_name must exist");
|
|
37
|
+
invariant(commit_range, "commit_range must exist");
|
|
38
|
+
invariant(repo_root, "repo_root must exist");
|
|
39
|
+
|
|
40
|
+
// always listen for SIGINT event and restore git state
|
|
41
|
+
process.once("SIGINT", handle_exit);
|
|
42
|
+
|
|
43
|
+
const temp_branch_name = `${branch_name}_${short_id()}`;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// actions.debug(`commit_range=${JSON.stringify(commit_range, null, 2)}`);
|
|
47
|
+
|
|
48
|
+
// must perform rebase from repo root for applying git patch
|
|
49
|
+
process.chdir(repo_root);
|
|
50
|
+
await cli(`pwd`);
|
|
51
|
+
|
|
52
|
+
// update local master to match remote
|
|
53
|
+
await cli(
|
|
54
|
+
`git fetch --no-tags -v origin ${master_branch}:${master_branch}`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const master_sha = (await cli(`git rev-parse ${master_branch}`)).stdout;
|
|
58
|
+
const rebase_merge_base = master_sha;
|
|
59
|
+
|
|
60
|
+
// create temporary branch based on merge base
|
|
61
|
+
await cli(`git checkout -b ${temp_branch_name} ${rebase_merge_base}`);
|
|
62
|
+
|
|
63
|
+
const picked_commit_list = [];
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < commit_range.commit_list.length; i++) {
|
|
66
|
+
const commit = commit_range.commit_list[i];
|
|
67
|
+
const commit_pr = commit_range.pr_lookup[commit.branch_id || ""];
|
|
68
|
+
|
|
69
|
+
// drop commits that are in groups of merged PRs
|
|
70
|
+
const merged_pr = commit_pr?.state === "MERGED";
|
|
71
|
+
|
|
72
|
+
if (merged_pr) {
|
|
73
|
+
if (actions.isDebug()) {
|
|
74
|
+
actions.output(
|
|
75
|
+
<FormatText
|
|
76
|
+
wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
|
|
77
|
+
message="Dropping {commit_message} {pr_status}"
|
|
78
|
+
values={{
|
|
79
|
+
commit_message: <Brackets>{commit.subject_line}</Brackets>,
|
|
80
|
+
pr_status: <Parens>MERGED</Parens>,
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (actions.isDebug()) {
|
|
90
|
+
actions.output(
|
|
91
|
+
<FormatText
|
|
92
|
+
wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
|
|
93
|
+
message="Picking {commit_message}"
|
|
94
|
+
values={{
|
|
95
|
+
commit_message: <Brackets>{commit.subject_line}</Brackets>,
|
|
96
|
+
}}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
picked_commit_list.push(commit);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (picked_commit_list.length > 0) {
|
|
105
|
+
// ensure clean base to avoid conflicts when applying patch
|
|
106
|
+
await cli(`git clean -fd`);
|
|
107
|
+
|
|
108
|
+
// create list of sha for cherry-pick
|
|
109
|
+
const sha_list = picked_commit_list.map((commit) => commit.sha).join(" ");
|
|
110
|
+
|
|
111
|
+
await cli(`git cherry-pick --keep-redundant-commits ${sha_list}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// after all commits have been cherry-picked and amended
|
|
115
|
+
// move the branch pointer to the newly created temporary branch
|
|
116
|
+
// now we are locally in sync with github and on the original branch
|
|
117
|
+
await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
|
|
118
|
+
|
|
119
|
+
restore_git();
|
|
120
|
+
|
|
121
|
+
const next_commit_range = await CommitMetadata.range();
|
|
122
|
+
|
|
123
|
+
actions.output(
|
|
124
|
+
<FormatText
|
|
125
|
+
wrapper={<Ink.Text color={colors.green} />}
|
|
126
|
+
message="✅ {branch_name} in sync with {origin_branch}"
|
|
127
|
+
values={{
|
|
128
|
+
branch_name: <Brackets>{branch_name}</Brackets>,
|
|
129
|
+
origin_branch: <Brackets>{`origin/${master_branch}`}</Brackets>,
|
|
130
|
+
}}
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
actions.set((state) => {
|
|
135
|
+
state.commit_range = next_commit_range;
|
|
136
|
+
state.step = "status";
|
|
137
|
+
});
|
|
138
|
+
} catch (err) {
|
|
139
|
+
actions.error("Unable to rebase.");
|
|
140
|
+
|
|
141
|
+
if (err instanceof Error) {
|
|
142
|
+
if (actions.isDebug()) {
|
|
143
|
+
actions.error(err.message);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
handle_exit();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// cleanup git operations if cancelled during manual rebase
|
|
151
|
+
function restore_git() {
|
|
152
|
+
// signint handler MUST run synchronously
|
|
153
|
+
// trying to use `await cli(...)` here will silently fail since
|
|
154
|
+
// all children processes receive the SIGINT signal
|
|
155
|
+
const spawn_options = { ignoreExitCode: true };
|
|
156
|
+
|
|
157
|
+
// always clean up any patch files
|
|
158
|
+
cli.sync(`rm ${PATCH_FILE}`, spawn_options);
|
|
159
|
+
|
|
160
|
+
// always hard reset and clean to allow subsequent checkout
|
|
161
|
+
// if there are files checkout will fail and cascade fail subsequent commands
|
|
162
|
+
cli.sync(`git reset --hard`, spawn_options);
|
|
163
|
+
cli.sync(`git clean -df`, spawn_options);
|
|
164
|
+
|
|
165
|
+
// always put self back in original branch
|
|
166
|
+
cli.sync(`git checkout ${branch_name}`, spawn_options);
|
|
167
|
+
|
|
168
|
+
// ...and cleanup temporary branch
|
|
169
|
+
cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
|
|
170
|
+
|
|
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
|
+
// restore back to original dir
|
|
179
|
+
if (fs.existsSync(cwd)) {
|
|
180
|
+
process.chdir(cwd);
|
|
181
|
+
}
|
|
182
|
+
cli.sync(`pwd`, spawn_options);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function handle_exit() {
|
|
186
|
+
actions.output(
|
|
187
|
+
<Ink.Text color={colors.yellow}>
|
|
188
|
+
Restoring <Brackets>{branch_name}</Brackets>…
|
|
189
|
+
</Ink.Text>
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
restore_git();
|
|
193
|
+
|
|
194
|
+
actions.output(
|
|
195
|
+
<Ink.Text color={colors.yellow}>
|
|
196
|
+
Restored <Brackets>{branch_name}</Brackets>.
|
|
197
|
+
</Ink.Text>
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
actions.exit(6);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const PATCH_FILE = "git-stack-cli-patch.patch";
|