git-stack-cli 1.13.2 → 1.15.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.
@@ -14912,7 +14912,7 @@ var isSafeInteger = hasNativeIsSafeInteger
14912
14912
  // IE11 does not support y and u.
14913
14913
  var REGEX_SUPPORTS_U_AND_Y = true;
14914
14914
  try {
14915
- var re = RE$5('([^\\p{White_Space}\\p{Pattern_Syntax}]*)', 'yu');
14915
+ var re = RE$6('([^\\p{White_Space}\\p{Pattern_Syntax}]*)', 'yu');
14916
14916
  /**
14917
14917
  * legacy Edge or Xbox One browser
14918
14918
  * Unicode flag support: supported
@@ -15009,14 +15009,14 @@ var trimEnd = hasTrimEnd
15009
15009
  return s.replace(SPACE_SEPARATOR_END_REGEX, '');
15010
15010
  };
15011
15011
  // Prevent minifier to translate new RegExp to literal form that might cause syntax error on IE11.
15012
- function RE$5(s, flag) {
15012
+ function RE$6(s, flag) {
15013
15013
  return new RegExp(s, flag);
15014
15014
  }
15015
15015
  // #endregion
15016
15016
  var matchIdentifierAtIndex;
15017
15017
  if (REGEX_SUPPORTS_U_AND_Y) {
15018
15018
  // Native
15019
- var IDENTIFIER_PREFIX_RE_1 = RE$5('([^\\p{White_Space}\\p{Pattern_Syntax}]*)', 'yu');
15019
+ var IDENTIFIER_PREFIX_RE_1 = RE$6('([^\\p{White_Space}\\p{Pattern_Syntax}]*)', 'yu');
15020
15020
  matchIdentifierAtIndex = function matchIdentifierAtIndex(s, index) {
15021
15021
  var _a;
15022
15022
  IDENTIFIER_PREFIX_RE_1.lastIndex = index;
@@ -18487,15 +18487,41 @@ const immerImpl = (initializer) => (set, get, store) => {
18487
18487
  };
18488
18488
  const immer = immerImpl;
18489
18489
 
18490
+ async function sleep(time) {
18491
+ return new Promise((resolve) => setTimeout(resolve, time));
18492
+ }
18493
+
18490
18494
  function Exit(props) {
18491
- const actions = Store.useActions();
18492
18495
  reactExports.useEffect(() => {
18493
- if (props.clear) {
18494
- actions.clear();
18496
+ // immediately handle exit on mount
18497
+ handle_exit().catch((err) => {
18498
+ // eslint-disable-next-line no-console
18499
+ console.error(err);
18500
+ });
18501
+ async function handle_exit() {
18502
+ const state = Store.getState();
18503
+ const actions = state.actions;
18504
+ actions.debug(`[Exit] handle_exit ${JSON.stringify(props)}`);
18505
+ let exit_code = props.code;
18506
+ // run abort_handler if it exists
18507
+ if (state.abort_handler) {
18508
+ exit_code = await state.abort_handler();
18509
+ }
18510
+ // restore git stash if necessary
18511
+ if (state.is_dirty_check_stash) {
18512
+ await cli("git stash pop");
18513
+ actions.output(reactExports.createElement(Text, { color: colors.green }, "\u2705 Changes restored from stash"));
18514
+ }
18515
+ // ensure output has a chance to render
18516
+ await sleep(1);
18517
+ // finally handle the actual app and process exit
18518
+ if (props.clear) {
18519
+ actions.clear();
18520
+ }
18521
+ actions.unmount();
18522
+ process.exitCode = exit_code;
18523
+ process.exit();
18495
18524
  }
18496
- actions.unmount();
18497
- process.exitCode = props.code;
18498
- process.exit();
18499
18525
  }, [props.clear, props.code]);
18500
18526
  return null;
18501
18527
  }
@@ -26197,6 +26223,10 @@ function LogTimestamp() {
26197
26223
  return (reactExports.createElement(Text, { dimColor: true }, DateTime.now().toFormat("[yyyy-MM-dd HH:mm:ss.SSS] ")));
26198
26224
  }
26199
26225
 
26226
+ function pretty_json(input) {
26227
+ return JSON.stringify(input, null, 2);
26228
+ }
26229
+
26200
26230
  const BaseStore = createStore()(immer((set, get) => ({
26201
26231
  // set immediately in `index.tsx` so no `null` scenario
26202
26232
  process_argv: [],
@@ -26214,6 +26244,8 @@ const BaseStore = createStore()(immer((set, get) => ({
26214
26244
  pr_templates: [],
26215
26245
  pr_template_body: null,
26216
26246
  sync_github: null,
26247
+ is_dirty_check_stash: false,
26248
+ abort_handler: null,
26217
26249
  step: "loading",
26218
26250
  output: [],
26219
26251
  pending_output: {},
@@ -26239,7 +26271,7 @@ const BaseStore = createStore()(immer((set, get) => ({
26239
26271
  },
26240
26272
  json(value) {
26241
26273
  set((state) => {
26242
- const node = JSON.stringify(value, null, 2);
26274
+ const node = pretty_json(value);
26243
26275
  state.mutate.output(state, { node });
26244
26276
  });
26245
26277
  },
@@ -26276,6 +26308,16 @@ const BaseStore = createStore()(immer((set, get) => ({
26276
26308
  state.pr = {};
26277
26309
  });
26278
26310
  },
26311
+ register_abort_handler(abort_handler) {
26312
+ set((state) => {
26313
+ state.abort_handler = abort_handler;
26314
+ });
26315
+ },
26316
+ unregister_abort_handler() {
26317
+ set((state) => {
26318
+ state.abort_handler = null;
26319
+ });
26320
+ },
26279
26321
  set(setter) {
26280
26322
  set((state) => {
26281
26323
  setter(state);
@@ -26561,10 +26603,6 @@ function semver_compare(version_a, version_b) {
26561
26603
  return 0;
26562
26604
  }
26563
26605
 
26564
- async function sleep(time) {
26565
- return new Promise((resolve) => setTimeout(resolve, time));
26566
- }
26567
-
26568
26606
  function reducer$4(state, patch) {
26569
26607
  return { ...state, ...patch };
26570
26608
  }
@@ -26898,7 +26936,7 @@ function Debug() {
26898
26936
  const output_file = path.join(state.cwd, "git-stack-state.json");
26899
26937
  await safe_rm(output_file);
26900
26938
  const serialized = serialize(state);
26901
- const content = JSON.stringify(serialized, null, 2);
26939
+ const content = pretty_json(serialized);
26902
26940
  await fs$1.writeFile(output_file, content);
26903
26941
  }
26904
26942
  }, [argv, state]);
@@ -26941,15 +26979,15 @@ match_group.safe = (value, re, group) => {
26941
26979
 
26942
26980
  function auth_status(output) {
26943
26981
  let username;
26944
- username = match_group.safe(output, RE$4.logged_in_as, "username");
26982
+ username = match_group.safe(output, RE$5.logged_in_as, "username");
26945
26983
  if (username)
26946
26984
  return username;
26947
- username = match_group.safe(output, RE$4.logged_in_account, "username");
26985
+ username = match_group.safe(output, RE$5.logged_in_account, "username");
26948
26986
  if (username)
26949
26987
  return username;
26950
26988
  return null;
26951
26989
  }
26952
- const RE$4 = {
26990
+ const RE$5 = {
26953
26991
  // Logged in to github.com as magus
26954
26992
  logged_in_as: /Logged in to github.com as (?<username>[^\s]+)/,
26955
26993
  logged_in_account: /Logged in to github.com account (?<username>[^\s]+)/,
@@ -29663,10 +29701,10 @@ var cloneDeep$1 = /*@__PURE__*/getDefaultExportFromCjs(cloneDeep_1);
29663
29701
  // escape double-quote for cli
29664
29702
  function safe_quote(value) {
29665
29703
  let result = value;
29666
- result = result.replace(RE$3.all_double_quote, '\\"');
29704
+ result = result.replace(RE$4.all_double_quote, '\\"');
29667
29705
  return result;
29668
29706
  }
29669
- const RE$3 = {
29707
+ const RE$4 = {
29670
29708
  all_double_quote: /"/g,
29671
29709
  };
29672
29710
 
@@ -29684,12 +29722,12 @@ function write$1(message, values) {
29684
29722
  }
29685
29723
  function read(message) {
29686
29724
  const values = { id: null, title: null };
29687
- const match_id = message.match(RE$2.stack_id);
29725
+ const match_id = message.match(RE$3.stack_id);
29688
29726
  if (match_id?.groups) {
29689
29727
  values.id = match_id.groups["id"];
29690
29728
  invariant(values.id, "id must exist");
29691
29729
  }
29692
- const match_title = message.match(RE$2.group_title);
29730
+ const match_title = message.match(RE$3.group_title);
29693
29731
  if (match_title?.groups) {
29694
29732
  values.title = match_title.groups["title"];
29695
29733
  }
@@ -29698,8 +29736,8 @@ function read(message) {
29698
29736
  function remove(message) {
29699
29737
  let result = message;
29700
29738
  // remove metadata
29701
- result = result.replace(new RegExp(RE$2.stack_id, "gmi"), "");
29702
- result = result.replace(new RegExp(RE$2.group_title, "gmi"), "");
29739
+ result = result.replace(new RegExp(RE$3.stack_id, "gmi"), "");
29740
+ result = result.replace(new RegExp(RE$3.group_title, "gmi"), "");
29703
29741
  result = result.trimEnd();
29704
29742
  return result;
29705
29743
  }
@@ -29711,7 +29749,7 @@ const TEMPLATE$1 = {
29711
29749
  return `git-stack-title: ${title}`;
29712
29750
  },
29713
29751
  };
29714
- const RE$2 = {
29752
+ const RE$3 = {
29715
29753
  // https://regex101.com/r/wLmGVq/1
29716
29754
  stack_id: new RegExp(`${TEMPLATE$1.stack_id("(?<id>[^\\s]+)")}`, "i"),
29717
29755
  group_title: new RegExp(TEMPLATE$1.group_title("(?<title>[^\\n^\\r]+)"), "i"),
@@ -29788,11 +29826,17 @@ async function pr_create(args) {
29788
29826
  // pull request create failed: GraphQL: Head sha can't be blank, Base sha can't be blank, No commits between gs-6LAx-On45 and origin/gs-ED2etrzv2, Head ref must be a branch (createPullRequest)
29789
29827
  //
29790
29828
  // https://github.com/cli/cli/issues/5465
29791
- let command = `gh pr create --head refs/heads/${args.branch} --base ${args.base} --title="${title}" --body="${args.body}"`;
29829
+ let command_parts = [
29830
+ "gh pr create",
29831
+ `--head refs/heads/${args.branch}`,
29832
+ `--base ${args.base}`,
29833
+ `--title="${title}"`,
29834
+ `--body="${args.body}"`,
29835
+ ];
29792
29836
  if (args.draft) {
29793
- command += " --draft";
29837
+ command_parts.push("--draft");
29794
29838
  }
29795
- const cli_result = await cli(command);
29839
+ const cli_result = await cli(command_parts);
29796
29840
  if (cli_result.code !== 0) {
29797
29841
  handle_error(cli_result.output);
29798
29842
  return null;
@@ -29801,15 +29845,19 @@ async function pr_create(args) {
29801
29845
  }
29802
29846
  async function pr_edit(args) {
29803
29847
  const command_parts = [`gh pr edit ${args.branch} --base ${args.base}`];
29848
+ let body_file;
29804
29849
  if (args.body) {
29805
- const body_file = await write_body_file(args);
29850
+ body_file = await write_body_file(args);
29806
29851
  command_parts.push(`--body-file="${body_file}"`);
29807
29852
  }
29808
- const command = command_parts.join(" ");
29809
- const cli_result = await cli(command);
29853
+ const cli_result = await cli(command_parts);
29810
29854
  if (cli_result.code !== 0) {
29811
29855
  handle_error(cli_result.output);
29812
29856
  }
29857
+ // cleanup body_file
29858
+ if (body_file) {
29859
+ await safe_rm(body_file);
29860
+ }
29813
29861
  }
29814
29862
  async function pr_draft(args) {
29815
29863
  // https://cli.github.com/manual/gh_api
@@ -29873,11 +29921,19 @@ function handle_error(output) {
29873
29921
  async function write_body_file(args) {
29874
29922
  invariant(args.body, "args.body must exist");
29875
29923
  const temp_dir = os.tmpdir();
29876
- const temp_path = path.join(temp_dir, `git-stack-body-${args.base}`);
29924
+ // ensure unique filename is safe for filesystem
29925
+ // base (group id) might contain slashes, e.g. dev/magus/gs-3cmrMBSUj
29926
+ // the flashes would mess up the filesystem path to this file
29927
+ let temp_filename = `git-stack-body-${args.base}`;
29928
+ temp_filename = temp_filename.replace(RE$2.non_alphanumeric_dash, "-");
29929
+ const temp_path = path.join(temp_dir, temp_filename);
29877
29930
  await safe_rm(temp_path);
29878
29931
  await fs$1.writeFile(temp_path, args.body);
29879
29932
  return temp_path;
29880
29933
  }
29934
+ const RE$2 = {
29935
+ non_alphanumeric_dash: /[^a-zA-Z0-9_-]+/g,
29936
+ };
29881
29937
 
29882
29938
  async function range(commit_group_map) {
29883
29939
  const master_branch = Store.getState().master_branch;
@@ -30173,6 +30229,8 @@ echo "$GIT_REVISE_TODO" > "$git_revise_todo_path"
30173
30229
  // change to pipe to see output temporarily
30174
30230
  // https://github.com/magus/git-stack-cli/commit/f9f10e3ac3cd9a35ee75d3e0851a48391967a23f
30175
30231
  await cli(command, { stdio: ["ignore", "ignore", "ignore"] });
30232
+ // cleanup tmp_git_sequence_editor_path
30233
+ await safe_rm(tmp_git_sequence_editor_path);
30176
30234
  };
30177
30235
 
30178
30236
  function reducer$2(state, patch) {
@@ -30289,19 +30347,23 @@ function DirtyCheck(props) {
30289
30347
  case "done":
30290
30348
  return props.children;
30291
30349
  case "prompt":
30292
- return (reactExports.createElement(YesNoPrompt, { message: reactExports.createElement(Box, { flexDirection: "column" },
30293
- reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.yellow }), message: "{git} repo has uncommitted changes.", values: {
30294
- git: reactExports.createElement(Command, null, "git"),
30295
- git_stack: reactExports.createElement(Command, null, "git stack"),
30296
- } }),
30297
- reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.yellow }), message: "Changes may be lost during {git_stack}, are you sure you want to proceed?", values: {
30298
- git: reactExports.createElement(Command, null, "git"),
30299
- git_stack: reactExports.createElement(Command, null, "git stack"),
30300
- } })), onYes: async () => {
30301
- patch({ status: "done" });
30302
- }, onNo: async () => {
30303
- actions.exit(0);
30304
- } }));
30350
+ return (reactExports.createElement(Box, { flexDirection: "column" },
30351
+ reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.yellow }), message: "\u26A0\uFE0F Uncommitted changes detected. {git_stack} needs a clean working tree.", values: {
30352
+ git: reactExports.createElement(Command, null, "git"),
30353
+ git_stack: reactExports.createElement(Command, null, "git stack"),
30354
+ } }),
30355
+ reactExports.createElement(YesNoPrompt, { message: reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.yellow }), message: "{git_stash} changes to proceed?", values: {
30356
+ git_stash: reactExports.createElement(Command, null, "git stash"),
30357
+ } }), onYes: async () => {
30358
+ await cli("git stash --include-untracked");
30359
+ actions.output(reactExports.createElement(Text, null, "\uD83D\uDCE6 Changes saved to stash"));
30360
+ actions.set((state) => {
30361
+ state.is_dirty_check_stash = true;
30362
+ });
30363
+ patch({ status: "done" });
30364
+ }, onNo: async () => {
30365
+ actions.exit(0);
30366
+ } })));
30305
30367
  default:
