git-stack-cli 2.10.0 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
@@ -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
- const childProcess = child.spawn("sh", ["-c", command], options);
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
- const duration = timer.duration();
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");