git-stack-cli 2.9.9 → 2.11.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/dist/js/index.js +61 -61
- package/package.json +1 -1
- package/src/command.ts +1 -1
- package/src/commands/Fixup.tsx +43 -23
- package/src/commands/Log.tsx +1 -1
- package/src/commands/Rebase.tsx +40 -6
- package/src/core/cli.ts +40 -3
package/package.json
CHANGED
package/src/command.ts
CHANGED
package/src/commands/Fixup.tsx
CHANGED
|
@@ -3,11 +3,13 @@ import * as React from "react";
|
|
|
3
3
|
import * as Ink from "ink-cjs";
|
|
4
4
|
|
|
5
5
|
import { Await } from "~/app/Await";
|
|
6
|
+
import { Command } from "~/app/Command";
|
|
6
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";
|
|
10
11
|
import { colors } from "~/core/colors";
|
|
12
|
+
import { is_finite_value } from "~/core/is_finite_value";
|
|
11
13
|
|
|
12
14
|
export function Fixup() {
|
|
13
15
|
return (
|
|
@@ -25,27 +27,48 @@ async function run() {
|
|
|
25
27
|
|
|
26
28
|
const relative_number = argv.commit;
|
|
27
29
|
|
|
28
|
-
if (!relative_number) {
|
|
30
|
+
if (!is_finite_value(relative_number)) {
|
|
29
31
|
actions.output(
|
|
30
|
-
<Ink.
|
|
32
|
+
<Ink.Box flexDirection="column">
|
|
33
|
+
<Ink.Text color={colors.red}>❗️ Usage: git fixup {"<relative-commit-number>"}</Ink.Text>
|
|
34
|
+
<Ink.Text color={colors.gray}>
|
|
35
|
+
Automates the process of adding staged changes to a previous commit.
|
|
36
|
+
</Ink.Text>
|
|
37
|
+
<FormatText
|
|
38
|
+
wrapper={<Ink.Text color={colors.gray} />}
|
|
39
|
+
message="You can use {git_stack_log} to get the relative commit number."
|
|
40
|
+
values={{ git_stack_log: <Command>git stack log</Command> }}
|
|
41
|
+
/>
|
|
42
|
+
<Ink.Box height={1} />
|
|
43
|
+
<FormatText
|
|
44
|
+
message=" {prompt} git stack log"
|
|
45
|
+
values={{ prompt: <Ink.Text color={colors.green}>❯</Ink.Text> }}
|
|
46
|
+
/>
|
|
47
|
+
<FormatText
|
|
48
|
+
message=" 0 * {sha} 18 hours ago noah homebrew-git-stack 2.9.9"
|
|
49
|
+
values={{ sha: <Ink.Text color={colors.green}>e329794</Ink.Text> }}
|
|
50
|
+
/>
|
|
51
|
+
<FormatText
|
|
52
|
+
message=" 1 * {sha} 18 hours ago noah 2.9.9"
|
|
53
|
+
values={{ sha: <Ink.Text color={colors.green}>c7e4065</Ink.Text> }}
|
|
54
|
+
/>
|
|
55
|
+
<FormatText
|
|
56
|
+
message=" 2 * {sha} 18 hours ago noah command: --label + github add labels"
|
|
57
|
+
values={{ sha: <Ink.Text color={colors.green}>f82ac73</Ink.Text> }}
|
|
58
|
+
/>
|
|
59
|
+
<Ink.Box height={1} />
|
|
60
|
+
<FormatText
|
|
61
|
+
wrapper={<Ink.Text color={colors.gray} />}
|
|
62
|
+
message="To target {sha} above, use {command}"
|
|
63
|
+
values={{
|
|
64
|
+
sha: <Ink.Text color={colors.green}>838e878</Ink.Text>,
|
|
65
|
+
command: <Command>git stack fixup 2</Command>,
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
</Ink.Box>,
|
|
31
69
|
);
|
|
70
|
+
|
|
32
71
|
actions.output("");
|
|
33
|
-
actions.output("This script automates the process of adding staged changes as a fixup commit");
|
|
34
|
-
actions.output(
|
|
35
|
-
"and the subsequent git rebase to flatten the commits based on relative commit number",
|
|
36
|
-
);
|
|
37
|
-
actions.output("You can use a `git log` like below to get the relative commit number");
|
|
38
|
-
actions.output("");
|
|
39
|
-
actions.output(" ❯ git stack log");
|
|
40
|
-
actions.output(
|
|
41
|
-
" 1\te329794d5f881cbf0fc3f26d2108cf6f3fdebabe enable drop_error_subtask test param",
|
|
42
|
-
);
|
|
43
|
-
actions.output(
|
|
44
|
-
" 2\t57f43b596e5c6b97bc47e2a591f82ccc81651156 test drop_error_subtask baseline",
|
|
45
|
-
);
|
|
46
|
-
actions.output(" 3\t838e878d483c6a2d5393063fc59baf2407225c6d ErrorSubtask test baseline");
|
|
47
|
-
actions.output("");
|
|
48
|
-
actions.output("To target `838e87` above, you would call `fixup 3`");
|
|
49
72
|
|
|
50
73
|
actions.exit(0);
|
|
51
74
|
}
|
|
@@ -64,11 +87,8 @@ async function run() {
|
|
|
64
87
|
// );
|
|
65
88
|
}
|
|
66
89
|
|
|
67
|
-
// Calculate commit SHA based on the relative commit number
|
|
68
|
-
const adjusted_number = Number(relative_number) - 1;
|
|
69
|
-
|
|
70
90
|
// get the commit SHA of the target commit
|
|
71
|
-
const commit_sha = (await cli(`git rev-parse HEAD~${
|
|
91
|
+
const commit_sha = (await cli(`git rev-parse HEAD~${relative_number}`)).stdout;
|
|
72
92
|
|
|
73
93
|
actions.output(
|
|
74
94
|
<FormatText
|
|
@@ -104,7 +124,7 @@ async function run() {
|
|
|
104
124
|
|
|
105
125
|
try {
|
|
106
126
|
// rebase target needs to account for new commit created above
|
|
107
|
-
const rebase_target = Number(relative_number) +
|
|
127
|
+
const rebase_target = Number(relative_number) + 2;
|
|
108
128
|
|
|
109
129
|
await cli(`git rebase -i --autosquash HEAD~${rebase_target}`, {
|
|
110
130
|
env: {
|
package/src/commands/Log.tsx
CHANGED
|
@@ -65,7 +65,7 @@ async function run(args: Args) {
|
|
|
65
65
|
const command = [
|
|
66
66
|
`git log --pretty=format:"${format}" -n20 --graph --color ${rest_args}`,
|
|
67
67
|
`cut -c 1-"${truncation_width}"`,
|
|
68
|
-
`nl -w3 -s' '`,
|
|
68
|
+
`nl -v0 -w3 -s' '`,
|
|
69
69
|
].join(" | ");
|
|
70
70
|
|
|
71
71
|
const result = await cli(command);
|
package/src/commands/Rebase.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import * as Ink from "ink-cjs";
|
|
|
6
6
|
|
|
7
7
|
import { Await } from "~/app/Await";
|
|
8
8
|
import { Brackets } from "~/app/Brackets";
|
|
9
|
+
import { Command } from "~/app/Command";
|
|
9
10
|
import { FormatText } from "~/app/FormatText";
|
|
10
11
|
import { Status } from "~/app/Status";
|
|
11
12
|
import { Store } from "~/app/Store";
|
|
@@ -41,8 +42,12 @@ Rebase.run = async function run(props: Props) {
|
|
|
41
42
|
invariant(commit_range, "commit_range must exist");
|
|
42
43
|
invariant(repo_root, "repo_root must exist");
|
|
43
44
|
|
|
45
|
+
const abort_controller = new AbortController();
|
|
46
|
+
const signal = abort_controller.signal;
|
|
47
|
+
|
|
44
48
|
// immediately register abort_handler in case of ctrl+c exit
|
|
45
49
|
actions.register_abort_handler(async function abort_rebase() {
|
|
50
|
+
abort_controller.abort();
|
|
46
51
|
actions.output(<Ink.Text color={colors.red}>🚨 Abort</Ink.Text>);
|
|
47
52
|
handle_exit();
|
|
48
53
|
return 19;
|
|
@@ -59,7 +64,7 @@ Rebase.run = async function run(props: Props) {
|
|
|
59
64
|
await cli(`pwd`);
|
|
60
65
|
|
|
61
66
|
// fetch origin master branch for latest sha
|
|
62
|
-
await cli(`git fetch --no-tags -v origin ${master_branch_name}
|
|
67
|
+
await cli(`git fetch --no-tags -v origin ${master_branch_name}`, { signal });
|
|
63
68
|
|
|
64
69
|
if (branch_name === master_branch_name) {
|
|
65
70
|
await rebase_master();
|
|
@@ -69,12 +74,41 @@ Rebase.run = async function run(props: Props) {
|
|
|
69
74
|
|
|
70
75
|
actions.unregister_abort_handler();
|
|
71
76
|
} catch (err) {
|
|
77
|
+
actions.unregister_abort_handler();
|
|
72
78
|
actions.error("Unable to rebase.");
|
|
73
79
|
|
|
74
80
|
if (err instanceof Error) {
|
|
75
81
|
actions.error(err.message);
|
|
76
82
|
}
|
|
77
83
|
|
|
84
|
+
actions.output(
|
|
85
|
+
<Ink.Box flexDirection="column">
|
|
86
|
+
<Ink.Box height={1} />
|
|
87
|
+
<Ink.Text>⚠️ WARNING</Ink.Text>
|
|
88
|
+
<FormatText
|
|
89
|
+
message="You are in a temporary branch {branch_name} based on {original_branch}"
|
|
90
|
+
values={{
|
|
91
|
+
branch_name: <Brackets>{temp_branch_name}</Brackets>,
|
|
92
|
+
original_branch: <Brackets>{branch_name}</Brackets>,
|
|
93
|
+
}}
|
|
94
|
+
/>
|
|
95
|
+
<FormatText
|
|
96
|
+
message="Fix merge conflicts then run {cp_continue} to proceed"
|
|
97
|
+
values={{
|
|
98
|
+
cp_continue: <Command>git cherry-pick --continue</Command>,
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
<FormatText
|
|
102
|
+
message="To go back to the original branch run {checkout_original}"
|
|
103
|
+
values={{
|
|
104
|
+
checkout_original: (
|
|
105
|
+
<Command>git cherry-pick --abort && git checkout -f {branch_name}</Command>
|
|
106
|
+
),
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
</Ink.Box>,
|
|
110
|
+
);
|
|
111
|
+
|
|
78
112
|
actions.exit(8);
|
|
79
113
|
}
|
|
80
114
|
|
|
@@ -105,7 +139,7 @@ Rebase.run = async function run(props: Props) {
|
|
|
105
139
|
}
|
|
106
140
|
|
|
107
141
|
async function rebase_master() {
|
|
108
|
-
await cli(`git switch -C "${master_branch_name}" "${master_branch}"
|
|
142
|
+
await cli(`git switch -C "${master_branch_name}" "${master_branch}"`, { signal });
|
|
109
143
|
}
|
|
110
144
|
|
|
111
145
|
async function rebase_branch() {
|
|
@@ -115,7 +149,7 @@ Rebase.run = async function run(props: Props) {
|
|
|
115
149
|
const rebase_merge_base = master_sha;
|
|
116
150
|
|
|
117
151
|
// create temporary branch based on merge base
|
|
118
|
-
await cli(`git checkout -b ${temp_branch_name} ${rebase_merge_base}
|
|
152
|
+
await cli(`git checkout -b ${temp_branch_name} ${rebase_merge_base}`, { signal });
|
|
119
153
|
|
|
120
154
|
const picked_commit_list = [];
|
|
121
155
|
|
|
@@ -157,17 +191,17 @@ Rebase.run = async function run(props: Props) {
|
|
|
157
191
|
|
|
158
192
|
if (picked_commit_list.length > 0) {
|
|
159
193
|
// ensure clean base to avoid conflicts when applying patch
|
|
160
|
-
await cli(`git clean -fd
|
|
194
|
+
await cli(`git clean -fd`, { signal });
|
|
161
195
|
|
|
162
196
|
// create list of sha for cherry-pick
|
|
163
197
|
const sha_list = picked_commit_list.map((commit) => commit.sha).join(" ");
|
|
164
198
|
|
|
165
|
-
await cli(`git cherry-pick --keep-redundant-commits ${sha_list}
|
|
199
|
+
await cli(`git cherry-pick --keep-redundant-commits ${sha_list}`, { signal });
|
|
166
200
|
}
|
|
167
201
|
|
|
168
202
|
// after all commits have been cherry-picked move the pointer
|
|
169
203
|
// of original branch to the newly created temporary branch
|
|
170
|
-
await cli(`git branch -f ${branch_name} ${temp_branch_name}
|
|
204
|
+
await cli(`git branch -f ${branch_name} ${temp_branch_name}`, { signal });
|
|
171
205
|
|
|
172
206
|
actions.debug("start restore_git()");
|
|
173
207
|
restore_git();
|
package/src/core/cli.ts
CHANGED
|
@@ -38,7 +38,15 @@ export async function cli(
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
return new Promise((resolve, reject) => {
|
|
41
|
-
|
|
41
|
+
let childProcess: child.ChildProcess;
|
|
42
|
+
try {
|
|
43
|
+
childProcess = child.spawn("sh", ["-c", command], options);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
reject(err);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let settled = false;
|
|
42
50
|
|
|
43
51
|
let stdout = "";
|
|
44
52
|
let stderr = "";
|
|
@@ -71,7 +79,11 @@ export async function cli(
|
|
|
71
79
|
});
|
|
72
80
|
|
|
73
81
|
childProcess.on("close", (unsafe_code) => {
|
|
74
|
-
|
|
82
|
+
if (settled) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
settled = true;
|
|
75
87
|
|
|
76
88
|
const result = {
|
|
77
89
|
command,
|
|
@@ -79,11 +91,12 @@ export async function cli(
|
|
|
79
91
|
stdout: stdout.trimEnd(),
|
|
80
92
|
stderr: stderr.trimEnd(),
|
|
81
93
|
output: output.trimEnd(),
|
|
82
|
-
duration,
|
|
94
|
+
duration: timer.duration(),
|
|
83
95
|
};
|
|
84
96
|
|
|
85
97
|
state.actions.debug_pending_end(id);
|
|
86
98
|
state.actions.debug(log.end(result));
|
|
99
|
+
|
|
87
100
|
if (!options.quiet) {
|
|
88
101
|
state.actions.debug(log.output(result));
|
|
89
102
|
}
|
|
@@ -97,6 +110,24 @@ export async function cli(
|
|
|
97
110
|
});
|
|
98
111
|
|
|
99
112
|
childProcess.on("error", (err) => {
|
|
113
|
+
if (settled) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
settled = true;
|
|
118
|
+
|
|
119
|
+
const result = {
|
|
120
|
+
command,
|
|
121
|
+
code: -1,
|
|
122
|
+
stdout: stdout.trimEnd(),
|
|
123
|
+
stderr: stderr.trimEnd(),
|
|
124
|
+
output: output.trimEnd(),
|
|
125
|
+
duration: timer.duration(),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
state.actions.debug_pending_end(id);
|
|
129
|
+
state.actions.debug(log.abort({ result, err }));
|
|
130
|
+
|
|
100
131
|
reject(err);
|
|
101
132
|
});
|
|
102
133
|
});
|
|
@@ -173,6 +204,12 @@ const log = {
|
|
|
173
204
|
return `${command} (exit_code=${code} duration=${duration})`;
|
|
174
205
|
},
|
|
175
206
|
|
|
207
|
+
abort({ result, err }: { result: Return; err?: unknown }) {
|
|
208
|
+
const { command, duration } = result;
|
|
209
|
+
const err_message = err instanceof Error ? err.message : String(err);
|
|
210
|
+
return `[error] ${command} err=${err_message} (duration=${duration})`;
|
|
211
|
+
},
|
|
212
|
+
|
|
176
213
|
error(result: Return) {
|
|
177
214
|
const lines = [result.output, this.non_zero_exit(result)];
|
|
178
215
|
return lines.join("\n");
|