30306
30368
  return (reactExports.createElement(Await, { function: run, fallback: reactExports.createElement(Text, { color: colors.yellow },
30307
30369
  "Ensuring ",
@@ -30312,8 +30374,12 @@ function DirtyCheck(props) {
30312
30374
  const actions = Store.getState().actions;
30313
30375
  try {
30314
30376
  const git_dirty = (await cli(`git status --porcelain`)).stdout;
30315
- const status = git_dirty ? "prompt" : "done";
30316
- patch({ status });
30377
+ if (!git_dirty) {
30378
+ patch({ status: "done" });
30379
+ }
30380
+ else {
30381
+ patch({ status: "prompt" });
30382
+ }
30317
30383
  }
30318
30384
  catch (err) {
30319
30385
  actions.error("Must be run from within a git repository.");
@@ -30463,6 +30529,32 @@ async function run$9() {
30463
30529
  reactExports.createElement(Text, { bold: true, color: colors.yellow }, time_until))));
30464
30530
  }
30465
30531
 
30532
+ function HandleCtrlCSigint() {
30533
+ const actions = Store.useActions();
30534
+ const [exiting, set_exiting] = reactExports.useState(false);
30535
+ useInput((input, key) => {
30536
+ handle_input().catch((err) => {
30537
+ // eslint-disable-next-line no-console
30538
+ console.error(err);
30539
+ });
30540
+ async function handle_input() {
30541
+ if (input === "c" && key.ctrl) {
30542
+ actions.clear();
30543
+ actions.output(reactExports.createElement(Text, { color: colors.red },
30544
+ reactExports.createElement(FormatText, { message: "\uD83D\uDEA8 Ctrl+C detected" })));
30545
+ set_exiting(true);
30546
+ await sleep(1);
30547
+ actions.exit(235);
30548
+ }
30549
+ }
30550
+ });
30551
+ if (exiting) {
30552
+ return (reactExports.createElement(Text, { color: colors.red },
30553
+ reactExports.createElement(FormatText, { message: "\uD83D\uDEA8 Exiting\u2026" })));
30554
+ }
30555
+ return null;
30556
+ }
30557
+
30466
30558
  function LocalCommitStatus(props) {
30467
30559
  const argv = Store.useState((state) => state.argv);
30468
30560
  const fallback = (reactExports.createElement(Text, { color: colors.yellow }, "Fetching PR status from Github\u2026"));
@@ -30559,21 +30651,9 @@ function encode(value) {
30559
30651
  }
30560
30652
 
30561
30653
  function Rebase() {
30562
- const abort_handler = reactExports.useRef(() => { });
30563
- reactExports.useEffect(function listen_sigint() {
30564
- process.once("SIGINT", sigint_handler);
30565
- return function cleanup() {
30566
- process.removeListener("SIGINT", sigint_handler);
30567
- };
30568
- function sigint_handler() {
30569
- abort_handler.current();
30570
- }
30571
- }, []);
30572
- return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026"), function: async function () {
30573
- await Rebase.run({ abort_handler });
30574
- } }));
30654
+ return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026"), function: Rebase.run }));
30575
30655
  }
