git-stack-cli 2.6.1 → 2.7.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 +25 -6
- package/dist/js/index.js +367 -277
- package/package.json +1 -1
- package/src/app/App.tsx +3 -0
- package/src/app/SelectCommitRanges.tsx +4 -12
- package/src/command.ts +68 -50
- package/src/commands/Config.tsx +106 -0
- package/src/index.tsx +2 -2
- package/src/types/global.d.ts +1 -1
- package/src/core/gs_short_id.ts +0 -5
package/README.md
CHANGED
|
@@ -58,6 +58,15 @@ git stack --no-verify # skip git hooks such as pre-commit and pre-push
|
|
|
58
58
|
git stack help # print a table of all CLI arguments
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
### Adding new commits and pull requests
|
|
62
|
+
|
|
63
|
+
Adding commits to a PR is as simple as running `git stack` and adding commits to a PR.
|
|
64
|
+
|
|
65
|
+
1. Run `git stack`
|
|
66
|
+
1. Press `c` to create a new PR
|
|
67
|
+
1. Use `↑↓` and `space`/`enter` to navigate and assign commits to the PR
|
|
68
|
+
1. Press `s` to sync created PRs and commits to Github
|
|
69
|
+
|
|
61
70
|
### Editing existing commits and pull requests
|
|
62
71
|
|
|
63
72
|
Sometimes you want to add changes to an existing commit or pull request.
|
|
@@ -87,19 +96,29 @@ To update your local branch with the latest changes in the remote branch (e.g. `
|
|
|
87
96
|
git stack rebase
|
|
88
97
|
```
|
|
89
98
|
|
|
90
|
-
###
|
|
99
|
+
### Branch names
|
|
100
|
+
|
|
101
|
+
By default `git stack` generates a unique identifier and appends it to your local branch name.
|
|
91
102
|
|
|
92
|
-
|
|
103
|
+
For example, if you are locally working in `dev/feature-a`, PRs branch names will look like `dev/feature-a-3cmrMBSUj`.
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
### Overriding defaults
|
|
106
|
+
|
|
107
|
+
`git stack` has many optional flags to control its behavior.
|
|
108
|
+
You can view them by running `git stack help` and set them as needed.
|
|
109
|
+
|
|
110
|
+
If you prefer to set them once in your environment, you can use the command below to generate a one-time configuration.
|
|
96
111
|
|
|
97
112
|
```bash
|
|
98
|
-
|
|
113
|
+
❯ git stack config --draft --force
|
|
99
114
|
|
|
100
|
-
|
|
115
|
+
Add the line below to your shell rc file (.zshrc, .bashrc, etc.)
|
|
116
|
+
|
|
117
|
+
export GIT_STACK_CONFIG="{\"draft\":true,\"force\":true}"
|
|
101
118
|
```
|
|
102
119
|
|
|
120
|
+
Copy this environment variable into your shell configuration, e.g. `.zshrc`, `.bashrc`, etc.
|
|
121
|
+
|
|
103
122
|
## Why?
|
|
104
123
|
|
|
105
124
|
The goal of `git stack` is to combine the **simplicity of developing in a single branch** in order to **preserve your commit history** while also **grouping commits into pull requests for code review**.
|
package/dist/js/index.js
CHANGED
|
@@ -28098,7 +28098,7 @@ var require_last = __commonJS((exports, module) => {
|
|
|
28098
28098
|
});
|
|
28099
28099
|
|
|
28100
28100
|
// src/index.tsx
|
|
28101
|
-
var
|
|
28101
|
+
var React57 = __toESM(require_react(), 1);
|
|
28102
28102
|
import fs12 from "node:fs/promises";
|
|
28103
28103
|
import path9 from "node:path";
|
|
28104
28104
|
|
|
@@ -31935,7 +31935,7 @@ var import_react20 = __toESM(require_react(), 1);
|
|
|
31935
31935
|
// node_modules/.pnpm/ink-cjs@4.4.1_@types+react@18.2.33_react-devtools-core@4.19.1_react@18.2.0/node_modules/ink-cjs/build/hooks/use-focus-manager.js
|
|
31936
31936
|
var import_react21 = __toESM(require_react(), 1);
|
|
31937
31937
|
// src/app/App.tsx
|
|
31938
|
-
var
|
|
31938
|
+
var React56 = __toESM(require_react(), 1);
|
|
31939
31939
|
|
|
31940
31940
|
// src/app/AutoUpdate.tsx
|
|
31941
31941
|
var React18 = __toESM(require_react(), 1);
|
|
@@ -37467,7 +37467,7 @@ function AutoUpdate(props) {
|
|
|
37467
37467
|
let status2 = "init";
|
|
37468
37468
|
let latest_version = null;
|
|
37469
37469
|
let is_brew_bun_standalone = false;
|
|
37470
|
-
const local_version = "2.
|
|
37470
|
+
const local_version = "2.7.0";
|
|
37471
37471
|
const is_output = props_ref.current.verbose || props_ref.current.force;
|
|
37472
37472
|
async function auto_update() {
|
|
37473
37473
|
if (!local_version) {
|
|
@@ -39844,11 +39844,6 @@ function get_value(props) {
|
|
|
39844
39844
|
return props.value || props.defaultValue || "";
|
|
39845
39845
|
}
|
|
39846
39846
|
|
|
39847
|
-
// src/core/gs_short_id.ts
|
|
39848
|
-
function gs_short_id() {
|
|
39849
|
-
return `gs-${short_id()}`;
|
|
39850
|
-
}
|
|
39851
|
-
|
|
39852
39847
|
// src/app/SelectCommitRanges.tsx
|
|
39853
39848
|
function SelectCommitRanges() {
|
|
39854
39849
|
const commit_range = Store.useState((state) => state.commit_range);
|
|
@@ -39860,6 +39855,8 @@ function SelectCommitRanges() {
|
|
|
39860
39855
|
function SelectCommitRangesInternal(props) {
|
|
39861
39856
|
const actions = Store.useActions();
|
|
39862
39857
|
const argv = Store.useState((state) => state.argv);
|
|
39858
|
+
const branch_name = Store.useState((state) => state.branch_name);
|
|
39859
|
+
invariant(branch_name, "branch_name must exist");
|
|
39863
39860
|
const [selected_group_id, set_selected_group_id] = React42.useState(() => {
|
|
39864
39861
|
const first_group = props.commit_range.group_list.find((g2) => g2.id !== props.commit_range.UNASSIGNED);
|
|
39865
39862
|
if (first_group) {
|
|
@@ -40155,13 +40152,7 @@ function SelectCommitRangesInternal(props) {
|
|
|
40155
40152
|
}
|
|
40156
40153
|
})));
|
|
40157
40154
|
function get_group_id() {
|
|
40158
|
-
|
|
40159
|
-
if (argv["branch-prefix"]) {
|
|
40160
|
-
branch_prefix = argv["branch-prefix"];
|
|
40161
|
-
} else if (process.env.GIT_STACK_BRANCH_PREFIX) {
|
|
40162
|
-
branch_prefix = process.env.GIT_STACK_BRANCH_PREFIX;
|
|
40163
|
-
}
|
|
40164
|
-
return `${branch_prefix}${gs_short_id()}`;
|
|
40155
|
+
return `${branch_name}-${short_id()}`;
|
|
40165
40156
|
}
|
|
40166
40157
|
function submit_group_input(title) {
|
|
40167
40158
|
const id = get_group_id();
|
|
@@ -40674,260 +40665,8 @@ async function run11() {
|
|
|
40674
40665
|
}
|
|
40675
40666
|
}
|
|
40676
40667
|
|
|
40677
|
-
// src/commands/
|
|
40668
|
+
// src/commands/Config.tsx
|
|
40678
40669
|
var React50 = __toESM(require_react(), 1);
|
|
40679
|
-
function Fixup() {
|
|
40680
|
-
return /* @__PURE__ */ React50.createElement(Await, {
|
|
40681
|
-
fallback: null,
|
|
40682
|
-
function: run12
|
|
40683
|
-
});
|
|
40684
|
-
}
|
|
40685
|
-
async function run12() {
|
|
40686
|
-
const state = Store.getState();
|
|
40687
|
-
const actions = state.actions;
|
|
40688
|
-
const argv = state.argv;
|
|
40689
|
-
const relative_number = argv.commit;
|
|
40690
|
-
if (!relative_number) {
|
|
40691
|
-
actions.output(/* @__PURE__ */ React50.createElement(Text, {
|
|
40692
|
-
color: colors.red
|
|
40693
|
-
}, "❗️ Usage: git fixup ", "<relative-commit-number>"));
|
|
40694
|
-
actions.output("");
|
|
40695
|
-
actions.output("This script automates the process of adding staged changes as a fixup commit");
|
|
40696
|
-
actions.output("and the subsequent git rebase to flatten the commits based on relative commit number");
|
|
40697
|
-
actions.output("You can use a `git log` like below to get the relative commit number");
|
|
40698
|
-
actions.output("");
|
|
40699
|
-
actions.output(" ❯ git stack log");
|
|
40700
|
-
actions.output(" 1\te329794d5f881cbf0fc3f26d2108cf6f3fdebabe enable drop_error_subtask test param");
|
|
40701
|
-
actions.output(" 2\t57f43b596e5c6b97bc47e2a591f82ccc81651156 test drop_error_subtask baseline");
|
|
40702
|
-
actions.output(" 3\t838e878d483c6a2d5393063fc59baf2407225c6d ErrorSubtask test baseline");
|
|
40703
|
-
actions.output("");
|
|
40704
|
-
actions.output("To target `838e87` above, you would call `fixup 3`");
|
|
40705
|
-
actions.exit(0);
|
|
40706
|
-
}
|
|
40707
|
-
const diff_staged_cmd = await cli("git diff --cached --quiet", {
|
|
40708
|
-
ignoreExitCode: true
|
|
40709
|
-
});
|
|
40710
|
-
if (!diff_staged_cmd.code) {
|
|
40711
|
-
actions.error("\uD83D\uDEA8 Stage changes before calling fixup");
|
|
40712
|
-
actions.exit(1);
|
|
40713
|
-
}
|
|
40714
|
-
const adjusted_number = Number(relative_number) - 1;
|
|
40715
|
-
const commit_sha = (await cli(`git rev-parse HEAD~${adjusted_number}`)).stdout;
|
|
40716
|
-
actions.output(/* @__PURE__ */ React50.createElement(FormatText, {
|
|
40717
|
-
wrapper: /* @__PURE__ */ React50.createElement(Text, {
|
|
40718
|
-
color: colors.yellow
|
|
40719
|
-
}),
|
|
40720
|
-
message: "\uD83D\uDEE0️ fixup {relative_number} {commit_sha}",
|
|
40721
|
-
values: {
|
|
40722
|
-
commit_sha: /* @__PURE__ */ React50.createElement(Parens, null, commit_sha),
|
|
40723
|
-
relative_number
|
|
40724
|
-
}
|
|
40725
|
-
}));
|
|
40726
|
-
await cli(`git commit --fixup ${commit_sha}`);
|
|
40727
|
-
let save_stash = false;
|
|
40728
|
-
const diff_cmd = await cli("git diff-index --quiet HEAD --", {
|
|
40729
|
-
ignoreExitCode: true
|
|
40730
|
-
});
|
|
40731
|
-
if (diff_cmd.code) {
|
|
40732
|
-
save_stash = true;
|
|
40733
|
-
await cli("git stash --include-untracked");
|
|
40734
|
-
actions.output(/* @__PURE__ */ React50.createElement(Text, {
|
|
40735
|
-
color: colors.yellow
|
|
40736
|
-
}, /* @__PURE__ */ React50.createElement(FormatText, {
|
|
40737
|
-
message: "\uD83D\uDCE6 Changes saved to stash"
|
|
40738
|
-
})));
|
|
40739
|
-
}
|
|
40740
|
-
try {
|
|
40741
|
-
const rebase_target = Number(relative_number) + 1;
|
|
40742
|
-
await cli(`git rebase -i --autosquash HEAD~${rebase_target}`, {
|
|
40743
|
-
env: {
|
|
40744
|
-
...process.env,
|
|
40745
|
-
GIT_EDITOR: "true"
|
|
40746
|
-
}
|
|
40747
|
-
});
|
|
40748
|
-
} catch (error) {
|
|
40749
|
-
actions.error("\uD83D\uDEA8 Fixup failed");
|
|
40750
|
-
await cli("git rebase --abort");
|
|
40751
|
-
await cli("git reset --soft HEAD~1");
|
|
40752
|
-
} finally {
|
|
40753
|
-
if (save_stash) {
|
|
40754
|
-
await cli("git stash pop");
|
|
40755
|
-
actions.output(/* @__PURE__ */ React50.createElement(Text, {
|
|
40756
|
-
color: colors.green
|
|
40757
|
-
}, "✅ Changes restored from stash"));
|
|
40758
|
-
}
|
|
40759
|
-
}
|
|
40760
|
-
actions.exit(0);
|
|
40761
|
-
}
|
|
40762
|
-
|
|
40763
|
-
// src/commands/Log.tsx
|
|
40764
|
-
var React51 = __toESM(require_react(), 1);
|
|
40765
|
-
function Log() {
|
|
40766
|
-
const { stdout } = use_stdout_default();
|
|
40767
|
-
const available_width = stdout.columns || 80;
|
|
40768
|
-
return /* @__PURE__ */ React51.createElement(Await, {
|
|
40769
|
-
fallback: null,
|
|
40770
|
-
function: () => run13({ available_width })
|
|
40771
|
-
});
|
|
40772
|
-
}
|
|
40773
|
-
async function run13(args) {
|
|
40774
|
-
const state = Store.getState();
|
|
40775
|
-
const actions = state.actions;
|
|
40776
|
-
const process_argv = state.process_argv;
|
|
40777
|
-
invariant(actions, "actions must exist");
|
|
40778
|
-
const color_buffer = 12 * 5;
|
|
40779
|
-
const truncation_width = args.available_width + color_buffer;
|
|
40780
|
-
const short_sha = (await cli(`git log -1 --format=%h`)).stdout.trim();
|
|
40781
|
-
const short_sha_length = short_sha.length + 1;
|
|
40782
|
-
const sha_format = `%C(green)%<(${short_sha_length},trunc)%h`;
|
|
40783
|
-
const date_format = `%C(white)%<(15,trunc)%cr`;
|
|
40784
|
-
const author_format = `%C(white)%<(8,trunc)%al`;
|
|
40785
|
-
const decoration_format = `%C(auto)%d`;
|
|
40786
|
-
const subject_format = `%<(60,trunc)%s`;
|
|
40787
|
-
const format = [sha_format, date_format, author_format, decoration_format, subject_format].join(" ");
|
|
40788
|
-
const rest_args = process_argv.slice(3).join(" ");
|
|
40789
|
-
const command = [
|
|
40790
|
-
`git log --pretty=format:"${format}" -n20 --graph --color ${rest_args}`,
|
|
40791
|
-
`cut -c 1-"${truncation_width}"`,
|
|
40792
|
-
`nl -w3 -s' '`
|
|
40793
|
-
].join(" | ");
|
|
40794
|
-
const result = await cli(command);
|
|
40795
|
-
actions.output(result.stdout);
|
|
40796
|
-
actions.exit(0);
|
|
40797
|
-
}
|
|
40798
|
-
|
|
40799
|
-
// src/commands/Update.tsx
|
|
40800
|
-
var React52 = __toESM(require_react(), 1);
|
|
40801
|
-
function Update() {
|
|
40802
|
-
return /* @__PURE__ */ React52.createElement(Await, {
|
|
40803
|
-
fallback: null,
|
|
40804
|
-
function: run14
|
|
40805
|
-
});
|
|
40806
|
-
}
|
|
40807
|
-
async function run14() {
|
|
40808
|
-
const state = Store.getState();
|
|
40809
|
-
const actions = state.actions;
|
|
40810
|
-
actions.exit(0);
|
|
40811
|
-
}
|
|
40812
|
-
|
|
40813
|
-
// src/components/ErrorBoundary.tsx
|
|
40814
|
-
var React53 = __toESM(require_react(), 1);
|
|
40815
|
-
class ErrorBoundary extends React53.Component {
|
|
40816
|
-
constructor(props) {
|
|
40817
|
-
super(props);
|
|
40818
|
-
this.state = {
|
|
40819
|
-
error: null,
|
|
40820
|
-
component_stack: ""
|
|
40821
|
-
};
|
|
40822
|
-
}
|
|
40823
|
-
static getDerivedStateFromError(error) {
|
|
40824
|
-
return { error };
|
|
40825
|
-
}
|
|
40826
|
-
componentDidCatch(_error, error_info) {
|
|
40827
|
-
let component_stack = error_info.componentStack;
|
|
40828
|
-
if (component_stack) {
|
|
40829
|
-
component_stack = component_stack.split(`
|
|
40830
|
-
`).slice(1).join(`
|
|
40831
|
-
`);
|
|
40832
|
-
this.setState({ component_stack }, async () => {
|
|
40833
|
-
await Exit.handle_exit({ code: 30, clear: true });
|
|
40834
|
-
});
|
|
40835
|
-
}
|
|
40836
|
-
}
|
|
40837
|
-
render() {
|
|
40838
|
-
if (!this.state.error) {
|
|
40839
|
-
return this.props.children;
|
|
40840
|
-
}
|
|
40841
|
-
const message = this.state.error.message;
|
|
40842
|
-
return /* @__PURE__ */ React53.createElement(Box_default, {
|
|
40843
|
-
flexDirection: "column",
|
|
40844
|
-
gap: 0
|
|
40845
|
-
}, /* @__PURE__ */ React53.createElement(Text, {
|
|
40846
|
-
color: colors.red
|
|
40847
|
-
}, /* @__PURE__ */ React53.createElement(FormatText, {
|
|
40848
|
-
message: "\uD83D\uDEA8 Unhandled error {message}",
|
|
40849
|
-
values: {
|
|
40850
|
-
message: /* @__PURE__ */ React53.createElement(Text, {
|
|
40851
|
-
color: colors.gray
|
|
40852
|
-
}, message)
|
|
40853
|
-
}
|
|
40854
|
-
})), this._render_verbose());
|
|
40855
|
-
}
|
|
40856
|
-
_render_verbose() {
|
|
40857
|
-
const store_state = Store.getState();
|
|
40858
|
-
if (store_state.argv.verbose) {
|
|
40859
|
-
return /* @__PURE__ */ React53.createElement(Text, {
|
|
40860
|
-
color: colors.gray
|
|
40861
|
-
}, this.state.component_stack);
|
|
40862
|
-
}
|
|
40863
|
-
return /* @__PURE__ */ React53.createElement(Text, {
|
|
40864
|
-
color: colors.gray
|
|
40865
|
-
}, /* @__PURE__ */ React53.createElement(FormatText, {
|
|
40866
|
-
message: "Try again with `--verbose` to see more information."
|
|
40867
|
-
}));
|
|
40868
|
-
}
|
|
40869
|
-
}
|
|
40870
|
-
|
|
40871
|
-
// src/components/ExitingGate.tsx
|
|
40872
|
-
var React54 = __toESM(require_react(), 1);
|
|
40873
|
-
function ExitingGate(props) {
|
|
40874
|
-
const exit_mode = Store.useState((state) => state.exit_mode);
|
|
40875
|
-
if (!exit_mode) {
|
|
40876
|
-
return props.children;
|
|
40877
|
-
}
|
|
40878
|
-
switch (exit_mode) {
|
|
40879
|
-
case "quiet":
|
|
40880
|
-
return null;
|
|
40881
|
-
case "normal":
|
|
40882
|
-
return /* @__PURE__ */ React54.createElement(Box_default, {
|
|
40883
|
-
flexDirection: "column"
|
|
40884
|
-
}, /* @__PURE__ */ React54.createElement(Text, {
|
|
40885
|
-
color: colors.red
|
|
40886
|
-
}, /* @__PURE__ */ React54.createElement(FormatText, {
|
|
40887
|
-
message: "\uD83D\uDEA8 Exiting…"
|
|
40888
|
-
})));
|
|
40889
|
-
default:
|
|
40890
|
-
return null;
|
|
40891
|
-
}
|
|
40892
|
-
}
|
|
40893
|
-
|
|
40894
|
-
// src/app/App.tsx
|
|
40895
|
-
function App2() {
|
|
40896
|
-
const actions = Store.useActions();
|
|
40897
|
-
const ink = Store.useState((state) => state.ink);
|
|
40898
|
-
const argv = Store.useState((state) => state.argv);
|
|
40899
|
-
if (!ink || !argv || !argv.$0) {
|
|
40900
|
-
return null;
|
|
40901
|
-
}
|
|
40902
|
-
const positional_list = new Set(argv["_"]);
|
|
40903
|
-
const is_update = positional_list.has("update") || positional_list.has("upgrade");
|
|
40904
|
-
return /* @__PURE__ */ React55.createElement(Providers, null, /* @__PURE__ */ React55.createElement(ErrorBoundary, null, /* @__PURE__ */ React55.createElement(Debug, null), /* @__PURE__ */ React55.createElement(Output2, null), /* @__PURE__ */ React55.createElement(ExitingGate, null, /* @__PURE__ */ React55.createElement(AutoUpdate, {
|
|
40905
|
-
name: "git-stack-cli",
|
|
40906
|
-
verbose: argv.verbose,
|
|
40907
|
-
force: is_update,
|
|
40908
|
-
timeoutMs: is_update ? 30 * 1000 : 2 * 1000,
|
|
40909
|
-
onOutput: actions.output,
|
|
40910
|
-
onDone: () => {
|
|
40911
|
-
if (is_update) {
|
|
40912
|
-
actions.exit(0);
|
|
40913
|
-
}
|
|
40914
|
-
}
|
|
40915
|
-
}, /* @__PURE__ */ React55.createElement(VerboseDebugInfo, null, /* @__PURE__ */ React55.createElement(RebaseCheck, null, /* @__PURE__ */ React55.createElement(CherryPickCheck, null, /* @__PURE__ */ React55.createElement(MaybeMain, null))))), /* @__PURE__ */ React55.createElement(HandleCtrlCSigint, null))));
|
|
40916
|
-
}
|
|
40917
|
-
function MaybeMain() {
|
|
40918
|
-
const argv = Store.useState((state) => state.argv);
|
|
40919
|
-
const positional_list = new Set(argv["_"]);
|
|
40920
|
-
if (positional_list.has("fixup")) {
|
|
40921
|
-
return /* @__PURE__ */ React55.createElement(Fixup, null);
|
|
40922
|
-
} else if (positional_list.has("log")) {
|
|
40923
|
-
return /* @__PURE__ */ React55.createElement(Log, null);
|
|
40924
|
-
} else if (positional_list.has("update")) {
|
|
40925
|
-
return /* @__PURE__ */ React55.createElement(Update, null);
|
|
40926
|
-
} else if (positional_list.has("rebase")) {
|
|
40927
|
-
return /* @__PURE__ */ React55.createElement(DependencyCheck, null, /* @__PURE__ */ React55.createElement(DirtyCheck, null, /* @__PURE__ */ React55.createElement(GatherMetadata, null, /* @__PURE__ */ React55.createElement(LocalCommitStatus, null, /* @__PURE__ */ React55.createElement(Rebase, null)))));
|
|
40928
|
-
}
|
|
40929
|
-
return /* @__PURE__ */ React55.createElement(React55.Fragment, null, !argv.verbose ? null : /* @__PURE__ */ React55.createElement(GithubApiError, null), /* @__PURE__ */ React55.createElement(DependencyCheck, null, /* @__PURE__ */ React55.createElement(DirtyCheck, null, /* @__PURE__ */ React55.createElement(GatherMetadata, null, /* @__PURE__ */ React55.createElement(RequireBranch, null, /* @__PURE__ */ React55.createElement(LocalCommitStatus, null, /* @__PURE__ */ React55.createElement(DetectInitialPR, null, /* @__PURE__ */ React55.createElement(Main, null))))))));
|
|
40930
|
-
}
|
|
40931
40670
|
|
|
40932
40671
|
// node_modules/.pnpm/yargs@17.7.2/node_modules/yargs/lib/platform-shims/esm.mjs
|
|
40933
40672
|
import { notStrictEqual, strictEqual } from "assert";
|
|
@@ -45790,8 +45529,17 @@ var Yargs = YargsFactory(esm_default);
|
|
|
45790
45529
|
var yargs_default = Yargs;
|
|
45791
45530
|
|
|
45792
45531
|
// src/command.ts
|
|
45793
|
-
async function command2() {
|
|
45794
|
-
|
|
45532
|
+
async function command2(argv, options = {}) {
|
|
45533
|
+
let builder = yargs_default(hideBin(argv));
|
|
45534
|
+
if (options.parserConfiguration) {
|
|
45535
|
+
builder = builder.parserConfiguration(options.parserConfiguration);
|
|
45536
|
+
}
|
|
45537
|
+
if (options.env_config) {
|
|
45538
|
+
builder = builder.config(options.env_config);
|
|
45539
|
+
}
|
|
45540
|
+
const parsed = await builder.scriptName("git stack").usage("Usage: git stack [command] [options]").command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions)).command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) => yargs.positional("commit", FixupOptions.commit)).command("log [args...]", "Print an abbreviated log with numbered commits, useful for git stack fixup", (yargs) => yargs.strict(false)).command("rebase", "Update local branch via rebase with latest changes from origin master branch", (yargs) => yargs).command(["update", "upgrade"], "Check and install the latest version of git stack", (yargs) => yargs).command("config", "Generate a one-time configuration json based on the passed arguments", (yargs) => yargs.options(DefaultOptions)).option("verbose", GlobalOptions.verbose).wrap(123).strict().version("2.7.0").showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`").help("help", "Show usage via `git stack help`");
|
|
45541
|
+
const result = parsed.argv;
|
|
45542
|
+
return result;
|
|
45795
45543
|
}
|
|
45796
45544
|
var GlobalOptions = {
|
|
45797
45545
|
verbose: {
|
|
@@ -45844,11 +45592,6 @@ var DefaultOptions = {
|
|
|
45844
45592
|
default: false,
|
|
45845
45593
|
description: "Open all PRs as drafts"
|
|
45846
45594
|
},
|
|
45847
|
-
"branch-prefix": {
|
|
45848
|
-
type: "string",
|
|
45849
|
-
default: "",
|
|
45850
|
-
description: "Prefix for generated branch names, e.g. dev/magus/"
|
|
45851
|
-
},
|
|
45852
45595
|
"revise-sign": {
|
|
45853
45596
|
type: "boolean",
|
|
45854
45597
|
default: true,
|
|
@@ -45888,6 +45631,353 @@ var FixupOptions = {
|
|
|
45888
45631
|
}
|
|
45889
45632
|
};
|
|
45890
45633
|
|
|
45634
|
+
// src/commands/Config.tsx
|
|
45635
|
+
function Config() {
|
|
45636
|
+
return /* @__PURE__ */ React50.createElement(Await, {
|
|
45637
|
+
fallback: null,
|
|
45638
|
+
function: run12
|
|
45639
|
+
});
|
|
45640
|
+
async function run12() {
|
|
45641
|
+
const state = Store.getState();
|
|
45642
|
+
const actions = state.actions;
|
|
45643
|
+
const config = await get_explicit_args();
|
|
45644
|
+
const config_json = JSON.stringify(config).replace(/"/g, "\\\"");
|
|
45645
|
+
actions.output(/* @__PURE__ */ React50.createElement(Box_default, {
|
|
45646
|
+
flexDirection: "column",
|
|
45647
|
+
gap: 1,
|
|
45648
|
+
paddingTop: 1
|
|
45649
|
+
}, /* @__PURE__ */ React50.createElement(Text, null), /* @__PURE__ */ React50.createElement(FormatText, {
|
|
45650
|
+
message: "Add the line below to your shell rc file ({zshrc}, {bashrc}, etc.)",
|
|
45651
|
+
values: {
|
|
45652
|
+
zshrc: /* @__PURE__ */ React50.createElement(Text, {
|
|
45653
|
+
color: colors.gray
|
|
45654
|
+
}, ".zshrc"),
|
|
45655
|
+
bashrc: /* @__PURE__ */ React50.createElement(Text, {
|
|
45656
|
+
color: colors.gray
|
|
45657
|
+
}, ".bashrc")
|
|
45658
|
+
}
|
|
45659
|
+
}), /* @__PURE__ */ React50.createElement(FormatText, {
|
|
45660
|
+
message: `{export} {ENV_VAR}{e}{q}{config_json}{q}`,
|
|
45661
|
+
values: {
|
|
45662
|
+
export: /* @__PURE__ */ React50.createElement(Text, {
|
|
45663
|
+
color: colors.purple
|
|
45664
|
+
}, "export"),
|
|
45665
|
+
ENV_VAR: /* @__PURE__ */ React50.createElement(Text, {
|
|
45666
|
+
color: colors.yellow
|
|
45667
|
+
}, ENV_VAR),
|
|
45668
|
+
e: /* @__PURE__ */ React50.createElement(Text, {
|
|
45669
|
+
color: colors.purple
|
|
45670
|
+
}, "="),
|
|
45671
|
+
q: /* @__PURE__ */ React50.createElement(Text, {
|
|
45672
|
+
color: colors.white
|
|
45673
|
+
}, '"'),
|
|
45674
|
+
config_json: /* @__PURE__ */ React50.createElement(Text, {
|
|
45675
|
+
color: colors.green
|
|
45676
|
+
}, config_json)
|
|
45677
|
+
}
|
|
45678
|
+
})));
|
|
45679
|
+
actions.exit(0);
|
|
45680
|
+
}
|
|
45681
|
+
}
|
|
45682
|
+
async function argv_with_config_from_env() {
|
|
45683
|
+
if (!process.env.GIT_STACK_CONFIG) {
|
|
45684
|
+
return await command2(process.argv);
|
|
45685
|
+
}
|
|
45686
|
+
const env_config = parse_env_config();
|
|
45687
|
+
return await command2(process.argv, { env_config });
|
|
45688
|
+
}
|
|
45689
|
+
function parse_env_config() {
|
|
45690
|
+
const GIT_STACK_CONFIG = process.env.GIT_STACK_CONFIG;
|
|
45691
|
+
invariant(GIT_STACK_CONFIG, "GIT_STACK_CONFIG must exist");
|
|
45692
|
+
try {
|
|
45693
|
+
const env_config = JSON.parse(GIT_STACK_CONFIG);
|
|
45694
|
+
return env_config;
|
|
45695
|
+
} catch (error) {
|
|
45696
|
+
console.error(`ERROR GIT_STACK_CONFIG=${GIT_STACK_CONFIG}`);
|
|
45697
|
+
console.error("ERROR GIT_STACK_CONFIG environment variable is not valid JSON");
|
|
45698
|
+
process.exit(18);
|
|
45699
|
+
}
|
|
45700
|
+
}
|
|
45701
|
+
async function get_explicit_args() {
|
|
45702
|
+
const default_argv = await command2(["git", "stack"], COMMAND_OPTIONS);
|
|
45703
|
+
const state_argv = await command2(process.argv, COMMAND_OPTIONS);
|
|
45704
|
+
const config = {};
|
|
45705
|
+
for (const key of Object.keys(state_argv)) {
|
|
45706
|
+
if (key === "_" || key === "$0")
|
|
45707
|
+
continue;
|
|
45708
|
+
const state_value = state_argv[key];
|
|
45709
|
+
const default_value = default_argv[key];
|
|
45710
|
+
const is_set = default_value !== state_value;
|
|
45711
|
+
if (is_set) {
|
|
45712
|
+
config[key] = state_value;
|
|
45713
|
+
}
|
|
45714
|
+
}
|
|
45715
|
+
return config;
|
|
45716
|
+
}
|
|
45717
|
+
var ENV_VAR = "GIT_STACK_CONFIG";
|
|
45718
|
+
var COMMAND_OPTIONS = {
|
|
45719
|
+
parserConfiguration: {
|
|
45720
|
+
"strip-aliased": true
|
|
45721
|
+
}
|
|
45722
|
+
};
|
|
45723
|
+
|
|
45724
|
+
// src/commands/Fixup.tsx
|
|
45725
|
+
var React51 = __toESM(require_react(), 1);
|
|
45726
|
+
function Fixup() {
|
|
45727
|
+
return /* @__PURE__ */ React51.createElement(Await, {
|
|
45728
|
+
fallback: null,
|
|
45729
|
+
function: run12
|
|
45730
|
+
});
|
|
45731
|
+
}
|
|
45732
|
+
async function run12() {
|
|
45733
|
+
const state = Store.getState();
|
|
45734
|
+
const actions = state.actions;
|
|
45735
|
+
const argv = state.argv;
|
|
45736
|
+
const relative_number = argv.commit;
|
|
45737
|
+
if (!relative_number) {
|
|
45738
|
+
actions.output(/* @__PURE__ */ React51.createElement(Text, {
|
|
45739
|
+
color: colors.red
|
|
45740
|
+
}, "❗️ Usage: git fixup ", "<relative-commit-number>"));
|
|
45741
|
+
actions.output("");
|
|
45742
|
+
actions.output("This script automates the process of adding staged changes as a fixup commit");
|
|
45743
|
+
actions.output("and the subsequent git rebase to flatten the commits based on relative commit number");
|
|
45744
|
+
actions.output("You can use a `git log` like below to get the relative commit number");
|
|
45745
|
+
actions.output("");
|
|
45746
|
+
actions.output(" ❯ git stack log");
|
|
45747
|
+
actions.output(" 1\te329794d5f881cbf0fc3f26d2108cf6f3fdebabe enable drop_error_subtask test param");
|
|
45748
|
+
actions.output(" 2\t57f43b596e5c6b97bc47e2a591f82ccc81651156 test drop_error_subtask baseline");
|
|
45749
|
+
actions.output(" 3\t838e878d483c6a2d5393063fc59baf2407225c6d ErrorSubtask test baseline");
|
|
45750
|
+
actions.output("");
|
|
45751
|
+
actions.output("To target `838e87` above, you would call `fixup 3`");
|
|
45752
|
+
actions.exit(0);
|
|
45753
|
+
}
|
|
45754
|
+
const diff_staged_cmd = await cli("git diff --cached --quiet", {
|
|
45755
|
+
ignoreExitCode: true
|
|
45756
|
+
});
|
|
45757
|
+
if (!diff_staged_cmd.code) {
|
|
45758
|
+
actions.error("\uD83D\uDEA8 Stage changes before calling fixup");
|
|
45759
|
+
actions.exit(1);
|
|
45760
|
+
}
|
|
45761
|
+
const adjusted_number = Number(relative_number) - 1;
|
|
45762
|
+
const commit_sha = (await cli(`git rev-parse HEAD~${adjusted_number}`)).stdout;
|
|
45763
|
+
actions.output(/* @__PURE__ */ React51.createElement(FormatText, {
|
|
45764
|
+
wrapper: /* @__PURE__ */ React51.createElement(Text, {
|
|
45765
|
+
color: colors.yellow
|
|
45766
|
+
}),
|
|
45767
|
+
message: "\uD83D\uDEE0️ fixup {relative_number} {commit_sha}",
|
|
45768
|
+
values: {
|
|
45769
|
+
commit_sha: /* @__PURE__ */ React51.createElement(Parens, null, commit_sha),
|
|
45770
|
+
relative_number
|
|
45771
|
+
}
|
|
45772
|
+
}));
|
|
45773
|
+
await cli(`git commit --fixup ${commit_sha}`);
|
|
45774
|
+
let save_stash = false;
|
|
45775
|
+
const diff_cmd = await cli("git diff-index --quiet HEAD --", {
|
|
45776
|
+
ignoreExitCode: true
|
|
45777
|
+
});
|
|
45778
|
+
if (diff_cmd.code) {
|
|
45779
|
+
save_stash = true;
|
|
45780
|
+
await cli("git stash --include-untracked");
|
|
45781
|
+
actions.output(/* @__PURE__ */ React51.createElement(Text, {
|
|
45782
|
+
color: colors.yellow
|
|
45783
|
+
}, /* @__PURE__ */ React51.createElement(FormatText, {
|
|
45784
|
+
message: "\uD83D\uDCE6 Changes saved to stash"
|
|
45785
|
+
})));
|
|
45786
|
+
}
|
|
45787
|
+
try {
|
|
45788
|
+
const rebase_target = Number(relative_number) + 1;
|
|
45789
|
+
await cli(`git rebase -i --autosquash HEAD~${rebase_target}`, {
|
|
45790
|
+
env: {
|
|
45791
|
+
...process.env,
|
|
45792
|
+
GIT_EDITOR: "true"
|
|
45793
|
+
}
|
|
45794
|
+
});
|
|
45795
|
+
} catch (error) {
|
|
45796
|
+
actions.error("\uD83D\uDEA8 Fixup failed");
|
|
45797
|
+
await cli("git rebase --abort");
|
|
45798
|
+
await cli("git reset --soft HEAD~1");
|
|
45799
|
+
} finally {
|
|
45800
|
+
if (save_stash) {
|
|
45801
|
+
await cli("git stash pop");
|
|
45802
|
+
actions.output(/* @__PURE__ */ React51.createElement(Text, {
|
|
45803
|
+
color: colors.green
|
|
45804
|
+
}, "✅ Changes restored from stash"));
|
|
45805
|
+
}
|
|
45806
|
+
}
|
|
45807
|
+
actions.exit(0);
|
|
45808
|
+
}
|
|
45809
|
+
|
|
45810
|
+
// src/commands/Log.tsx
|
|
45811
|
+
var React52 = __toESM(require_react(), 1);
|
|
45812
|
+
function Log() {
|
|
45813
|
+
const { stdout } = use_stdout_default();
|
|
45814
|
+
const available_width = stdout.columns || 80;
|
|
45815
|
+
return /* @__PURE__ */ React52.createElement(Await, {
|
|
45816
|
+
fallback: null,
|
|
45817
|
+
function: () => run13({ available_width })
|
|
45818
|
+
});
|
|
45819
|
+
}
|
|
45820
|
+
async function run13(args) {
|
|
45821
|
+
const state = Store.getState();
|
|
45822
|
+
const actions = state.actions;
|
|
45823
|
+
const process_argv = state.process_argv;
|
|
45824
|
+
invariant(actions, "actions must exist");
|
|
45825
|
+
const color_buffer = 12 * 5;
|
|
45826
|
+
const truncation_width = args.available_width + color_buffer;
|
|
45827
|
+
const short_sha = (await cli(`git log -1 --format=%h`)).stdout.trim();
|
|
45828
|
+
const short_sha_length = short_sha.length + 1;
|
|
45829
|
+
const sha_format = `%C(green)%<(${short_sha_length},trunc)%h`;
|
|
45830
|
+
const date_format = `%C(white)%<(15,trunc)%cr`;
|
|
45831
|
+
const author_format = `%C(white)%<(8,trunc)%al`;
|
|
45832
|
+
const decoration_format = `%C(auto)%d`;
|
|
45833
|
+
const subject_format = `%<(60,trunc)%s`;
|
|
45834
|
+
const format3 = [sha_format, date_format, author_format, decoration_format, subject_format].join(" ");
|
|
45835
|
+
const rest_args = process_argv.slice(3).join(" ");
|
|
45836
|
+
const command3 = [
|
|
45837
|
+
`git log --pretty=format:"${format3}" -n20 --graph --color ${rest_args}`,
|
|
45838
|
+
`cut -c 1-"${truncation_width}"`,
|
|
45839
|
+
`nl -w3 -s' '`
|
|
45840
|
+
].join(" | ");
|
|
45841
|
+
const result = await cli(command3);
|
|
45842
|
+
actions.output(result.stdout);
|
|
45843
|
+
actions.exit(0);
|
|
45844
|
+
}
|
|
45845
|
+
|
|
45846
|
+
// src/commands/Update.tsx
|
|
45847
|
+
var React53 = __toESM(require_react(), 1);
|
|
45848
|
+
function Update() {
|
|
45849
|
+
return /* @__PURE__ */ React53.createElement(Await, {
|
|
45850
|
+
fallback: null,
|
|
45851
|
+
function: run14
|
|
45852
|
+
});
|
|
45853
|
+
}
|
|
45854
|
+
async function run14() {
|
|
45855
|
+
const state = Store.getState();
|
|
45856
|
+
const actions = state.actions;
|
|
45857
|
+
actions.exit(0);
|
|
45858
|
+
}
|
|
45859
|
+
|
|
45860
|
+
// src/components/ErrorBoundary.tsx
|
|
45861
|
+
var React54 = __toESM(require_react(), 1);
|
|
45862
|
+
class ErrorBoundary extends React54.Component {
|
|
45863
|
+
constructor(props) {
|
|
45864
|
+
super(props);
|
|
45865
|
+
this.state = {
|
|
45866
|
+
error: null,
|
|
45867
|
+
component_stack: ""
|
|
45868
|
+
};
|
|
45869
|
+
}
|
|
45870
|
+
static getDerivedStateFromError(error) {
|
|
45871
|
+
return { error };
|
|
45872
|
+
}
|
|
45873
|
+
componentDidCatch(_error, error_info) {
|
|
45874
|
+
let component_stack = error_info.componentStack;
|
|
45875
|
+
if (component_stack) {
|
|
45876
|
+
component_stack = component_stack.split(`
|
|
45877
|
+
`).slice(1).join(`
|
|
45878
|
+
`);
|
|
45879
|
+
this.setState({ component_stack }, async () => {
|
|
45880
|
+
await Exit.handle_exit({ code: 30, clear: true });
|
|
45881
|
+
});
|
|
45882
|
+
}
|
|
45883
|
+
}
|
|
45884
|
+
render() {
|
|
45885
|
+
if (!this.state.error) {
|
|
45886
|
+
return this.props.children;
|
|
45887
|
+
}
|
|
45888
|
+
const message = this.state.error.message;
|
|
45889
|
+
return /* @__PURE__ */ React54.createElement(Box_default, {
|
|
45890
|
+
flexDirection: "column",
|
|
45891
|
+
gap: 0
|
|
45892
|
+
}, /* @__PURE__ */ React54.createElement(Text, {
|
|
45893
|
+
color: colors.red
|
|
45894
|
+
}, /* @__PURE__ */ React54.createElement(FormatText, {
|
|
45895
|
+
message: "\uD83D\uDEA8 Unhandled error {message}",
|
|
45896
|
+
values: {
|
|
45897
|
+
message: /* @__PURE__ */ React54.createElement(Text, {
|
|
45898
|
+
color: colors.gray
|
|
45899
|
+
}, message)
|
|
45900
|
+
}
|
|
45901
|
+
})), this._render_verbose());
|
|
45902
|
+
}
|
|
45903
|
+
_render_verbose() {
|
|
45904
|
+
const store_state = Store.getState();
|
|
45905
|
+
if (store_state.argv.verbose) {
|
|
45906
|
+
return /* @__PURE__ */ React54.createElement(Text, {
|
|
45907
|
+
color: colors.gray
|
|
45908
|
+
}, this.state.component_stack);
|
|
45909
|
+
}
|
|
45910
|
+
return /* @__PURE__ */ React54.createElement(Text, {
|
|
45911
|
+
color: colors.gray
|
|
45912
|
+
}, /* @__PURE__ */ React54.createElement(FormatText, {
|
|
45913
|
+
message: "Try again with `--verbose` to see more information."
|
|
45914
|
+
}));
|
|
45915
|
+
}
|
|
45916
|
+
}
|
|
45917
|
+
|
|
45918
|
+
// src/components/ExitingGate.tsx
|
|
45919
|
+
var React55 = __toESM(require_react(), 1);
|
|
45920
|
+
function ExitingGate(props) {
|
|
45921
|
+
const exit_mode = Store.useState((state) => state.exit_mode);
|
|
45922
|
+
if (!exit_mode) {
|
|
45923
|
+
return props.children;
|
|
45924
|
+
}
|
|
45925
|
+
switch (exit_mode) {
|
|
45926
|
+
case "quiet":
|
|
45927
|
+
return null;
|
|
45928
|
+
case "normal":
|
|
45929
|
+
return /* @__PURE__ */ React55.createElement(Box_default, {
|
|
45930
|
+
flexDirection: "column"
|
|
45931
|
+
}, /* @__PURE__ */ React55.createElement(Text, {
|
|
45932
|
+
color: colors.red
|
|
45933
|
+
}, /* @__PURE__ */ React55.createElement(FormatText, {
|
|
45934
|
+
message: "\uD83D\uDEA8 Exiting…"
|
|
45935
|
+
})));
|
|
45936
|
+
default:
|
|
45937
|
+
return null;
|
|
45938
|
+
}
|
|
45939
|
+
}
|
|
45940
|
+
|
|
45941
|
+
// src/app/App.tsx
|
|
45942
|
+
function App2() {
|
|
45943
|
+
const actions = Store.useActions();
|
|
45944
|
+
const ink = Store.useState((state) => state.ink);
|
|
45945
|
+
const argv = Store.useState((state) => state.argv);
|
|
45946
|
+
if (!ink || !argv || !argv.$0) {
|
|
45947
|
+
return null;
|
|
45948
|
+
}
|
|
45949
|
+
const positional_list = new Set(argv["_"]);
|
|
45950
|
+
const is_update = positional_list.has("update") || positional_list.has("upgrade");
|
|
45951
|
+
return /* @__PURE__ */ React56.createElement(Providers, null, /* @__PURE__ */ React56.createElement(ErrorBoundary, null, /* @__PURE__ */ React56.createElement(Debug, null), /* @__PURE__ */ React56.createElement(Output2, null), /* @__PURE__ */ React56.createElement(ExitingGate, null, /* @__PURE__ */ React56.createElement(AutoUpdate, {
|
|
45952
|
+
name: "git-stack-cli",
|
|
45953
|
+
verbose: argv.verbose,
|
|
45954
|
+
force: is_update,
|
|
45955
|
+
timeoutMs: is_update ? 30 * 1000 : 2 * 1000,
|
|
45956
|
+
onOutput: actions.output,
|
|
45957
|
+
onDone: () => {
|
|
45958
|
+
if (is_update) {
|
|
45959
|
+
actions.exit(0);
|
|
45960
|
+
}
|
|
45961
|
+
}
|
|
45962
|
+
}, /* @__PURE__ */ React56.createElement(VerboseDebugInfo, null, /* @__PURE__ */ React56.createElement(RebaseCheck, null, /* @__PURE__ */ React56.createElement(CherryPickCheck, null, /* @__PURE__ */ React56.createElement(MaybeMain, null))))), /* @__PURE__ */ React56.createElement(HandleCtrlCSigint, null))));
|
|
45963
|
+
}
|
|
45964
|
+
function MaybeMain() {
|
|
45965
|
+
const argv = Store.useState((state) => state.argv);
|
|
45966
|
+
const positional_list = new Set(argv["_"]);
|
|
45967
|
+
if (positional_list.has("fixup")) {
|
|
45968
|
+
return /* @__PURE__ */ React56.createElement(Fixup, null);
|
|
45969
|
+
} else if (positional_list.has("log")) {
|
|
45970
|
+
return /* @__PURE__ */ React56.createElement(Log, null);
|
|
45971
|
+
} else if (positional_list.has("update")) {
|
|
45972
|
+
return /* @__PURE__ */ React56.createElement(Update, null);
|
|
45973
|
+
} else if (positional_list.has("config")) {
|
|
45974
|
+
return /* @__PURE__ */ React56.createElement(Config, null);
|
|
45975
|
+
} else if (positional_list.has("rebase")) {
|
|
45976
|
+
return /* @__PURE__ */ React56.createElement(DependencyCheck, null, /* @__PURE__ */ React56.createElement(DirtyCheck, null, /* @__PURE__ */ React56.createElement(GatherMetadata, null, /* @__PURE__ */ React56.createElement(LocalCommitStatus, null, /* @__PURE__ */ React56.createElement(Rebase, null)))));
|
|
45977
|
+
}
|
|
45978
|
+
return /* @__PURE__ */ React56.createElement(React56.Fragment, null, !argv.verbose ? null : /* @__PURE__ */ React56.createElement(GithubApiError, null), /* @__PURE__ */ React56.createElement(DependencyCheck, null, /* @__PURE__ */ React56.createElement(DirtyCheck, null, /* @__PURE__ */ React56.createElement(GatherMetadata, null, /* @__PURE__ */ React56.createElement(RequireBranch, null, /* @__PURE__ */ React56.createElement(LocalCommitStatus, null, /* @__PURE__ */ React56.createElement(DetectInitialPR, null, /* @__PURE__ */ React56.createElement(Main, null))))))));
|
|
45979
|
+
}
|
|
45980
|
+
|
|
45891
45981
|
// src/index.tsx
|
|
45892
45982
|
(async function main() {
|
|
45893
45983
|
try {
|
|
@@ -45897,7 +45987,7 @@ var FixupOptions = {
|
|
|
45897
45987
|
console.error("Try again with `--verbose` to see more information.");
|
|
45898
45988
|
}
|
|
45899
45989
|
};
|
|
45900
|
-
const argv = await
|
|
45990
|
+
const argv = await argv_with_config_from_env();
|
|
45901
45991
|
process.stdin.resume();
|
|
45902
45992
|
process.on("uncaughtException", (error) => {
|
|
45903
45993
|
console.error("\uD83D\uDEA8 uncaughtException");
|
|
@@ -45913,7 +46003,7 @@ var FixupOptions = {
|
|
|
45913
46003
|
});
|
|
45914
46004
|
const tmp_dir = await get_tmp_dir();
|
|
45915
46005
|
await fs12.rm(tmp_dir, { recursive: true });
|
|
45916
|
-
const ink = render_default(/* @__PURE__ */
|
|
46006
|
+
const ink = render_default(/* @__PURE__ */ React57.createElement(App2, null), {
|
|
45917
46007
|
exitOnCtrlC: false
|
|
45918
46008
|
});
|
|
45919
46009
|
Store.setState((state) => {
|
package/package.json
CHANGED
package/src/app/App.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import { RebaseCheck } from "~/app/RebaseCheck";
|
|
|
17
17
|
import { RequireBranch } from "~/app/RequireBranch";
|
|
18
18
|
import { Store } from "~/app/Store";
|
|
19
19
|
import { VerboseDebugInfo } from "~/app/VerboseDebugInfo";
|
|
20
|
+
import { Config } from "~/commands/Config";
|
|
20
21
|
import { Fixup } from "~/commands/Fixup";
|
|
21
22
|
import { Log } from "~/commands/Log";
|
|
22
23
|
import { Rebase } from "~/commands/Rebase";
|
|
@@ -92,6 +93,8 @@ function MaybeMain() {
|
|
|
92
93
|
return <Log />;
|
|
93
94
|
} else if (positional_list.has("update")) {
|
|
94
95
|
return <Update />;
|
|
96
|
+
} else if (positional_list.has("config")) {
|
|
97
|
+
return <Config />;
|
|
95
98
|
} else if (positional_list.has("rebase")) {
|
|
96
99
|
return (
|
|
97
100
|
<DependencyCheck>
|
|
@@ -10,8 +10,8 @@ import { Parens } from "~/app/Parens";
|
|
|
10
10
|
import { Store } from "~/app/Store";
|
|
11
11
|
import { TextInput } from "~/app/TextInput";
|
|
12
12
|
import { colors } from "~/core/colors";
|
|
13
|
-
import { gs_short_id } from "~/core/gs_short_id";
|
|
14
13
|
import { invariant } from "~/core/invariant";
|
|
14
|
+
import { short_id } from "~/core/short_id";
|
|
15
15
|
import { wrap_index } from "~/core/wrap_index";
|
|
16
16
|
|
|
17
17
|
import type { State } from "~/app/Store";
|
|
@@ -35,6 +35,8 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
35
35
|
const actions = Store.useActions();
|
|
36
36
|
|
|
37
37
|
const argv = Store.useState((state) => state.argv);
|
|
38
|
+
const branch_name = Store.useState((state) => state.branch_name);
|
|
39
|
+
invariant(branch_name, "branch_name must exist");
|
|
38
40
|
|
|
39
41
|
const [selected_group_id, set_selected_group_id] = React.useState(() => {
|
|
40
42
|
const first_group = props.commit_range.group_list.find(
|
|
@@ -444,17 +446,7 @@ function SelectCommitRangesInternal(props: Props) {
|
|
|
444
446
|
);
|
|
445
447
|
|
|
446
448
|
function get_group_id() {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
// branch prefix via cli flag or env var
|
|
450
|
-
// cli flag takes precedence since it is more explicit
|
|
451
|
-
if (argv["branch-prefix"]) {
|
|
452
|
-
branch_prefix = argv["branch-prefix"];
|
|
453
|
-
} else if (process.env.GIT_STACK_BRANCH_PREFIX) {
|
|
454
|
-
branch_prefix = process.env.GIT_STACK_BRANCH_PREFIX;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return `${branch_prefix}${gs_short_id()}`;
|
|
449
|
+
return `${branch_name}-${short_id()}`;
|
|
458
450
|
}
|
|
459
451
|
|
|
460
452
|
function submit_group_input(title: string) {
|
package/src/command.ts
CHANGED
|
@@ -1,54 +1,78 @@
|
|
|
1
1
|
import yargs from "yargs";
|
|
2
2
|
import { hideBin } from "yargs/helpers";
|
|
3
3
|
|
|
4
|
-
import type { Options, InferredOptionTypes, Arguments } from "yargs";
|
|
4
|
+
import type { Options, InferredOptionTypes, Arguments, ParserConfigurationOptions } from "yargs";
|
|
5
5
|
|
|
6
6
|
export type Argv = Arguments & TGlobalOptions & TFixupOptions & TDefaultOptions;
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
type CommandOptions = {
|
|
9
|
+
env_config?: Partial<Argv>;
|
|
10
|
+
parserConfiguration?: Partial<ParserConfigurationOptions>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export async function command(argv: string[], options: CommandOptions = {}) {
|
|
9
14
|
// https://yargs.js.org/docs/#api-reference-optionkey-opt
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
15
|
+
let builder = yargs(hideBin(argv));
|
|
16
|
+
|
|
17
|
+
if (options.parserConfiguration) {
|
|
18
|
+
builder = builder.parserConfiguration(options.parserConfiguration);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// apply overrides from config
|
|
22
|
+
// higher precedence than defaults, but lower precendence than cli flags
|
|
23
|
+
// perfect since that's what we want, prefer config only if not explicitly set on cli
|
|
24
|
+
if (options.env_config) {
|
|
25
|
+
builder = builder.config(options.env_config);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const parsed = await builder
|
|
29
|
+
.scriptName("git stack")
|
|
30
|
+
.usage("Usage: git stack [command] [options]")
|
|
31
|
+
|
|
32
|
+
.command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions))
|
|
33
|
+
|
|
34
|
+
.command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) =>
|
|
35
|
+
yargs.positional("commit", FixupOptions.commit),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
.command(
|
|
39
|
+
"log [args...]",
|
|
40
|
+
"Print an abbreviated log with numbered commits, useful for git stack fixup",
|
|
41
|
+
(yargs) => yargs.strict(false),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
.command(
|
|
45
|
+
"rebase",
|
|
46
|
+
"Update local branch via rebase with latest changes from origin master branch",
|
|
47
|
+
(yargs) => yargs,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
.command(
|
|
51
|
+
["update", "upgrade"],
|
|
52
|
+
"Check and install the latest version of git stack",
|
|
53
|
+
(yargs) => yargs,
|
|
54
|
+
)
|
|
55
|
+
.command(
|
|
56
|
+
"config",
|
|
57
|
+
"Generate a one-time configuration json based on the passed arguments",
|
|
58
|
+
(yargs) => yargs.options(DefaultOptions),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
.option("verbose", GlobalOptions.verbose)
|
|
62
|
+
|
|
63
|
+
// yargs default wraps to 80 columns
|
|
64
|
+
// passing null will wrap to terminal width
|
|
65
|
+
// value below if what seems to look decent
|
|
66
|
+
.wrap(123)
|
|
67
|
+
|
|
68
|
+
// disallow unknown options
|
|
69
|
+
.strict()
|
|
70
|
+
.version(process.env.CLI_VERSION || "unknown")
|
|
71
|
+
.showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
|
|
72
|
+
.help("help", "Show usage via `git stack help`");
|
|
73
|
+
|
|
74
|
+
const result = parsed.argv as unknown as Argv;
|
|
75
|
+
return result;
|
|
52
76
|
}
|
|
53
77
|
|
|
54
78
|
const GlobalOptions = {
|
|
@@ -109,12 +133,6 @@ const DefaultOptions = {
|
|
|
109
133
|
description: "Open all PRs as drafts",
|
|
110
134
|
},
|
|
111
135
|
|
|
112
|
-
"branch-prefix": {
|
|
113
|
-
type: "string",
|
|
114
|
-
default: "",
|
|
115
|
-
description: "Prefix for generated branch names, e.g. dev/magus/",
|
|
116
|
-
},
|
|
117
|
-
|
|
118
136
|
"revise-sign": {
|
|
119
137
|
type: "boolean",
|
|
120
138
|
default: true,
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import * as Ink from "ink-cjs";
|
|
4
|
+
|
|
5
|
+
import { Await } from "~/app/Await";
|
|
6
|
+
import { FormatText } from "~/app/FormatText";
|
|
7
|
+
import { Store } from "~/app/Store";
|
|
8
|
+
import { command } from "~/command";
|
|
9
|
+
import { colors } from "~/core/colors";
|
|
10
|
+
import { invariant } from "~/core/invariant";
|
|
11
|
+
|
|
12
|
+
export function Config() {
|
|
13
|
+
return <Await fallback={null} function={run} />;
|
|
14
|
+
|
|
15
|
+
async function run() {
|
|
16
|
+
const state = Store.getState();
|
|
17
|
+
const actions = state.actions;
|
|
18
|
+
|
|
19
|
+
const config = await get_explicit_args();
|
|
20
|
+
const config_json = JSON.stringify(config).replace(/"/g, '\\"');
|
|
21
|
+
|
|
22
|
+
actions.output(
|
|
23
|
+
<Ink.Box flexDirection="column" gap={1} paddingTop={1}>
|
|
24
|
+
<Ink.Text></Ink.Text>
|
|
25
|
+
|
|
26
|
+
<FormatText
|
|
27
|
+
message="Add the line below to your shell rc file ({zshrc}, {bashrc}, etc.)"
|
|
28
|
+
values={{
|
|
29
|
+
zshrc: <Ink.Text color={colors.gray}>.zshrc</Ink.Text>,
|
|
30
|
+
bashrc: <Ink.Text color={colors.gray}>.bashrc</Ink.Text>,
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
<FormatText
|
|
35
|
+
message={`{export} {ENV_VAR}{e}{q}{config_json}{q}`}
|
|
36
|
+
values={{
|
|
37
|
+
export: <Ink.Text color={colors.purple}>export</Ink.Text>,
|
|
38
|
+
ENV_VAR: <Ink.Text color={colors.yellow}>{ENV_VAR}</Ink.Text>,
|
|
39
|
+
e: <Ink.Text color={colors.purple}>{"="}</Ink.Text>,
|
|
40
|
+
q: <Ink.Text color={colors.white}>{'"'}</Ink.Text>,
|
|
41
|
+
config_json: <Ink.Text color={colors.green}>{config_json}</Ink.Text>,
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
</Ink.Box>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
actions.exit(0);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function argv_with_config_from_env() {
|
|
52
|
+
if (!process.env.GIT_STACK_CONFIG) {
|
|
53
|
+
return await command(process.argv);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const env_config = parse_env_config();
|
|
57
|
+
return await command(process.argv, { env_config });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parse_env_config() {
|
|
61
|
+
const GIT_STACK_CONFIG = process.env.GIT_STACK_CONFIG;
|
|
62
|
+
invariant(GIT_STACK_CONFIG, "GIT_STACK_CONFIG must exist");
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const env_config = JSON.parse(GIT_STACK_CONFIG);
|
|
66
|
+
return env_config;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
// eslint-disable-next-line no-console
|
|
69
|
+
console.error(`ERROR GIT_STACK_CONFIG=${GIT_STACK_CONFIG}`);
|
|
70
|
+
// eslint-disable-next-line no-console
|
|
71
|
+
console.error("ERROR GIT_STACK_CONFIG environment variable is not valid JSON");
|
|
72
|
+
process.exit(18);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function get_explicit_args() {
|
|
77
|
+
const default_argv = await command(["git", "stack"], COMMAND_OPTIONS);
|
|
78
|
+
const state_argv = await command(process.argv, COMMAND_OPTIONS);
|
|
79
|
+
|
|
80
|
+
const config: Record<string, any> = {};
|
|
81
|
+
|
|
82
|
+
// find delta between default_argv and argv
|
|
83
|
+
for (const key of Object.keys(state_argv)) {
|
|
84
|
+
if (key === "_" || key === "$0") continue;
|
|
85
|
+
|
|
86
|
+
const state_value = state_argv[key];
|
|
87
|
+
const default_value = default_argv[key];
|
|
88
|
+
const is_set = default_value !== state_value;
|
|
89
|
+
if (is_set) {
|
|
90
|
+
config[key] = state_value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return config;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const ENV_VAR = "GIT_STACK_CONFIG";
|
|
98
|
+
|
|
99
|
+
type CommandOptions = NonNullable<Parameters<typeof command>[1]>;
|
|
100
|
+
|
|
101
|
+
const COMMAND_OPTIONS = {
|
|
102
|
+
parserConfiguration: {
|
|
103
|
+
// Should aliases be removed before returning results? Default is `false`
|
|
104
|
+
"strip-aliased": true,
|
|
105
|
+
},
|
|
106
|
+
} satisfies CommandOptions;
|
package/src/index.tsx
CHANGED
|
@@ -11,13 +11,13 @@ import * as Ink from "ink-cjs";
|
|
|
11
11
|
|
|
12
12
|
import { App } from "~/app/App";
|
|
13
13
|
import { Store } from "~/app/Store";
|
|
14
|
-
import {
|
|
14
|
+
import { argv_with_config_from_env } from "~/commands/Config";
|
|
15
15
|
import { get_tmp_dir } from "~/core/get_tmp_dir";
|
|
16
16
|
import { pretty_json } from "~/core/pretty_json";
|
|
17
17
|
|
|
18
18
|
(async function main() {
|
|
19
19
|
try {
|
|
20
|
-
const argv = await
|
|
20
|
+
const argv = await argv_with_config_from_env();
|
|
21
21
|
|
|
22
22
|
// required to get bun working with ink
|
|
23
23
|
// https://github.com/oven-sh/bun/issues/6862#issuecomment-2429444852
|
package/src/types/global.d.ts
CHANGED