git-stack-cli 1.10.0 → 1.11.1

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.
@@ -30147,6 +30147,10 @@ function DetectInitialPR(props) {
30147
30147
  for (const group of commit_range.group_list) {
30148
30148
  group.id = branch_name;
30149
30149
  group.title = state.pr?.title || "-";
30150
+ for (const commit of commit_range.commit_list) {
30151
+ commit.branch_id = group.id;
30152
+ commit.title = group.title;
30153
+ }
30150
30154
  }
30151
30155
  // get latest merge_base relative to local master
30152
30156
  const rebase_group_index = 0;
@@ -30220,9 +30224,9 @@ function DirtyCheck(props) {
30220
30224
 
30221
30225
  function GatherMetadata(props) {
30222
30226
  const fallback = (reactExports.createElement(Text, { color: colors.yellow }, "Gathering local git information\u2026"));
30223
- return (reactExports.createElement(Await, { fallback: fallback, function: run$9 }, props.children));
30227
+ return (reactExports.createElement(Await, { fallback: fallback, function: run$8 }, props.children));
30224
30228
  }
30225
- async function run$9() {
30229
+ async function run$8() {
30226
30230
  const actions = Store.getState().actions;
30227
30231
  const argv = Store.getState().argv;
30228
30232
  try {
@@ -30316,9 +30320,9 @@ function format_time(date) {
30316
30320
  }
30317
30321
 
30318
30322
  function GithubApiError() {
30319
- return reactExports.createElement(Await, { fallback: null, function: run$8 });
30323
+ return reactExports.createElement(Await, { fallback: null, function: run$7 });
30320
30324
  }
30321
- async function run$8() {
30325
+ async function run$7() {
30322
30326
  const actions = Store.getState().actions;
30323
30327
  const res = await cli(`gh api https://api.github.com/rate_limit`);
30324
30328
  const res_json = JSON.parse(res.stdout);
@@ -30360,7 +30364,7 @@ function LocalCommitStatus(props) {
30360
30364
  if (argv["mock-metadata"]) {
30361
30365
  return (reactExports.createElement(Await, { fallback: fallback, function: mock_metadata }, props.children));
30362
30366
  }
30363
- return (reactExports.createElement(Await, { fallback: fallback, function: run$7 }, props.children));
30367
+ return (reactExports.createElement(Await, { fallback: fallback, function: run$6 }, props.children));
30364
30368
  }
30365
30369
  async function mock_metadata() {
30366
30370
  const module = await Promise.resolve().then(function () { return metadata; });
@@ -30370,7 +30374,7 @@ async function mock_metadata() {
30370
30374
  state.step = "status";
30371
30375
  });
30372
30376
  }
30373
- async function run$7() {
30377
+ async function run$6() {
30374
30378
  const actions = Store.getState().actions;
30375
30379
  try {
30376
30380
  const commit_range = await range();
@@ -30449,10 +30453,10 @@ function encode(value) {
30449
30453
  return result.padStart(max_char_size, "=");
30450
30454
  }
30451
30455
 
30452
- function LocalMergeRebase() {
30453
- return (reactExports.createElement(Await, { fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026"), function: run$6 }));
30456
+ function Rebase$1() {
30457
+ return (reactExports.createElement(Await, { function: Rebase$1.run, fallback: reactExports.createElement(Text, { color: colors.yellow }, "Rebasing commits\u2026") }));
30454
30458
  }
30455
- async function run$6() {
30459
+ Rebase$1.run = async function run() {
30456
30460
  const state = Store.getState();
30457
30461
  const actions = state.actions;
30458
30462
  const branch_name = state.branch_name;
@@ -30512,6 +30516,10 @@ async function run$6() {
30512
30516
  await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
30513
30517
  restore_git();
30514
30518
  const next_commit_range = await range();
30519
+ actions.output(reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.green }), message: "\u2705 {branch_name} in sync with {origin_branch}", values: {
30520
+ branch_name: reactExports.createElement(Brackets, null, branch_name),
30521
+ origin_branch: reactExports.createElement(Brackets, null, `origin/${master_branch}`),
30522
+ } }));
30515
30523
  actions.set((state) => {
30516
30524
  state.commit_range = next_commit_range;
30517
30525
  state.step = "status";
@@ -30566,9 +30574,13 @@ async function run$6() {
30566
30574
  "."));
30567
30575
  actions.exit(6);
30568
30576
  }
30569
- }
30577
+ };
30570
30578
  const PATCH_FILE$1 = "git-stack-cli-patch.patch";
30571
30579
 
30580
+ function LocalMergeRebase() {
30581
+ return reactExports.createElement(Rebase$1, null);
30582
+ }
30583
+
30572
30584
  function write(args) {
30573
30585
  const stack_table = table(args);
30574
30586
  let result = args.body;
@@ -32042,6 +32054,11 @@ function MaybeMain() {
32042
32054
  else if (positional_list.has("log")) {
32043
32055
  return reactExports.createElement(Log, null);
32044
32056
  }
32057
+ else if (positional_list.has("rebase")) {
32058
+ return (reactExports.createElement(GatherMetadata, null,
32059
+ reactExports.createElement(LocalCommitStatus, null,
32060
+ reactExports.createElement(Rebase$1, null))));
32061
+ }
32045
32062
  return (reactExports.createElement(DirtyCheck, null,
32046
32063
  !argv.verbose ? null : reactExports.createElement(GithubApiError, null),
32047
32064
  reactExports.createElement(GatherMetadata, null,
@@ -37360,6 +37377,7 @@ async function command() {
37360
37377
  .command("$0", "Sync commit ranges to Github", (yargs) => yargs.options(DefaultOptions))
37361
37378
  .command("fixup [commit]", "Amend staged changes to a specific commit in history", (yargs) => yargs.positional("commit", FixupOptions.commit))
37362
37379
  .command("log [args...]", "Print an abbreviated log with numbered commits, useful for git stack fixup", (yargs) => yargs.strict(false))
37380
+ .command("rebase", "Update local branch via rebase with latest changes from origin master branch", (yargs) => yargs)
37363
37381
  .option("verbose", GlobalOptions.verbose)
37364
37382
  // yargs default wraps to 80 columns
37365
37383
  // passing null will wrap to terminal width
@@ -37367,7 +37385,7 @@ async function command() {
37367
37385
  .wrap(123)
37368
37386
  // disallow unknown options
37369
37387
  .strict()
37370
- .version("1.10.0" )
37388
+ .version("1.11.1" )
37371
37389
  .showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
37372
37390
  .help("help", "Show usage via `git stack help`")
37373
37391
  .argv;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "1.10.0",
3
+ "version": "1.11.1",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
package/src/app/App.tsx CHANGED
@@ -16,6 +16,7 @@ import { RebaseCheck } from "~/app/RebaseCheck";
16
16
  import { Store } from "~/app/Store";
17
17
  import { Fixup } from "~/commands/Fixup";
18
18
  import { Log } from "~/commands/Log";
19
+ import { Rebase } from "~/commands/Rebase";
19
20
 
20
21
  export function App() {
21
22
  const actions = Store.useActions();
@@ -73,6 +74,14 @@ function MaybeMain() {
73
74
  return <Fixup />;
74
75
  } else if (positional_list.has("log")) {
75
76
  return <Log />;
77
+ } else if (positional_list.has("rebase")) {
78
+ return (
79
+ <GatherMetadata>
80
+ <LocalCommitStatus>
81
+ <Rebase />
82
+ </LocalCommitStatus>
83
+ </GatherMetadata>
84
+ );
76
85
  }
77
86
 
78
87
  return (
@@ -157,6 +157,10 @@ export function DetectInitialPR(props: Props) {
157
157
  for (const group of commit_range.group_list) {
158
158
  group.id = branch_name;
159
159
  group.title = state.pr?.title || "-";
160
+ for (const commit of commit_range.commit_list) {
161
+ commit.branch_id = group.id;
162
+ commit.title = group.title;
163
+ }
160
164
  }
161
165
 
162
166
  // get latest merge_base relative to local master
@@ -1,193 +1,7 @@
1
1
  import * as React from "react";
2
2
 
3
- import fs from "node:fs";
4
-
5
- import * as Ink from "ink-cjs";
6
-
7
- import { Await } from "~/app/Await";
8
- import { Brackets } from "~/app/Brackets";
9
- import { FormatText } from "~/app/FormatText";
10
- import { Parens } from "~/app/Parens";
11
- import { Store } from "~/app/Store";
12
- import * as CommitMetadata from "~/core/CommitMetadata";
13
- import { cli } from "~/core/cli";
14
- import { colors } from "~/core/colors";
15
- import { invariant } from "~/core/invariant";
16
- import { short_id } from "~/core/short_id";
3
+ import { Rebase } from "~/commands/Rebase";
17
4
 
18
5
  export function LocalMergeRebase() {
19
- return (
20
- <Await
21
- fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>}
22
- function={run}
23
- />
24
- );
25
- }
26
-
27
- async function run() {
28
- const state = Store.getState();
29
- const actions = state.actions;
30
- const branch_name = state.branch_name;
31
- const commit_range = state.commit_range;
32
- const master_branch = state.master_branch;
33
- const cwd = state.cwd;
34
- const repo_root = state.repo_root;
35
-
36
- invariant(branch_name, "branch_name must exist");
37
- invariant(commit_range, "commit_range must exist");
38
- invariant(repo_root, "repo_root must exist");
39
-
40
- // always listen for SIGINT event and restore git state
41
- process.once("SIGINT", handle_exit);
42
-
43
- const temp_branch_name = `${branch_name}_${short_id()}`;
44
-
45
- try {
46
- // actions.debug(`commit_range=${JSON.stringify(commit_range, null, 2)}`);
47
-
48
- // must perform rebase from repo root for applying git patch
49
- process.chdir(repo_root);
50
- await cli(`pwd`);
51
-
52
- // update local master to match remote
53
- await cli(
54
- `git fetch --no-tags -v origin ${master_branch}:${master_branch}`
55
- );
56
-
57
- const master_sha = (await cli(`git rev-parse ${master_branch}`)).stdout;
58
- const rebase_merge_base = master_sha;
59
-
60
- // create temporary branch based on merge base
61
- await cli(`git checkout -b ${temp_branch_name} ${rebase_merge_base}`);
62
-
63
- const picked_commit_list = [];
64
-
65
- for (let i = 0; i < commit_range.commit_list.length; i++) {
66
- const commit = commit_range.commit_list[i];
67
- const commit_pr = commit_range.pr_lookup[commit.branch_id || ""];
68
-
69
- // drop commits that are in groups of merged PRs
70
- const merged_pr = commit_pr?.state === "MERGED";
71
-
72
- if (merged_pr) {
73
- if (actions.isDebug()) {
74
- actions.output(
75
- <FormatText
76
- wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
77
- message="Dropping {commit_message} {pr_status}"
78
- values={{
79
- commit_message: <Brackets>{commit.subject_line}</Brackets>,
80
- pr_status: <Parens>MERGED</Parens>,
81
- }}
82
- />
83
- );
84
- }
85
-
86
- continue;
87
- }
88
-
89
- if (actions.isDebug()) {
90
- actions.output(
91
- <FormatText
92
- wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
93
- message="Picking {commit_message}"
94
- values={{
95
- commit_message: <Brackets>{commit.subject_line}</Brackets>,
96
- }}
97
- />
98
- );
99
- }
100
-
101
- picked_commit_list.push(commit);
102
- }
103
-
104
- if (picked_commit_list.length > 0) {
105
- // ensure clean base to avoid conflicts when applying patch
106
- await cli(`git clean -fd`);
107
-
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}`);
112
- }
113
-
114
- // after all commits have been cherry-picked and amended
115
- // move the branch pointer to the newly created temporary branch
116
- // now we are locally in sync with github and on the original branch
117
- await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
118
-
119
- restore_git();
120
-
121
- const next_commit_range = await CommitMetadata.range();
122
-
123
- actions.set((state) => {
124
- state.commit_range = next_commit_range;
125
- state.step = "status";
126
- });
127
- } catch (err) {
128
- actions.error("Unable to rebase.");
129
-
130
- if (err instanceof Error) {
131
- if (actions.isDebug()) {
132
- actions.error(err.message);
133
- }
134
- }
135
-
136
- handle_exit();
137
- }
138
-
139
- // cleanup git operations if cancelled during manual rebase
140
- function restore_git() {
141
- // signint handler MUST run synchronously
142
- // trying to use `await cli(...)` here will silently fail since
143
- // all children processes receive the SIGINT signal
144
- const spawn_options = { ignoreExitCode: true };
145
-
146
- // always clean up any patch files
147
- cli.sync(`rm ${PATCH_FILE}`, spawn_options);
148
-
149
- // always hard reset and clean to allow subsequent checkout
150
- // if there are files checkout will fail and cascade fail subsequent commands
151
- cli.sync(`git reset --hard`, spawn_options);
152
- cli.sync(`git clean -df`, spawn_options);
153
-
154
- // always put self back in original branch
155
- cli.sync(`git checkout ${branch_name}`, spawn_options);
156
-
157
- // ...and cleanup temporary branch
158
- cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
159
-
160
- if (commit_range) {
161
- // ...and cleanup pr group branches
162
- for (const group of commit_range.group_list) {
163
- cli.sync(`git branch -D ${group.id}`, spawn_options);
164
- }
165
- }
166
-
167
- // restore back to original dir
168
- if (fs.existsSync(cwd)) {
169
- process.chdir(cwd);
170
- }
171
- cli.sync(`pwd`, spawn_options);
172
- }
173
-
174
- function handle_exit() {
175
- actions.output(
176
- <Ink.Text color={colors.yellow}>
177
- Restoring <Brackets>{branch_name}</Brackets>…
178
- </Ink.Text>
179
- );
180
-
181
- restore_git();
182
-
183
- actions.output(
184
- <Ink.Text color={colors.yellow}>
185
- Restored <Brackets>{branch_name}</Brackets>.
186
- </Ink.Text>
187
- );
188
-
189
- actions.exit(6);
190
- }
6
+ return <Rebase />;
191
7
  }
192
-
193
- const PATCH_FILE = "git-stack-cli-patch.patch";
package/src/command.ts CHANGED
@@ -27,6 +27,12 @@ export async function command() {
27
27
  (yargs) => yargs.strict(false)
28
28
  )
29
29
 
30
+ .command(
31
+ "rebase",
32
+ "Update local branch via rebase with latest changes from origin master branch",
33
+ (yargs) => yargs
34
+ )
35
+
30
36
  .option("verbose", GlobalOptions.verbose)
31
37
 
32
38
  // yargs default wraps to 80 columns
@@ -0,0 +1,204 @@
1
+ import * as React from "react";
2
+
3
+ import fs from "node:fs";
4
+
5
+ import * as Ink from "ink-cjs";
6
+
7
+ import { Await } from "~/app/Await";
8
+ import { Brackets } from "~/app/Brackets";
9
+ import { FormatText } from "~/app/FormatText";
10
+ import { Parens } from "~/app/Parens";
11
+ import { Store } from "~/app/Store";
12
+ import * as CommitMetadata from "~/core/CommitMetadata";
13
+ import { cli } from "~/core/cli";
14
+ import { colors } from "~/core/colors";
15
+ import { invariant } from "~/core/invariant";
16
+ import { short_id } from "~/core/short_id";
17
+
18
+ export function Rebase() {
19
+ return (
20
+ <Await
21
+ function={Rebase.run}
22
+ fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>}
23
+ />
24
+ );
25
+ }
26
+
27
+ Rebase.run = async function run() {
28
+ const state = Store.getState();
29
+ const actions = state.actions;
30
+ const branch_name = state.branch_name;
31
+ const commit_range = state.commit_range;
32
+ const master_branch = state.master_branch;
33
+ const cwd = state.cwd;
34
+ const repo_root = state.repo_root;
35
+
36
+ invariant(branch_name, "branch_name must exist");
37
+ invariant(commit_range, "commit_range must exist");
38
+ invariant(repo_root, "repo_root must exist");
39
+
40
+ // always listen for SIGINT event and restore git state
41
+ process.once("SIGINT", handle_exit);
42
+
43
+ const temp_branch_name = `${branch_name}_${short_id()}`;
44
+
45
+ try {
46
+ // actions.debug(`commit_range=${JSON.stringify(commit_range, null, 2)}`);
47
+
48
+ // must perform rebase from repo root for applying git patch
49
+ process.chdir(repo_root);
50
+ await cli(`pwd`);
51
+
52
+ // update local master to match remote
53
+ await cli(
54
+ `git fetch --no-tags -v origin ${master_branch}:${master_branch}`
55
+ );
56
+
57
+ const master_sha = (await cli(`git rev-parse ${master_branch}`)).stdout;
58
+ const rebase_merge_base = master_sha;
59
+
60
+ // create temporary branch based on merge base
61
+ await cli(`git checkout -b ${temp_branch_name} ${rebase_merge_base}`);
62
+
63
+ const picked_commit_list = [];
64
+
65
+ for (let i = 0; i < commit_range.commit_list.length; i++) {
66
+ const commit = commit_range.commit_list[i];
67
+ const commit_pr = commit_range.pr_lookup[commit.branch_id || ""];
68
+
69
+ // drop commits that are in groups of merged PRs
70
+ const merged_pr = commit_pr?.state === "MERGED";
71
+
72
+ if (merged_pr) {
73
+ if (actions.isDebug()) {
74
+ actions.output(
75
+ <FormatText
76
+ wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
77
+ message="Dropping {commit_message} {pr_status}"
78
+ values={{
79
+ commit_message: <Brackets>{commit.subject_line}</Brackets>,
80
+ pr_status: <Parens>MERGED</Parens>,
81
+ }}
82
+ />
83
+ );
84
+ }
85
+
86
+ continue;
87
+ }
88
+
89
+ if (actions.isDebug()) {
90
+ actions.output(
91
+ <FormatText
92
+ wrapper={<Ink.Text color={colors.yellow} wrap="truncate-end" />}
93
+ message="Picking {commit_message}"
94
+ values={{
95
+ commit_message: <Brackets>{commit.subject_line}</Brackets>,
96
+ }}
97
+ />
98
+ );
99
+ }
100
+
101
+ picked_commit_list.push(commit);
102
+ }
103
+
104
+ if (picked_commit_list.length > 0) {
105
+ // ensure clean base to avoid conflicts when applying patch
106
+ await cli(`git clean -fd`);
107
+
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}`);
112
+ }
113
+
114
+ // after all commits have been cherry-picked and amended
115
+ // move the branch pointer to the newly created temporary branch
116
+ // now we are locally in sync with github and on the original branch
117
+ await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
118
+
119
+ restore_git();
120
+
121
+ const next_commit_range = await CommitMetadata.range();
122
+
123
+ actions.output(
124
+ <FormatText
125
+ wrapper={<Ink.Text color={colors.green} />}
126
+ message="✅ {branch_name} in sync with {origin_branch}"
127
+ values={{
128
+ branch_name: <Brackets>{branch_name}</Brackets>,
129
+ origin_branch: <Brackets>{`origin/${master_branch}`}</Brackets>,
130
+ }}
131
+ />
132
+ );
133
+
134
+ actions.set((state) => {
135
+ state.commit_range = next_commit_range;
136
+ state.step = "status";
137
+ });
138
+ } catch (err) {
139
+ actions.error("Unable to rebase.");
140
+
141
+ if (err instanceof Error) {
142
+ if (actions.isDebug()) {
143
+ actions.error(err.message);
144
+ }
145
+ }
146
+
147
+ handle_exit();
148
+ }
149
+
150
+ // cleanup git operations if cancelled during manual rebase
151
+ function restore_git() {
152
+ // signint handler MUST run synchronously
153
+ // trying to use `await cli(...)` here will silently fail since
154
+ // all children processes receive the SIGINT signal
155
+ const spawn_options = { ignoreExitCode: true };
156
+
157
+ // always clean up any patch files
158
+ cli.sync(`rm ${PATCH_FILE}`, spawn_options);
159
+
160
+ // always hard reset and clean to allow subsequent checkout
161
+ // if there are files checkout will fail and cascade fail subsequent commands
162
+ cli.sync(`git reset --hard`, spawn_options);
163
+ cli.sync(`git clean -df`, spawn_options);
164
+
165
+ // always put self back in original branch
166
+ cli.sync(`git checkout ${branch_name}`, spawn_options);
167
+
168
+ // ...and cleanup temporary branch
169
+ cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
170
+
171
+ if (commit_range) {
172
+ // ...and cleanup pr group branches
173
+ for (const group of commit_range.group_list) {
174
+ cli.sync(`git branch -D ${group.id}`, spawn_options);
175
+ }
176
+ }
177
+
178
+ // restore back to original dir
179
+ if (fs.existsSync(cwd)) {
180
+ process.chdir(cwd);
181
+ }
182
+ cli.sync(`pwd`, spawn_options);
183
+ }
184
+
185
+ function handle_exit() {
186
+ actions.output(
187
+ <Ink.Text color={colors.yellow}>
188
+ Restoring <Brackets>{branch_name}</Brackets>…
189
+ </Ink.Text>
190
+ );
191
+
192
+ restore_git();
193
+
194
+ actions.output(
195
+ <Ink.Text color={colors.yellow}>
196
+ Restored <Brackets>{branch_name}</Brackets>.
197
+ </Ink.Text>
198
+ );
199
+
200
+ actions.exit(6);
201
+ }
202
+ };
203
+
204
+ const PATCH_FILE = "git-stack-cli-patch.patch";