30576
- Rebase.run = async function run(args) {
30656
+ Rebase.run = async function run() {
30577
30657
  const state = Store.getState();
30578
30658
  const actions = state.actions;
30579
30659
  const branch_name = state.branch_name;
@@ -30584,11 +30664,12 @@ Rebase.run = async function run(args) {
30584
30664
  invariant(branch_name, "branch_name must exist");
30585
30665
  invariant(commit_range, "commit_range must exist");
30586
30666
  invariant(repo_root, "repo_root must exist");
30587
- // always listen for SIGINT event and restore git state
30588
- args.abort_handler.current = async function sigint_handler() {
30667
+ // immediately register abort_handler in case of ctrl+c exit
30668
+ actions.register_abort_handler(async function abort_rebase() {
30589
30669
  actions.output(reactExports.createElement(Text, { color: colors.red }, "\uD83D\uDEA8 Abort"));
30590
- handle_exit(19);
30591
- };
30670
+ handle_exit();
30671
+ return 19;
30672
+ });
30592
30673
  const temp_branch_name = `${branch_name}_${short_id()}`;
30593
30674
  try {
30594
30675
  // actions.debug(`commit_range=${JSON.stringify(commit_range, null, 2)}`);
@@ -30639,6 +30720,7 @@ Rebase.run = async function run(args) {
30639
30720
  branch_name: reactExports.createElement(Brackets, null, branch_name),
30640
30721
  origin_branch: reactExports.createElement(Brackets, null, `origin/${master_branch}`),
30641
30722
  } }));
30723
+ actions.unregister_abort_handler();
30642
30724
  actions.set((state) => {
30643
30725
  state.commit_range = next_commit_range;
30644
30726
  state.step = "status";
@@ -30651,7 +30733,8 @@ Rebase.run = async function run(args) {
30651
30733
  actions.error(err.message);
30652
30734
  }
30653
30735
  }
30654
- handle_exit(20);
30736
+ handle_exit();
30737
+ actions.exit(20);
30655
30738
  }
30656
30739
  // cleanup git operations if cancelled during manual rebase
30657
30740
  function restore_git() {
@@ -30673,7 +30756,7 @@ Rebase.run = async function run(args) {
30673
30756
  }
30674
30757
  cli.sync(`pwd`, spawn_options);
30675
30758
  }
30676
- function handle_exit(code) {
30759
+ function handle_exit() {
30677
30760
  actions.output(reactExports.createElement(Text, { color: colors.yellow },
30678
30761
  "Restoring ",
30679
30762
  reactExports.createElement(Brackets, null, branch_name),
@@ -30683,7 +30766,6 @@ Rebase.run = async function run(args) {
30683
30766
  "Restored ",
30684
30767
  reactExports.createElement(Brackets, null, branch_name),
30685
30768
  "."));
30686
- actions.exit(code);
30687
30769
  }
30688
30770
  };
30689
30771
 
@@ -30692,21 +30774,9 @@ function LocalMergeRebase() {
30692
30774
  }
30693
30775
 
30694
30776
  function ManualRebase() {
30695
- const abort_handler = reactExports.useRef(() => { });
30696
- reactExports.useEffect(function listen_sigint() {
30697
- process.once("SIGINT", sigint_handler);
30698
- return function cleanup() {
30699
- process.removeListener("SIGINT", sigint_handler);
30700
- };
30701
- async function sigint_handler() {
30702
- abort_handler.current();
30703
- }
30704
- }, []);
30705
- return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026"), function: async function () {
30706
- await run$7({ abort_handler });
30707
- } }));
30777
+ return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026"), function: run$7 }));
30708
30778
  }
