git-stack-cli 1.3.5 → 1.4.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/cjs/index.cjs +186 -90
- package/package.json +1 -1
- package/src/app/App.tsx +16 -10
- package/src/app/CherryPickCheck.tsx +94 -0
- package/src/app/DependencyCheck.tsx +4 -4
- package/src/app/DirtyCheck.tsx +91 -0
- package/src/app/GatherMetadata.tsx +1 -3
- package/src/app/LocalCommitStatus.tsx +1 -1
- package/src/app/LocalMergeRebase.tsx +6 -11
- package/src/app/ManualRebase.tsx +2 -2
- package/src/app/RebaseCheck.tsx +9 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -26560,14 +26560,14 @@ async function sleep(time) {
|
|
|
26560
26560
|
return new Promise((resolve) => setTimeout(resolve, time));
|
|
26561
26561
|
}
|
|
26562
26562
|
|
|
26563
|
-
function reducer$
|
|
26563
|
+
function reducer$3(state, patch) {
|
|
26564
26564
|
return { ...state, ...patch };
|
|
26565
26565
|
}
|
|
26566
26566
|
function AutoUpdate(props) {
|
|
26567
26567
|
const props_ref = reactExports.useRef(props);
|
|
26568
26568
|
props_ref.current = props;
|
|
26569
26569
|
const [output, set_output] = reactExports.useState([]);
|
|
26570
|
-
const [state, patch] = reactExports.useReducer(reducer$
|
|
26570
|
+
const [state, patch] = reactExports.useReducer(reducer$3, {
|
|
26571
26571
|
error: null,
|
|
26572
26572
|
local_version: null,
|
|
26573
26573
|
latest_version: null,
|
|
@@ -26676,67 +26676,6 @@ function AutoUpdate(props) {
|
|
|
26676
26676
|
status));
|
|
26677
26677
|
}
|
|
26678
26678
|
|
|
26679
|
-
function serialize(obj) {
|
|
26680
|
-
if (obj instanceof Map) {
|
|
26681
|
-
return {
|
|
26682
|
-
_type: "Map",
|
|
26683
|
-
_value: Array.from(obj.entries()).map(([k, v]) => [k, serialize(v)]),
|
|
26684
|
-
};
|
|
26685
|
-
}
|
|
26686
|
-
else if (Array.isArray(obj)) {
|
|
26687
|
-
return obj.map(serialize);
|
|
26688
|
-
}
|
|
26689
|
-
else if (obj !== null && typeof obj === "object") {
|
|
26690
|
-
const serializedObj = {};
|
|
26691
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
26692
|
-
serializedObj[key] = serialize(value);
|
|
26693
|
-
}
|
|
26694
|
-
return serializedObj;
|
|
26695
|
-
}
|
|
26696
|
-
return obj;
|
|
26697
|
-
}
|
|
26698
|
-
function deserialize(obj) {
|
|
26699
|
-
if (obj && obj._type === "Map") {
|
|
26700
|
-
return new Map(obj._value.map(([k, v]) => [k, deserialize(v)]));
|
|
26701
|
-
}
|
|
26702
|
-
else if (Array.isArray(obj)) {
|
|
26703
|
-
return obj.map(deserialize);
|
|
26704
|
-
}
|
|
26705
|
-
else if (obj !== null && typeof obj === "object") {
|
|
26706
|
-
const deserializedObj = {};
|
|
26707
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
26708
|
-
deserializedObj[key] = deserialize(value);
|
|
26709
|
-
}
|
|
26710
|
-
return deserializedObj;
|
|
26711
|
-
}
|
|
26712
|
-
return obj;
|
|
26713
|
-
}
|
|
26714
|
-
|
|
26715
|
-
function Debug() {
|
|
26716
|
-
const actions = Store.useActions();
|
|
26717
|
-
const state = Store.useState((state) => state);
|
|
26718
|
-
const argv = Store.useState((state) => state.argv);
|
|
26719
|
-
const debug = Store.useState((state) => state.select.debug(state));
|
|
26720
|
-
reactExports.useEffect(function debugMessageOnce() {
|
|
26721
|
-
if (debug) {
|
|
26722
|
-
actions.output(reactExports.createElement(Text, { color: colors.yellow }, "Debug mode enabled"));
|
|
26723
|
-
}
|
|
26724
|
-
}, [argv]);
|
|
26725
|
-
reactExports.useEffect(function syncStateJson() {
|
|
26726
|
-
if (!argv?.["write-state-json"]) {
|
|
26727
|
-
return;
|
|
26728
|
-
}
|
|
26729
|
-
const output_file = path.join(state.cwd, "git-stack-state.json");
|
|
26730
|
-
if (fs.existsSync(output_file)) {
|
|
26731
|
-
fs.rmSync(output_file);
|
|
26732
|
-
}
|
|
26733
|
-
const serialized = serialize(state);
|
|
26734
|
-
const content = JSON.stringify(serialized, null, 2);
|
|
26735
|
-
fs.writeFileSync(output_file, content);
|
|
26736
|
-
}, [argv, state]);
|
|
26737
|
-
return null;
|
|
26738
|
-
}
|
|
26739
|
-
|
|
26740
26679
|
function cache(cacheable) {
|
|
26741
26680
|
let status = "pending";
|
|
26742
26681
|
let response;
|
|
@@ -26831,6 +26770,113 @@ function Command(props) {
|
|
|
26831
26770
|
return (reactExports.createElement(Text, { bold: true, color: text_color }, props.children));
|
|
26832
26771
|
}
|
|
26833
26772
|
|
|
26773
|
+
function reducer$2(state, patch) {
|
|
26774
|
+
return { ...state, ...patch };
|
|
26775
|
+
}
|
|
26776
|
+
function CherryPickCheck(props) {
|
|
26777
|
+
const actions = Store.useActions();
|
|
26778
|
+
const [state, patch] = reactExports.useReducer(reducer$2, {
|
|
26779
|
+
status: "init",
|
|
26780
|
+
});
|
|
26781
|
+
switch (state.status) {
|
|
26782
|
+
case "done":
|
|
26783
|
+
return props.children;
|
|
26784
|
+
case "prompt":
|
|
26785
|
+
return (reactExports.createElement(YesNoPrompt, { message: reactExports.createElement(Text, { color: colors.yellow },
|
|
26786
|
+
reactExports.createElement(Command, null, "git cherry-pick"),
|
|
26787
|
+
" detected, would you like to abort it?"), onYes: async () => {
|
|
26788
|
+
await cli(`git cherry-pick --abort`);
|
|
26789
|
+
patch({ status: "done" });
|
|
26790
|
+
}, onNo: async () => {
|
|
26791
|
+
actions.exit(0);
|
|
26792
|
+
} }));
|
|
26793
|
+
default:
|
|
26794
|
+
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow },
|
|
26795
|
+
"Checking for ",
|
|
26796
|
+
reactExports.createElement(Command, null, "git cherry-pick"),
|
|
26797
|
+
"\u2026"), function: cherry_pick_check }));
|
|
26798
|
+
}
|
|
26799
|
+
async function cherry_pick_check() {
|
|
26800
|
+
const actions = Store.getState().actions;
|
|
26801
|
+
try {
|
|
26802
|
+
const git_dir = (await cli(`git rev-parse --absolute-git-dir`)).stdout;
|
|
26803
|
+
const is_cherry_pick = fs.existsSync(path.join(git_dir, "CHERRY_PICK_HEAD"));
|
|
26804
|
+
const status = is_cherry_pick ? "prompt" : "done";
|
|
26805
|
+
patch({ status });
|
|
26806
|
+
}
|
|
26807
|
+
catch (err) {
|
|
26808
|
+
actions.error("Must be run from within a git repository.");
|
|
26809
|
+
if (err instanceof Error) {
|
|
26810
|
+
if (actions.isDebug()) {
|
|
26811
|
+
actions.error(err.message);
|
|
26812
|
+
}
|
|
26813
|
+
}
|
|
26814
|
+
actions.exit(9);
|
|
26815
|
+
}
|
|
26816
|
+
}
|
|
26817
|
+
}
|
|
26818
|
+
|
|
26819
|
+
function serialize(obj) {
|
|
26820
|
+
if (obj instanceof Map) {
|
|
26821
|
+
return {
|
|
26822
|
+
_type: "Map",
|
|
26823
|
+
_value: Array.from(obj.entries()).map(([k, v]) => [k, serialize(v)]),
|
|
26824
|
+
};
|
|
26825
|
+
}
|
|
26826
|
+
else if (Array.isArray(obj)) {
|
|
26827
|
+
return obj.map(serialize);
|
|
26828
|
+
}
|
|
26829
|
+
else if (obj !== null && typeof obj === "object") {
|
|
26830
|
+
const serializedObj = {};
|
|
26831
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
26832
|
+
serializedObj[key] = serialize(value);
|
|
26833
|
+
}
|
|
26834
|
+
return serializedObj;
|
|
26835
|
+
}
|
|
26836
|
+
return obj;
|
|
26837
|
+
}
|
|
26838
|
+
function deserialize(obj) {
|
|
26839
|
+
if (obj && obj._type === "Map") {
|
|
26840
|
+
return new Map(obj._value.map(([k, v]) => [k, deserialize(v)]));
|
|
26841
|
+
}
|
|
26842
|
+
else if (Array.isArray(obj)) {
|
|
26843
|
+
return obj.map(deserialize);
|
|
26844
|
+
}
|
|
26845
|
+
else if (obj !== null && typeof obj === "object") {
|
|
26846
|
+
const deserializedObj = {};
|
|
26847
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
26848
|
+
deserializedObj[key] = deserialize(value);
|
|
26849
|
+
}
|
|
26850
|
+
return deserializedObj;
|
|
26851
|
+
}
|
|
26852
|
+
return obj;
|
|
26853
|
+
}
|
|
26854
|
+
|
|
26855
|
+
function Debug() {
|
|
26856
|
+
const actions = Store.useActions();
|
|
26857
|
+
const state = Store.useState((state) => state);
|
|
26858
|
+
const argv = Store.useState((state) => state.argv);
|
|
26859
|
+
const debug = Store.useState((state) => state.select.debug(state));
|
|
26860
|
+
reactExports.useEffect(function debugMessageOnce() {
|
|
26861
|
+
if (debug) {
|
|
26862
|
+
actions.output(reactExports.createElement(Text, { color: colors.yellow }, "Debug mode enabled"));
|
|
26863
|
+
}
|
|
26864
|
+
}, [argv]);
|
|
26865
|
+
reactExports.useEffect(function syncStateJson() {
|
|
26866
|
+
if (!argv?.["write-state-json"]) {
|
|
26867
|
+
return;
|
|
26868
|
+
}
|
|
26869
|
+
const output_file = path.join(state.cwd, "git-stack-state.json");
|
|
26870
|
+
if (fs.existsSync(output_file)) {
|
|
26871
|
+
fs.rmSync(output_file);
|
|
26872
|
+
}
|
|
26873
|
+
const serialized = serialize(state);
|
|
26874
|
+
const content = JSON.stringify(serialized, null, 2);
|
|
26875
|
+
fs.writeFileSync(output_file, content);
|
|
26876
|
+
}, [argv, state]);
|
|
26877
|
+
return null;
|
|
26878
|
+
}
|
|
26879
|
+
|
|
26834
26880
|
function Url(props) {
|
|
26835
26881
|
return (reactExports.createElement(Text, { bold: true, color: colors.blue, ...props }, props.children));
|
|
26836
26882
|
}
|
|
@@ -26892,7 +26938,7 @@ function CheckGit(props) {
|
|
|
26892
26938
|
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow },
|
|
26893
26939
|
"Checking ",
|
|
26894
26940
|
reactExports.createElement(Command, null, "git"),
|
|
26895
|
-
" install
|
|
26941
|
+
" install\u2026"), function: async () => {
|
|
26896
26942
|
// await Promise.all([
|
|
26897
26943
|
// cli(`for i in $(seq 1 5); do echo $i; sleep 1; done`),
|
|
26898
26944
|
// cli(`for i in $(seq 5 1); do printf "$i "; sleep 1; done; echo`),
|
|
@@ -26912,7 +26958,7 @@ function CheckGithubCli(props) {
|
|
|
26912
26958
|
reactExports.createElement(Text, null,
|
|
26913
26959
|
"Checking ",
|
|
26914
26960
|
reactExports.createElement(Command, null, "gh"),
|
|
26915
|
-
" install
|
|
26961
|
+
" install\u2026")), function: async () => {
|
|
26916
26962
|
if (is_command_available("gh")) {
|
|
26917
26963
|
return;
|
|
26918
26964
|
}
|
|
@@ -26932,7 +26978,7 @@ function CheckGithubCliAuth(props) {
|
|
|
26932
26978
|
reactExports.createElement(Text, null,
|
|
26933
26979
|
"Checking ",
|
|
26934
26980
|
reactExports.createElement(Command, null, "gh auth status"),
|
|
26935
|
-
"
|
|
26981
|
+
"\u2026")), function: async () => {
|
|
26936
26982
|
const options = { ignoreExitCode: true };
|
|
26937
26983
|
const auth_status$1 = await cli(`gh auth status`, options);
|
|
26938
26984
|
if (auth_status$1.code === 0) {
|
|
@@ -26965,7 +27011,7 @@ function CheckGitRevise(props) {
|
|
|
26965
27011
|
reactExports.createElement(Text, null,
|
|
26966
27012
|
"Checking ",
|
|
26967
27013
|
reactExports.createElement(Command, null, "git revise"),
|
|
26968
|
-
" install
|
|
27014
|
+
" install\u2026")), function: async () => {
|
|
26969
27015
|
if (is_command_available("git-revise")) {
|
|
26970
27016
|
return;
|
|
26971
27017
|
}
|
|
@@ -26982,8 +27028,53 @@ function CheckGitRevise(props) {
|
|
|
26982
27028
|
} }, props.children));
|
|
26983
27029
|
}
|
|
26984
27030
|
|
|
27031
|
+
function reducer$1(state, patch) {
|
|
27032
|
+
return { ...state, ...patch };
|
|
27033
|
+
}
|
|
27034
|
+
function DirtyCheck(props) {
|
|
27035
|
+
const actions = Store.useActions();
|
|
27036
|
+
const [state, patch] = reactExports.useReducer(reducer$1, {
|
|
27037
|
+
status: "init",
|
|
27038
|
+
});
|
|
27039
|
+
switch (state.status) {
|
|
27040
|
+
case "done":
|
|
27041
|
+
return props.children;
|
|
27042
|
+
case "prompt":
|
|
27043
|
+
return (reactExports.createElement(YesNoPrompt, { message: reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.yellow }), message: "{git} repo is dirty, changed may be lost during {git_stack}, are you sure you wannt to proceed?", values: {
|
|
27044
|
+
git: reactExports.createElement(Command, null, "git"),
|
|
27045
|
+
git_stack: reactExports.createElement(Command, null, "git stack"),
|
|
27046
|
+
} }), onYes: async () => {
|
|
27047
|
+
patch({ status: "done" });
|
|
27048
|
+
}, onNo: async () => {
|
|
27049
|
+
actions.exit(0);
|
|
27050
|
+
} }));
|
|
27051
|
+
default:
|
|
27052
|
+
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow },
|
|
27053
|
+
"Ensuring ",
|
|
27054
|
+
reactExports.createElement(Command, null, "git status --porcelain"),
|
|
27055
|
+
"\u2026"), function: rebase_check }));
|
|
27056
|
+
}
|
|
27057
|
+
async function rebase_check() {
|
|
27058
|
+
const actions = Store.getState().actions;
|
|
27059
|
+
try {
|
|
27060
|
+
const git_dirty = (await cli(`git status --porcelain`)).stdout;
|
|
27061
|
+
const status = git_dirty ? "prompt" : "done";
|
|
27062
|
+
patch({ status });
|
|
27063
|
+
}
|
|
27064
|
+
catch (err) {
|
|
27065
|
+
actions.error("Must be run from within a git repository.");
|
|
27066
|
+
if (err instanceof Error) {
|
|
27067
|
+
if (actions.isDebug()) {
|
|
27068
|
+
actions.error(err.message);
|
|
27069
|
+
}
|
|
27070
|
+
}
|
|
27071
|
+
actions.exit(9);
|
|
27072
|
+
}
|
|
27073
|
+
}
|
|
27074
|
+
}
|
|
27075
|
+
|
|
26985
27076
|
function GatherMetadata(props) {
|
|
26986
|
-
const fallback = (reactExports.createElement(Text, { color: colors.yellow }, "Gathering local git information
|
|
27077
|
+
const fallback = (reactExports.createElement(Text, { color: colors.yellow }, "Gathering local git information\u2026"));
|
|
26987
27078
|
return (reactExports.createElement(Await, { fallback: fallback, function: gather_metadata$1 }, props.children));
|
|
26988
27079
|
}
|
|
26989
27080
|
async function gather_metadata$1() {
|
|
@@ -27445,7 +27536,7 @@ const UNASSIGNED = "unassigned";
|
|
|
27445
27536
|
|
|
27446
27537
|
function LocalCommitStatus(props) {
|
|
27447
27538
|
const argv = Store.useState((state) => state.argv);
|
|
27448
|
-
const fallback = (reactExports.createElement(Text, { color: colors.yellow }, "Fetching PR status from Github
|
|
27539
|
+
const fallback = (reactExports.createElement(Text, { color: colors.yellow }, "Fetching PR status from Github\u2026"));
|
|
27449
27540
|
if (argv["mock-metadata"]) {
|
|
27450
27541
|
return (reactExports.createElement(Await, { fallback: fallback, function: mock_metadata }, props.children));
|
|
27451
27542
|
}
|
|
@@ -27539,7 +27630,7 @@ function encode(value) {
|
|
|
27539
27630
|
}
|
|
27540
27631
|
|
|
27541
27632
|
function LocalMergeRebase() {
|
|
27542
|
-
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits
|
|
27633
|
+
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026"), function: run$3 }));
|
|
27543
27634
|
}
|
|
27544
27635
|
async function run$3() {
|
|
27545
27636
|
const state = Store.getState();
|
|
@@ -27589,13 +27680,11 @@ async function run$3() {
|
|
|
27589
27680
|
picked_commit_list.push(commit);
|
|
27590
27681
|
}
|
|
27591
27682
|
if (picked_commit_list.length > 0) {
|
|
27592
|
-
const first_commit = picked_commit_list.at(0);
|
|
27593
|
-
const last_commit = picked_commit_list.at(-1);
|
|
27594
|
-
invariant(first_commit, "first_commit must exist");
|
|
27595
|
-
invariant(last_commit, "last_commit must exist");
|
|
27596
27683
|
// ensure clean base to avoid conflicts when applying patch
|
|
27597
27684
|
await cli(`git clean -fd`);
|
|
27598
|
-
|
|
27685
|
+
// create list of sha for cherry-pick
|
|
27686
|
+
const sha_list = picked_commit_list.map((commit) => commit.sha).join(" ");
|
|
27687
|
+
await cli(`git cherry-pick --keep-redundant-commits ${sha_list}`);
|
|
27599
27688
|
}
|
|
27600
27689
|
// after all commits have been cherry-picked and amended
|
|
27601
27690
|
// move the branch pointer to the newly created temporary branch
|
|
@@ -27649,7 +27738,7 @@ async function run$3() {
|
|
|
27649
27738
|
actions.output(reactExports.createElement(Text, { color: colors.yellow },
|
|
27650
27739
|
"Restoring ",
|
|
27651
27740
|
reactExports.createElement(Brackets, null, branch_name),
|
|
27652
|
-
"
|
|
27741
|
+
"\u2026"));
|
|
27653
27742
|
restore_git();
|
|
27654
27743
|
actions.output(reactExports.createElement(Text, { color: colors.yellow },
|
|
27655
27744
|
"Restored ",
|
|
@@ -27812,7 +27901,7 @@ const RE = {
|
|
|
27812
27901
|
};
|
|
27813
27902
|
|
|
27814
27903
|
function ManualRebase() {
|
|
27815
|
-
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits
|
|
27904
|
+
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026"), function: run$2 }));
|
|
27816
27905
|
}
|
|
27817
27906
|
async function run$2() {
|
|
27818
27907
|
const state = Store.getState();
|
|
@@ -28131,7 +28220,7 @@ echo "$GIT_REVISE_TODO" > "$git_revise_todo_path"
|
|
|
28131
28220
|
actions.output(reactExports.createElement(Text, { color: colors.yellow },
|
|
28132
28221
|
"Restoring ",
|
|
28133
28222
|
reactExports.createElement(Brackets, null, branch_name),
|
|
28134
|
-
"
|
|
28223
|
+
"\u2026"));
|
|
28135
28224
|
restore_git();
|
|
28136
28225
|
actions.output(reactExports.createElement(Text, { color: colors.yellow },
|
|
28137
28226
|
"Restored ",
|
|
@@ -28885,22 +28974,27 @@ function RebaseCheck(props) {
|
|
|
28885
28974
|
case "done":
|
|
28886
28975
|
return props.children;
|
|
28887
28976
|
case "prompt":
|
|
28888
|
-
return (reactExports.createElement(YesNoPrompt, { message: reactExports.createElement(Text, { color: colors.yellow },
|
|
28977
|
+
return (reactExports.createElement(YesNoPrompt, { message: reactExports.createElement(Text, { color: colors.yellow },
|
|
28978
|
+
reactExports.createElement(Command, null, "git rebase"),
|
|
28979
|
+
" detected, would you like to abort it?"), onYes: async () => {
|
|
28889
28980
|
await cli(`git rebase --abort`);
|
|
28890
28981
|
patch({ status: "done" });
|
|
28891
28982
|
}, onNo: async () => {
|
|
28892
28983
|
actions.exit(0);
|
|
28893
28984
|
} }));
|
|
28894
28985
|
default:
|
|
28895
|
-
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow },
|
|
28986
|
+
return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow },
|
|
28987
|
+
"Checking for ",
|
|
28988
|
+
reactExports.createElement(Command, null, "git rebase"),
|
|
28989
|
+
"\u2026"), function: rebase_check }));
|
|
28896
28990
|
}
|
|
28897
28991
|
async function rebase_check() {
|
|
28898
28992
|
const actions = Store.getState().actions;
|
|
28899
28993
|
try {
|
|
28900
|
-
const
|
|
28994
|
+
const git_dir = (await cli(`git rev-parse --absolute-git-dir`)).stdout;
|
|
28901
28995
|
let is_rebase = false;
|
|
28902
|
-
is_rebase ||= fs.existsSync(path.join(
|
|
28903
|
-
is_rebase ||= fs.existsSync(path.join(
|
|
28996
|
+
is_rebase ||= fs.existsSync(path.join(git_dir, "rebase-apply"));
|
|
28997
|
+
is_rebase ||= fs.existsSync(path.join(git_dir, "rebase-merge"));
|
|
28904
28998
|
const status = is_rebase ? "prompt" : "done";
|
|
28905
28999
|
patch({ status });
|
|
28906
29000
|
}
|
|
@@ -28939,12 +29033,14 @@ function App() {
|
|
|
28939
29033
|
actions.exit(0);
|
|
28940
29034
|
}
|
|
28941
29035
|
} },
|
|
28942
|
-
reactExports.createElement(
|
|
28943
|
-
reactExports.createElement(
|
|
28944
|
-
|
|
28945
|
-
|
|
28946
|
-
|
|
28947
|
-
reactExports.createElement(
|
|
29036
|
+
reactExports.createElement(DependencyCheck, null,
|
|
29037
|
+
reactExports.createElement(DirtyCheck, null,
|
|
29038
|
+
reactExports.createElement(RebaseCheck, null,
|
|
29039
|
+
reactExports.createElement(CherryPickCheck, null,
|
|
29040
|
+
!argv.verbose ? null : reactExports.createElement(GithubApiError, null),
|
|
29041
|
+
reactExports.createElement(GatherMetadata, null,
|
|
29042
|
+
reactExports.createElement(LocalCommitStatus, null,
|
|
29043
|
+
reactExports.createElement(Main, null))))))))));
|
|
28948
29044
|
}
|
|
28949
29045
|
|
|
28950
29046
|
const align = {
|
|
@@ -34322,7 +34418,7 @@ async function command() {
|
|
|
34322
34418
|
.wrap(123)
|
|
34323
34419
|
// disallow unknown options
|
|
34324
34420
|
.strict()
|
|
34325
|
-
.version("1.
|
|
34421
|
+
.version("1.4.0" )
|
|
34326
34422
|
.showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
|
|
34327
34423
|
.help("help", "Show usage via `git stack help`").argv);
|
|
34328
34424
|
}
|
package/package.json
CHANGED
package/src/app/App.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
3
|
import { AutoUpdate } from "~/app/AutoUpdate";
|
|
4
|
+
import { CherryPickCheck } from "~/app/CherryPickCheck";
|
|
4
5
|
import { Debug } from "~/app/Debug";
|
|
5
6
|
import { DependencyCheck } from "~/app/DependencyCheck";
|
|
7
|
+
import { DirtyCheck } from "~/app/DirtyCheck";
|
|
6
8
|
import { GatherMetadata } from "~/app/GatherMetadata";
|
|
7
9
|
import { GithubApiError } from "~/app/GithubApiError";
|
|
8
10
|
import { LocalCommitStatus } from "~/app/LocalCommitStatus";
|
|
@@ -48,17 +50,21 @@ export function App() {
|
|
|
48
50
|
}
|
|
49
51
|
}}
|
|
50
52
|
>
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
|
|
53
|
+
<DependencyCheck>
|
|
54
|
+
<DirtyCheck>
|
|
55
|
+
<RebaseCheck>
|
|
56
|
+
<CherryPickCheck>
|
|
57
|
+
{!argv.verbose ? null : <GithubApiError />}
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
<GatherMetadata>
|
|
60
|
+
<LocalCommitStatus>
|
|
61
|
+
<Main />
|
|
62
|
+
</LocalCommitStatus>
|
|
63
|
+
</GatherMetadata>
|
|
64
|
+
</CherryPickCheck>
|
|
65
|
+
</RebaseCheck>
|
|
66
|
+
</DirtyCheck>
|
|
67
|
+
</DependencyCheck>
|
|
62
68
|
</AutoUpdate>
|
|
63
69
|
</Providers>
|
|
64
70
|
);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
import * as Ink from "ink-cjs";
|
|
7
|
+
|
|
8
|
+
import { Await } from "~/app/Await";
|
|
9
|
+
import { Command } from "~/app/Command";
|
|
10
|
+
import { Store } from "~/app/Store";
|
|
11
|
+
import { YesNoPrompt } from "~/app/YesNoPrompt";
|
|
12
|
+
import { cli } from "~/core/cli";
|
|
13
|
+
import { colors } from "~/core/colors";
|
|
14
|
+
|
|
15
|
+
type Props = {
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type State = {
|
|
20
|
+
status: "init" | "prompt" | "done";
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function reducer(state: State, patch: Partial<State>) {
|
|
24
|
+
return { ...state, ...patch };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function CherryPickCheck(props: Props) {
|
|
28
|
+
const actions = Store.useActions();
|
|
29
|
+
|
|
30
|
+
const [state, patch] = React.useReducer(reducer, {
|
|
31
|
+
status: "init",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
switch (state.status) {
|
|
35
|
+
case "done":
|
|
36
|
+
return props.children;
|
|
37
|
+
|
|
38
|
+
case "prompt":
|
|
39
|
+
return (
|
|
40
|
+
<YesNoPrompt
|
|
41
|
+
message={
|
|
42
|
+
<Ink.Text color={colors.yellow}>
|
|
43
|
+
<Command>git cherry-pick</Command> detected, would you like to
|
|
44
|
+
abort it?
|
|
45
|
+
</Ink.Text>
|
|
46
|
+
}
|
|
47
|
+
onYes={async () => {
|
|
48
|
+
await cli(`git cherry-pick --abort`);
|
|
49
|
+
patch({ status: "done" });
|
|
50
|
+
}}
|
|
51
|
+
onNo={async () => {
|
|
52
|
+
actions.exit(0);
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
default:
|
|
58
|
+
return (
|
|
59
|
+
<Await
|
|
60
|
+
fallback={
|
|
61
|
+
<Ink.Text color={colors.yellow}>
|
|
62
|
+
Checking for <Command>git cherry-pick</Command>…
|
|
63
|
+
</Ink.Text>
|
|
64
|
+
}
|
|
65
|
+
function={cherry_pick_check}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function cherry_pick_check() {
|
|
71
|
+
const actions = Store.getState().actions;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const git_dir = (await cli(`git rev-parse --absolute-git-dir`)).stdout;
|
|
75
|
+
|
|
76
|
+
const is_cherry_pick = fs.existsSync(
|
|
77
|
+
path.join(git_dir, "CHERRY_PICK_HEAD")
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const status = is_cherry_pick ? "prompt" : "done";
|
|
81
|
+
patch({ status });
|
|
82
|
+
} catch (err) {
|
|
83
|
+
actions.error("Must be run from within a git repository.");
|
|
84
|
+
|
|
85
|
+
if (err instanceof Error) {
|
|
86
|
+
if (actions.isDebug()) {
|
|
87
|
+
actions.error(err.message);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
actions.exit(9);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -39,7 +39,7 @@ function CheckGit(props: Props) {
|
|
|
39
39
|
<Await
|
|
40
40
|
fallback={
|
|
41
41
|
<Ink.Text color={colors.yellow}>
|
|
42
|
-
Checking <Command>git</Command> install
|
|
42
|
+
Checking <Command>git</Command> install…
|
|
43
43
|
</Ink.Text>
|
|
44
44
|
}
|
|
45
45
|
function={async () => {
|
|
@@ -74,7 +74,7 @@ function CheckGithubCli(props: Props) {
|
|
|
74
74
|
fallback={
|
|
75
75
|
<Ink.Text color={colors.yellow}>
|
|
76
76
|
<Ink.Text>
|
|
77
|
-
Checking <Command>gh</Command> install
|
|
77
|
+
Checking <Command>gh</Command> install…
|
|
78
78
|
</Ink.Text>
|
|
79
79
|
</Ink.Text>
|
|
80
80
|
}
|
|
@@ -117,7 +117,7 @@ function CheckGithubCliAuth(props: Props) {
|
|
|
117
117
|
fallback={
|
|
118
118
|
<Ink.Text color={colors.yellow}>
|
|
119
119
|
<Ink.Text>
|
|
120
|
-
Checking <Command>gh auth status</Command
|
|
120
|
+
Checking <Command>gh auth status</Command>…
|
|
121
121
|
</Ink.Text>
|
|
122
122
|
</Ink.Text>
|
|
123
123
|
}
|
|
@@ -175,7 +175,7 @@ function CheckGitRevise(props: Props) {
|
|
|
175
175
|
fallback={
|
|
176
176
|
<Ink.Text color={colors.yellow}>
|
|
177
177
|
<Ink.Text>
|
|
178
|
-
Checking <Command>git revise</Command> install
|
|
178
|
+
Checking <Command>git revise</Command> install…
|
|
179
179
|
</Ink.Text>
|
|
180
180
|
</Ink.Text>
|
|
181
181
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import * as Ink from "ink-cjs";
|
|
4
|
+
|
|
5
|
+
import { Await } from "~/app/Await";
|
|
6
|
+
import { Command } from "~/app/Command";
|
|
7
|
+
import { FormatText } from "~/app/FormatText";
|
|
8
|
+
import { Store } from "~/app/Store";
|
|
9
|
+
import { YesNoPrompt } from "~/app/YesNoPrompt";
|
|
10
|
+
import { cli } from "~/core/cli";
|
|
11
|
+
import { colors } from "~/core/colors";
|
|
12
|
+
|
|
13
|
+
type Props = {
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type State = {
|
|
18
|
+
status: "init" | "prompt" | "done";
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function reducer(state: State, patch: Partial<State>) {
|
|
22
|
+
return { ...state, ...patch };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function DirtyCheck(props: Props) {
|
|
26
|
+
const actions = Store.useActions();
|
|
27
|
+
|
|
28
|
+
const [state, patch] = React.useReducer(reducer, {
|
|
29
|
+
status: "init",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
switch (state.status) {
|
|
33
|
+
case "done":
|
|
34
|
+
return props.children;
|
|
35
|
+
|
|
36
|
+
case "prompt":
|
|
37
|
+
return (
|
|
38
|
+
<YesNoPrompt
|
|
39
|
+
message={
|
|
40
|
+
<FormatText
|
|
41
|
+
wrapper={<Ink.Text color={colors.yellow} />}
|
|
42
|
+
message="{git} repo is dirty, changed may be lost during {git_stack}, are you sure you wannt to proceed?"
|
|
43
|
+
values={{
|
|
44
|
+
git: <Command>git</Command>,
|
|
45
|
+
git_stack: <Command>git stack</Command>,
|
|
46
|
+
}}
|
|
47
|
+
/>
|
|
48
|
+
}
|
|
49
|
+
onYes={async () => {
|
|
50
|
+
patch({ status: "done" });
|
|
51
|
+
}}
|
|
52
|
+
onNo={async () => {
|
|
53
|
+
actions.exit(0);
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
default:
|
|
59
|
+
return (
|
|
60
|
+
<Await
|
|
61
|
+
fallback={
|
|
62
|
+
<Ink.Text color={colors.yellow}>
|
|
63
|
+
Ensuring <Command>git status --porcelain</Command>…
|
|
64
|
+
</Ink.Text>
|
|
65
|
+
}
|
|
66
|
+
function={rebase_check}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function rebase_check() {
|
|
72
|
+
const actions = Store.getState().actions;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const git_dirty = (await cli(`git status --porcelain`)).stdout;
|
|
76
|
+
|
|
77
|
+
const status = git_dirty ? "prompt" : "done";
|
|
78
|
+
patch({ status });
|
|
79
|
+
} catch (err) {
|
|
80
|
+
actions.error("Must be run from within a git repository.");
|
|
81
|
+
|
|
82
|
+
if (err instanceof Error) {
|
|
83
|
+
if (actions.isDebug()) {
|
|
84
|
+
actions.error(err.message);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
actions.exit(9);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -16,9 +16,7 @@ type Props = {
|
|
|
16
16
|
|
|
17
17
|
export function GatherMetadata(props: Props) {
|
|
18
18
|
const fallback = (
|
|
19
|
-
<Ink.Text color={colors.yellow}>
|
|
20
|
-
Gathering local git information...
|
|
21
|
-
</Ink.Text>
|
|
19
|
+
<Ink.Text color={colors.yellow}>Gathering local git information…</Ink.Text>
|
|
22
20
|
);
|
|
23
21
|
|
|
24
22
|
return (
|
|
@@ -16,7 +16,7 @@ export function LocalCommitStatus(props: Props) {
|
|
|
16
16
|
const argv = Store.useState((state) => state.argv);
|
|
17
17
|
|
|
18
18
|
const fallback = (
|
|
19
|
-
<Ink.Text color={colors.yellow}>Fetching PR status from Github
|
|
19
|
+
<Ink.Text color={colors.yellow}>Fetching PR status from Github…</Ink.Text>
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
if (argv["mock-metadata"]) {
|
|
@@ -18,7 +18,7 @@ import { short_id } from "~/core/short_id";
|
|
|
18
18
|
export function LocalMergeRebase() {
|
|
19
19
|
return (
|
|
20
20
|
<Await
|
|
21
|
-
fallback={<Ink.Text color={colors.yellow}>Rebasing commits
|
|
21
|
+
fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>}
|
|
22
22
|
function={run}
|
|
23
23
|
/>
|
|
24
24
|
);
|
|
@@ -102,18 +102,13 @@ async function run() {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
if (picked_commit_list.length > 0) {
|
|
105
|
-
const first_commit = picked_commit_list.at(0);
|
|
106
|
-
const last_commit = picked_commit_list.at(-1);
|
|
107
|
-
|
|
108
|
-
invariant(first_commit, "first_commit must exist");
|
|
109
|
-
invariant(last_commit, "last_commit must exist");
|
|
110
|
-
|
|
111
105
|
// ensure clean base to avoid conflicts when applying patch
|
|
112
106
|
await cli(`git clean -fd`);
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
108
|
+
// create list of sha for cherry-pick
|
|
109
|
+
const sha_list = picked_commit_list.map((commit) => commit.sha).join(" ");
|
|
110
|
+
|
|
111
|
+
await cli(`git cherry-pick --keep-redundant-commits ${sha_list}`);
|
|
117
112
|
}
|
|
118
113
|
|
|
119
114
|
// after all commits have been cherry-picked and amended
|
|
@@ -179,7 +174,7 @@ async function run() {
|
|
|
179
174
|
function handle_exit() {
|
|
180
175
|
actions.output(
|
|
181
176
|
<Ink.Text color={colors.yellow}>
|
|
182
|
-
Restoring <Brackets>{branch_name}</Brackets
|
|
177
|
+
Restoring <Brackets>{branch_name}</Brackets>…
|
|
183
178
|
</Ink.Text>
|
|
184
179
|
);
|
|
185
180
|
|
package/src/app/ManualRebase.tsx
CHANGED
|
@@ -23,7 +23,7 @@ import { short_id } from "~/core/short_id";
|
|
|
23
23
|
export function ManualRebase() {
|
|
24
24
|
return (
|
|
25
25
|
<Await
|
|
26
|
-
fallback={<Ink.Text color={colors.yellow}>Rebasing commits
|
|
26
|
+
fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>}
|
|
27
27
|
function={run}
|
|
28
28
|
/>
|
|
29
29
|
);
|
|
@@ -423,7 +423,7 @@ async function run() {
|
|
|
423
423
|
function handle_exit() {
|
|
424
424
|
actions.output(
|
|
425
425
|
<Ink.Text color={colors.yellow}>
|
|
426
|
-
Restoring <Brackets>{branch_name}</Brackets
|
|
426
|
+
Restoring <Brackets>{branch_name}</Brackets>…
|
|
427
427
|
</Ink.Text>
|
|
428
428
|
);
|
|
429
429
|
|
package/src/app/RebaseCheck.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import * as Ink from "ink-cjs";
|
|
7
7
|
|
|
8
8
|
import { Await } from "~/app/Await";
|
|
9
|
+
import { Command } from "~/app/Command";
|
|
9
10
|
import { Store } from "~/app/Store";
|
|
10
11
|
import { YesNoPrompt } from "~/app/YesNoPrompt";
|
|
11
12
|
import { cli } from "~/core/cli";
|
|
@@ -39,7 +40,8 @@ export function RebaseCheck(props: Props) {
|
|
|
39
40
|
<YesNoPrompt
|
|
40
41
|
message={
|
|
41
42
|
<Ink.Text color={colors.yellow}>
|
|
42
|
-
|
|
43
|
+
<Command>git rebase</Command> detected, would you like to abort
|
|
44
|
+
it?
|
|
43
45
|
</Ink.Text>
|
|
44
46
|
}
|
|
45
47
|
onYes={async () => {
|
|
@@ -56,7 +58,9 @@ export function RebaseCheck(props: Props) {
|
|
|
56
58
|
return (
|
|
57
59
|
<Await
|
|
58
60
|
fallback={
|
|
59
|
-
<Ink.Text color={colors.yellow}>
|
|
61
|
+
<Ink.Text color={colors.yellow}>
|
|
62
|
+
Checking for <Command>git rebase</Command>…
|
|
63
|
+
</Ink.Text>
|
|
60
64
|
}
|
|
61
65
|
function={rebase_check}
|
|
62
66
|
/>
|
|
@@ -67,11 +71,11 @@ export function RebaseCheck(props: Props) {
|
|
|
67
71
|
const actions = Store.getState().actions;
|
|
68
72
|
|
|
69
73
|
try {
|
|
70
|
-
const
|
|
74
|
+
const git_dir = (await cli(`git rev-parse --absolute-git-dir`)).stdout;
|
|
71
75
|
|
|
72
76
|
let is_rebase = false;
|
|
73
|
-
is_rebase ||= fs.existsSync(path.join(
|
|
74
|
-
is_rebase ||= fs.existsSync(path.join(
|
|
77
|
+
is_rebase ||= fs.existsSync(path.join(git_dir, "rebase-apply"));
|
|
78
|
+
is_rebase ||= fs.existsSync(path.join(git_dir, "rebase-merge"));
|
|
75
79
|
|
|
76
80
|
const status = is_rebase ? "prompt" : "done";
|
|
77
81
|
patch({ status });
|