30709
- async function run$7(args) {
30779
+ async function run$7() {
30710
30780
  const state = Store.getState();
30711
30781
  const actions = state.actions;
30712
30782
  const argv = state.argv;
@@ -30718,11 +30788,12 @@ async function run$7(args) {
30718
30788
  invariant(branch_name, "branch_name must exist");
30719
30789
  invariant(commit_map, "commit_map must exist");
30720
30790
  invariant(repo_root, "repo_root must exist");
30721
- // always listen for SIGINT event and restore git state
30722
- args.abort_handler.current = function sigint_handler() {
30791
+ // immediately register abort_handler in case of ctrl+c exit
30792
+ actions.register_abort_handler(async function abort_manual_rebase() {
30723
30793
  actions.output(reactExports.createElement(Text, { color: colors.red }, "\uD83D\uDEA8 Abort"));
30724
- handle_exit(15);
30725
- };
30794
+ handle_exit();
30795
+ return 15;
30796
+ });
30726
30797
  const temp_branch_name = `${branch_name}_${short_id()}`;
30727
30798
  try {
30728
30799
  // get latest merge_base relative to local master
@@ -30777,6 +30848,7 @@ async function run$7(args) {
30777
30848
  // of original branch to the newly created temporary branch
30778
30849
  await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
30779
30850
  restore_git();
30851
+ actions.unregister_abort_handler();
30780
30852
  if (argv.sync) {
30781
30853
  actions.set((state) => {
30782
30854
  state.step = "sync-github";
@@ -30797,7 +30869,8 @@ async function run$7(args) {
30797
30869
  if (!argv.verbose) {
30798
30870
  actions.error("Try again with `--verbose` to see more information.");
30799
30871
  }
30800
- handle_exit(16);
30872
+ handle_exit();
30873
+ actions.exit(16);
30801
30874
  }
30802
30875
  // cleanup git operations if cancelled during manual rebase
30803
30876
  function restore_git() {
@@ -30819,7 +30892,7 @@ async function run$7(args) {
30819
30892
  }
30820
30893
  cli.sync(`pwd`, spawn_options);
30821
30894
  }
30822
- function handle_exit(code) {
30895
+ function handle_exit() {
30823
30896
  actions.output(reactExports.createElement(Text, { color: colors.yellow },
30824
30897
  "Restoring ",
30825
30898
  reactExports.createElement(Brackets, null, branch_name),
@@ -30829,7 +30902,6 @@ async function run$7(args) {
30829
30902
  "Restored ",
30830
30903
  reactExports.createElement(Brackets, null, branch_name),
30831
30904
  "."));
30832
- actions.exit(code);
30833
30905
  }
30834
30906
  }
30835
30907
 
@@ -31534,8 +31606,20 @@ function SelectCommitRangesInternal(props) {
31534
31606
  reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.gray }), message: "Press {enter} to toggle commit selection", values: {
31535
31607
  enter: (reactExports.createElement(Text, { bold: true, color: colors.green }, SYMBOL.enter)),
31536
31608
  } }))));
31609
+ function get_group_id() {
31610
+ let branch_prefix = "";
31611
+ // branch prefix via cli flag or env var
31612
+ // cli flag takes precedence since it is more explicit
31613
+ if (argv["branch-prefix"]) {
31614
+ branch_prefix = argv["branch-prefix"];
31615
+ }
31616
+ else if (process.env.GIT_STACK_BRANCH_PREFIX) {
31617
+ branch_prefix = process.env.GIT_STACK_BRANCH_PREFIX;
31618
+ }
31619
+ return `${branch_prefix}${gs_short_id()}`;
31620
+ }
31537
31621
  function submit_group_input(title) {
31538
- const id = gs_short_id();
31622
+ const id = get_group_id();
31539
31623
  actions.output(reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { dimColor: true }), message: "Created new group {group} {note}", values: {
31540
31624
  group: reactExports.createElement(Brackets, null, title),
31541
31625
  note: reactExports.createElement(Parens, null, id),
@@ -31732,21 +31816,9 @@ const RE = {
31732
31816
  };
31733
31817
 
31734
31818
  function SyncGithub() {
31735
- const abort_handler = reactExports.useRef(() => { });
31736
- reactExports.useEffect(function listen_sigint() {
31737
- process.once("SIGINT", sigint_handler);
31738
- return function cleanup() {
31739
- process.removeListener("SIGINT", sigint_handler);
31740
- };
31741
- function sigint_handler() {
31742
- abort_handler.current();
31743
- }
31744
- }, []);
31745
- return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Syncing\u2026"), function: async function () {
31746
- await run$3({ abort_handler });
31747
- } }));
31819
+ return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Syncing\u2026"), function: run$3 }));
31748
31820
  }
31749
- async function run$3(args) {
31821
+ async function run$3() {
31750
31822
  const state = Store.getState();
31751
31823
  const actions = state.actions;
31752
31824
  const argv = state.argv;
@@ -31761,11 +31833,12 @@ async function run$3(args) {
31761
31833
  invariant(sync_github, "sync_github must exist");
31762
31834
  const commit_range = sync_github.commit_range;
31763
31835
  const rebase_group_index = sync_github.rebase_group_index;
31764
- // always listen for SIGINT event and restore pr state
31765
- args.abort_handler.current = function sigint_handler() {
31836
+ // immediately register abort_handler in case of ctrl+c exit
31837
+ actions.register_abort_handler(async function abort_sync_github() {
31766
31838
  actions.output(reactExports.createElement(Text, { color: colors.red }, "\uD83D\uDEA8 Abort"));
31767
- handle_exit(17);
31768
- };
31839
+ handle_exit();
31840
+ return 17;
31841
+ });
31769
31842
  let DEFAULT_PR_BODY = "";
31770
31843
  if (state.pr_template_body) {
31771
31844
  DEFAULT_PR_BODY = state.pr_template_body;
@@ -31834,6 +31907,7 @@ async function run$3(args) {
31834
31907
  update_pr_body_tasks.push(task);
31835
31908
  }
31836
31909
  await Promise.all(update_pr_body_tasks);
31910
+ actions.unregister_abort_handler();
31837
31911
  actions.set((state) => {
31838
31912
  state.step = "post-rebase-status";
31839
31913
  });
@@ -31846,7 +31920,8 @@ async function run$3(args) {
31846
31920
  if (!argv.verbose) {
31847
31921
  actions.error("Try again with `--verbose` to see more information.");
31848
31922
  }
31849
- await handle_exit(18);
31923
+ handle_exit();
31924
+ actions.exit(18);
31850
31925
  }
31851
31926
  function get_push_group_list() {
31852
31927
  // start from HEAD and work backward to rebase_group_index
@@ -31955,7 +32030,7 @@ async function run$3(args) {
31955
32030
  });
31956
32031
  }
31957
32032
  }
31958
- function handle_exit(code) {
32033
+ function handle_exit() {
31959
32034
  actions.output(reactExports.createElement(Text, { color: colors.yellow }, "Restoring PR state\u2026"));
31960
32035
  for (const group of push_group_list) {
31961
32036
  // we may temporarily mark PR as a draft before editing it
@@ -31971,7 +32046,6 @@ async function run$3(args) {
31971
32046
  }
31972
32047
  }
31973
32048
  actions.output(reactExports.createElement(Text, { color: colors.yellow }, "Restored PR state."));
31974
- actions.exit(code);
31975
32049
  }
31976
32050
  }
31977
32051
  const get_group_url = (group) => group.pr?.url || group.id;
@@ -32154,7 +32228,7 @@ async function run$1() {
32154
32228
  });
32155
32229
  if (diff_cmd.code) {
32156
32230
  save_stash = true;
32157
- await cli("git stash -q");
32231
+ await cli("git stash --include-untracked");
32158
32232
  actions.output(reactExports.createElement(Text, null, "\uD83D\uDCE6 Changes saved to stash"));
32159
32233
  }
32160
32234
  try {
@@ -32174,7 +32248,7 @@ async function run$1() {
32174
32248
  }
32175
32249
  finally {
32176
32250
  if (save_stash) {
32177
- await cli("git stash pop -q");
32251
+ await cli("git stash pop");
32178
32252
  actions.output(reactExports.createElement(Text, { color: colors.green }, "\u2705 Changes restored from stash"));
32179
32253
  }
32180
32254
  }
@@ -32253,7 +32327,8 @@ function App() {
32253
32327
  reactExports.createElement(DependencyCheck, null,
32254
32328
  reactExports.createElement(RebaseCheck, null,
32255
32329
  reactExports.createElement(CherryPickCheck, null,
32256
- reactExports.createElement(MaybeMain, null))))))));
32330
+ reactExports.createElement(MaybeMain, null)))))),
32331
+ reactExports.createElement(HandleCtrlCSigint, null)));
32257
32332
  }
32258
32333
  function MaybeMain() {
32259
32334
  const argv = Store.useState((state) => state.argv);
@@ -37595,7 +37670,7 @@ async function command() {
37595
37670
  .wrap(123)
37596
37671
  // disallow unknown options
37597
37672
  .strict()
37598
- .version("1.13.2" )
37673
+ .version("1.15.0" )
37599
37674
  .showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
37600
37675
  .help("help", "Show usage via `git stack help`")
37601
37676
  .argv;
@@ -37649,17 +37724,22 @@ const DefaultOptions = {
37649
37724
  default: false,
37650
37725
  description: "Open all PRs as drafts",
37651
37726
  },
37652
- "write-state-json": {
37653
- hidden: true,
37654
- type: "boolean",
37655
- default: false,
37656
- description: "Write state to local json file for debugging",
37727
+ "branch-prefix": {
37728
+ type: "string",
37729
+ default: "",
37730
+ description: "Prefix for generated branch names, e.g. dev/magus/",
37657
37731
  },
37658
37732
  "template": {
37659
37733
  type: "boolean",
37660
37734
  default: true,
37661
37735
  description: "Use automatic Github PR template, e.g. .github/pull_request_template.md, disable with --no-template",
37662
37736
  },
37737
+ "write-state-json": {
37738
+ hidden: true,
37739
+ type: "boolean",
37740
+ default: false,
37741
+ description: "Write state to local json file for debugging",
37742
+ },
37663
37743
  "mock-metadata": {
37664
37744
  hidden: true,
37665
37745
  type: "boolean",
@@ -37678,22 +37758,35 @@ const FixupOptions = {
37678
37758
  },
37679
37759
  };
37680
37760
 
37681
- command()
37682
- .then((argv) => {
37683
- const ink = render(reactExports.createElement(App, null), {
37684
- // If true, each update will be rendered as a separate output, without replacing the previous one.
37685
- // debug: true,
37686
- });
37687
- Store.setState((state) => {
37688
- state.ink = ink;
37689
- state.process_argv = process.argv;
37690
- state.argv = argv;
37691
- state.cwd = process.cwd();
37692
- });
37693
- Store.getState().actions.debug(JSON.stringify(argv, null, 2));
37694
- })
37761
+ (async function main() {
37762
+ try {
37763
+ const argv = await command();
37764
+ const ink = render(reactExports.createElement(App, null), {
37765
+ // If true, each update will be rendered as a separate output, without replacing the previous one.
37766
+ // debug: true,
37767
+ //
37768
+ // Configure whether Ink should listen to Ctrl+C keyboard input and exit the app.
37769
+ // We intentionally handle this ourselves in `<Exit />`
37770
+ exitOnCtrlC: false,
37771
+ });
37772
+ Store.setState((state) => {
37773
+ state.ink = ink;
37774
+ state.process_argv = process.argv;
37775
+ state.argv = argv;
37776
+ state.cwd = process.cwd();
37777
+ });
37778
+ Store.getState().actions.debug(pretty_json(argv));
37779
+ await ink.waitUntilExit();
37780
+ }
37781
+ catch (err) {
37782
+ // eslint-disable-next-line no-console
37783
+ console.error(err);
37784
+ process.exit(235);
37785
+ }
37786
+ })().catch((err) => {
37695
37787
  // eslint-disable-next-line no-console
37696
- .catch(console.error);
37788
+ console.error(err);
37789
+ });
37697
37790
 
37698
37791
  // prettier-ignore
37699
37792
  const METADATA = {