git-stack-cli 1.3.0 → 1.3.2

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.
@@ -27128,29 +27128,37 @@ const RE$2 = {
27128
27128
  all_double_quote: /"/g,
27129
27129
  };
27130
27130
 
27131
- function write$1(message, stack_id) {
27131
+ function write$1(message, values) {
27132
27132
  let result = message;
27133
27133
  // escape double-quote for cli
27134
27134
  result = safe_quote(result);
27135
27135
  // remove any previous metadata lines
27136
27136
  result = remove(result);
27137
- const line_list = [result, "", TEMPLATE$1.stack_id(stack_id)];
27137
+ const line_list = [result, "", TEMPLATE$1.stack_id(values.id)];
27138
+ if (values.title) {
27139
+ line_list.push(TEMPLATE$1.group_title(values.title));
27140
+ }
27138
27141
  const new_message = line_list.join("\n");
27139
27142
  return new_message;
27140
27143
  }
27141
27144
  function read(message) {
27142
- const match = message.match(RE$1.stack_id);
27143
- if (!match?.groups) {
27144
- return null;
27145
+ const values = { id: null, title: null };
27146
+ const match_id = message.match(RE$1.stack_id);
27147
+ if (match_id?.groups) {
27148
+ values.id = match_id.groups["id"];
27149
+ invariant(values.id, "id must exist");
27145
27150
  }
27146
- const id = match.groups["id"];
27147
- invariant(id, "id must exist");
27148
- return id;
27151
+ const match_title = message.match(RE$1.group_title);
27152
+ if (match_title?.groups) {
27153
+ values.title = match_title.groups["title"];
27154
+ }
27155
+ return values;
27149
27156
  }
27150
27157
  function remove(message) {
27151
27158
  let result = message;
27152
27159
  // remove metadata
27153
27160
  result = result.replace(new RegExp(RE$1.stack_id, "gmi"), "");
27161
+ result = result.replace(new RegExp(RE$1.group_title, "gmi"), "");
27154
27162
  result = result.trimEnd();
27155
27163
  return result;
27156
27164
  }
@@ -27158,9 +27166,13 @@ const TEMPLATE$1 = {
27158
27166
  stack_id(id) {
27159
27167
  return `git-stack-id: ${id}`;
27160
27168
  },
27169
+ group_title(title) {
27170
+ return `git-stack-title: ${title}`;
27171
+ },
27161
27172
  };
27162
27173
  const RE$1 = {
27163
27174
  stack_id: new RegExp(TEMPLATE$1.stack_id("(?<id>[a-z0-9-+=]+)"), "i"),
27175
+ group_title: new RegExp(TEMPLATE$1.group_title("(?<title>[^\\n^\\r]+)"), "i"),
27164
27176
  };
27165
27177
 
27166
27178
  // prettier-ignore
@@ -27282,7 +27294,7 @@ async function range(commit_group_map) {
27282
27294
  const group_map = new Map();
27283
27295
  for (const commit of commit_list) {
27284
27296
  let id = commit.branch_id;
27285
- let title = id;
27297
+ let title = commit.title || id;
27286
27298
  // use commit map if provided (via select commit ranges)
27287
27299
  if (commit_group_map) {
27288
27300
  const group = commit_group_map[commit.sha];
@@ -27409,13 +27421,16 @@ async function get_commit_list() {
27409
27421
  }
27410
27422
  async function commit(sha) {
27411
27423
  const full_message = (await cli(`git show -s --format=%B ${sha}`)).stdout;
27412
- const branch_id = await read(full_message);
27424
+ const metadata = await read(full_message);
27425
+ const branch_id = metadata?.id;
27413
27426
  const subject_line = get_subject_line(full_message);
27427
+ const title = metadata?.title;
27414
27428
  return {
27415
27429
  sha,
27416
27430
  full_message,
27417
27431
  subject_line,
27418
27432
  branch_id,
27433
+ title,
27419
27434
  };
27420
27435
  }
27421
27436
  function get_subject_line(message) {
@@ -27582,7 +27597,8 @@ async function run$3() {
27582
27597
  await cli(`git add --all`);
27583
27598
  let new_message;
27584
27599
  if (commit.branch_id) {
27585
- new_message = write$1(commit.full_message, commit.branch_id);
27600
+ const metadata = { id: commit.branch_id };
27601
+ new_message = write$1(commit.full_message, metadata);
27586
27602
  }
27587
27603
  else {
27588
27604
  new_message = commit.full_message;
@@ -27716,7 +27732,8 @@ function GitReviseTodo(args) {
27716
27732
  const group = group_list[i];
27717
27733
  for (const commit of group.commits) {
27718
27734
  // update git commit message with stack id
27719
- const message_with_id = write$1(commit.full_message, group.id);
27735
+ const metadata = { id: group.id, title: group.title };
27736
+ const message_with_id = write$1(commit.full_message, metadata);
27720
27737
  // get first 12 characters of commit sha
27721
27738
  const sha = commit.sha.slice(0, 12);
27722
27739
  // generate git revise entry
@@ -27999,7 +28016,8 @@ echo "$GIT_REVISE_TODO" > "$git_revise_todo_path"
27999
28016
  await cli(`rm ${PATCH_FILE}`);
28000
28017
  // add all changes to stage
28001
28018
  await cli(`git add --all`);
28002
- const new_message = write$1(commit.full_message, group.id);
28019
+ const metadata = { id: group.id, title: group.title };
28020
+ const new_message = write$1(commit.full_message, metadata);
28003
28021
  const git_commit_comand = [`git commit -m "${new_message}"`];
28004
28022
  if (argv.verify === false) {
28005
28023
  git_commit_comand.push("--no-verify");
@@ -28070,6 +28088,9 @@ echo "$GIT_REVISE_TODO" > "$git_revise_todo_path"
28070
28088
  }
28071
28089
  }
28072
28090
  async function update_pr_tables(pr_url_list) {
28091
+ if (!argv.sync) {
28092
+ return;
28093
+ }
28073
28094
  for (let i = 0; i < commit_range.group_list.length; i++) {
28074
28095
  const group = commit_range.group_list[i];
28075
28096
  // use the updated pr_url_list to get the actual selected_url
@@ -28575,6 +28596,7 @@ function SelectCommitRanges() {
28575
28596
  }
28576
28597
  function SelectCommitRangesInternal(props) {
28577
28598
  const actions = Store.useActions();
28599
+ const argv = Store.useState((state) => state.argv);
28578
28600
  const [selected_group_id, set_selected_group_id] = reactExports.useState(props.commit_range.UNASSIGNED);
28579
28601
  const [group_input, set_group_input] = reactExports.useState(false);
28580
28602
  const [new_group_list, create_group] = reactExports.useReducer((group_list, group) => {
@@ -28616,7 +28638,7 @@ function SelectCommitRangesInternal(props) {
28616
28638
  }
28617
28639
  group_list.push({
28618
28640
  id: group.id,
28619
- title: group.pr?.title || group.id,
28641
+ title: group.pr?.title || group.title || group.id,
28620
28642
  });
28621
28643
  }
28622
28644
  let current_index = group_list.findIndex((g) => g.id === selected_group_id);
@@ -28720,13 +28742,17 @@ function SelectCommitRangesInternal(props) {
28720
28742
  create: (reactExports.createElement(Text, { bold: true, color: colors.green },
28721
28743
  reactExports.createElement(Parens, null, "c"),
28722
28744
  "reate")),
28723
- } })) : (reactExports.createElement(reactExports.Fragment, null,
28724
- reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, null), message: "\uD83C\uDF89 Done! Press {s} to {sync} the commits to Github", values: {
28725
- s: (reactExports.createElement(Text, { bold: true, color: colors.green }, "s")),
28726
- sync: (reactExports.createElement(Text, { bold: true, color: colors.green },
28727
- reactExports.createElement(Parens, null, "s"),
28728
- "ync")),
28729
- } }))),
28745
+ } })) : (reactExports.createElement(reactExports.Fragment, null, argv.sync ? (reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, null), message: "\uD83C\uDF89 Done! Press {s} to {sync} the commits to Github", values: {
28746
+ s: (reactExports.createElement(Text, { bold: true, color: colors.green }, "s")),
28747
+ sync: (reactExports.createElement(Text, { bold: true, color: colors.green },
28748
+ reactExports.createElement(Parens, null, "s"),
28749
+ "ync")),
28750
+ } })) : (reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, null), message: "\uD83C\uDF89 Done! Press {s} to {save} the commits locally", values: {
28751
+ s: (reactExports.createElement(Text, { bold: true, color: colors.green }, "s")),
28752
+ save: (reactExports.createElement(Text, { bold: true, color: colors.green },
28753
+ reactExports.createElement(Parens, null, "s"),
28754
+ "save")),
28755
+ } })))),
28730
28756
  !group_input ? null : (reactExports.createElement(reactExports.Fragment, null,
28731
28757
  reactExports.createElement(Box, { height: 1 }),
28732
28758
  reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.gray }), message: "Enter a title for the PR {note}", values: {
@@ -28784,13 +28810,6 @@ async function run() {
28784
28810
  needs_rebase = true;
28785
28811
  }
28786
28812
  }
28787
- for (let i = 0; i < commit_range.commit_list.length; i++) {
28788
- const commit = commit_range.commit_list[i];
28789
- const commit_pr = commit_range.pr_lookup[commit.branch_id || ""];
28790
- if (commit.branch_id && !commit_pr) {
28791
- needs_rebase = true;
28792
- }
28793
- }
28794
28813
  if (argv.check) {
28795
28814
  actions.exit(0);
28796
28815
  }
@@ -34316,7 +34335,7 @@ async function command() {
34316
34335
  .wrap(123)
34317
34336
  // disallow unknown options
34318
34337
  .strict()
34319
- .version("1.3.0" )
34338
+ .version("1.3.2" )
34320
34339
  .showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
34321
34340
  .help("help", "Show usage via `git stack help`").argv);
34322
34341
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
@@ -52,7 +52,7 @@ for (const filepath of package_json.files) {
52
52
  process.env.GS_RELEASE_NPM = "true";
53
53
  console.info("Publishing to NPM requires a one-time password");
54
54
  const otp = await input("Enter OTP: ");
55
- await spawn(`npm publish --otp=${otp}`);
55
+ await spawn(["npm", "publish", `--otp=${otp}`]);
56
56
 
57
57
  process.chdir(PROJECT_DIR);
58
58
 
@@ -70,34 +70,6 @@ console.debug();
70
70
  console.debug("https://www.npmjs.com/package/git-stack-cli");
71
71
  console.debug();
72
72
 
73
- // // https://github.com/magus/git-stack-cli/releases/download/0.8.9/git-stack-cli-linux
74
- // async function create_asset(name: string) {
75
- // const sha256_cmd = await spawn.sync(`shasum -a 256 ${name}`);
76
- // const match = sha256_cmd.stdout.match(/(?<sha256>[^\s]+)/i);
77
-
78
- // if (!match?.groups) {
79
- // throw new Error(`unable to get sha256 for ${name}`);
80
- // }
81
-
82
- // const sha256 = match.groups.sha256;
83
-
84
- // const url = `https://github.com/magus/git-stack-cli/releases/download/${version}/${name}`;
85
-
86
- // return { name, sha256, url };
87
- // }
88
-
89
- // function reFind(list: Array<string>, re: RegExp) {
90
- // for (const str of list) {
91
- // const match = str.match(re);
92
-
93
- // if (match?.groups) {
94
- // return match.groups.value;
95
- // }
96
- // }
97
-
98
- // return null;
99
- // }
100
-
101
73
  async function input(prompt: string) {
102
74
  process.stdout.write(prompt);
103
75
  for await (const value of console) {
@@ -112,7 +112,8 @@ async function run() {
112
112
 
113
113
  let new_message;
114
114
  if (commit.branch_id) {
115
- new_message = Metadata.write(commit.full_message, commit.branch_id);
115
+ const metadata = { id: commit.branch_id };
116
+ new_message = Metadata.write(commit.full_message, metadata);
116
117
  } else {
117
118
  new_message = commit.full_message;
118
119
  }
@@ -241,7 +241,8 @@ async function run() {
241
241
  // add all changes to stage
242
242
  await cli(`git add --all`);
243
243
 
244
- const new_message = Metadata.write(commit.full_message, group.id);
244
+ const metadata = { id: group.id, title: group.title };
245
+ const new_message = Metadata.write(commit.full_message, metadata);
245
246
  const git_commit_comand = [`git commit -m "${new_message}"`];
246
247
 
247
248
  if (argv.verify === false) {
@@ -342,6 +343,10 @@ async function run() {
342
343
  }
343
344
 
344
345
  async function update_pr_tables(pr_url_list: Array<string>) {
346
+ if (!argv.sync) {
347
+ return;
348
+ }
349
+
345
350
  for (let i = 0; i < commit_range.group_list.length; i++) {
346
351
  const group = commit_range.group_list[i];
347
352
 
@@ -33,6 +33,8 @@ type SimpleGroup = { id: string; title: string };
33
33
  function SelectCommitRangesInternal(props: Props) {
34
34
  const actions = Store.useActions();
35
35
 
36
+ const argv = Store.useState((state) => state.argv);
37
+
36
38
  const [selected_group_id, set_selected_group_id] = React.useState(
37
39
  props.commit_range.UNASSIGNED
38
40
  );
@@ -99,7 +101,7 @@ function SelectCommitRangesInternal(props: Props) {
99
101
 
100
102
  group_list.push({
101
103
  id: group.id,
102
- title: group.pr?.title || group.id,
104
+ title: group.pr?.title || group.title || group.id,
103
105
  });
104
106
  }
105
107
 
@@ -259,22 +261,41 @@ function SelectCommitRangesInternal(props: Props) {
259
261
  />
260
262
  ) : (
261
263
  <React.Fragment>
262
- <FormatText
263
- wrapper={<Ink.Text />}
264
- message="🎉 Done! Press {s} to {sync} the commits to Github"
265
- values={{
266
- s: (
267
- <Ink.Text bold color={colors.green}>
268
- s
269
- </Ink.Text>
270
- ),
271
- sync: (
272
- <Ink.Text bold color={colors.green}>
273
- <Parens>s</Parens>ync
274
- </Ink.Text>
275
- ),
276
- }}
277
- />
264
+ {argv.sync ? (
265
+ <FormatText
266
+ wrapper={<Ink.Text />}
267
+ message="🎉 Done! Press {s} to {sync} the commits to Github"
268
+ values={{
269
+ s: (
270
+ <Ink.Text bold color={colors.green}>
271
+ s
272
+ </Ink.Text>
273
+ ),
274
+ sync: (
275
+ <Ink.Text bold color={colors.green}>
276
+ <Parens>s</Parens>ync
277
+ </Ink.Text>
278
+ ),
279
+ }}
280
+ />
281
+ ) : (
282
+ <FormatText
283
+ wrapper={<Ink.Text />}
284
+ message="🎉 Done! Press {s} to {save} the commits locally"
285
+ values={{
286
+ s: (
287
+ <Ink.Text bold color={colors.green}>
288
+ s
289
+ </Ink.Text>
290
+ ),
291
+ save: (
292
+ <Ink.Text bold color={colors.green}>
293
+ <Parens>s</Parens>save
294
+ </Ink.Text>
295
+ ),
296
+ }}
297
+ />
298
+ )}
278
299
  </React.Fragment>
279
300
  )}
280
301
 
@@ -36,15 +36,6 @@ async function run() {
36
36
  }
37
37
  }
38
38
 
39
- for (let i = 0; i < commit_range.commit_list.length; i++) {
40
- const commit = commit_range.commit_list[i];
41
- const commit_pr = commit_range.pr_lookup[commit.branch_id || ""];
42
-
43
- if (commit.branch_id && !commit_pr) {
44
- needs_rebase = true;
45
- }
46
- }
47
-
48
39
  if (argv.check) {
49
40
  actions.exit(0);
50
41
  } else if (needs_rebase) {
@@ -36,7 +36,7 @@ export async function range(commit_group_map?: CommitGroupMap) {
36
36
 
37
37
  for (const commit of commit_list) {
38
38
  let id = commit.branch_id;
39
- let title = id;
39
+ let title = commit.title || id;
40
40
 
41
41
  // use commit map if provided (via select commit ranges)
42
42
  if (commit_group_map) {
@@ -189,14 +189,17 @@ async function get_commit_list() {
189
189
 
190
190
  export async function commit(sha: string) {
191
191
  const full_message = (await cli(`git show -s --format=%B ${sha}`)).stdout;
192
- const branch_id = await Metadata.read(full_message);
192
+ const metadata = await Metadata.read(full_message);
193
+ const branch_id = metadata?.id;
193
194
  const subject_line = get_subject_line(full_message);
195
+ const title = metadata?.title;
194
196
 
195
197
  return {
196
198
  sha,
197
199
  full_message,
198
200
  subject_line,
199
201
  branch_id,
202
+ title,
200
203
  };
201
204
  }
202
205
 
@@ -16,21 +16,25 @@ test("git-revise-todo from commit range with single new commit", () => {
16
16
  "lemon color",
17
17
  "",
18
18
  "git-stack-id: E63ytp5dj",
19
+ "git-stack-title: lemon color",
19
20
  "",
20
21
  "++ pick d36d63499425",
21
22
  "cantaloupe color",
22
23
  "",
23
24
  "git-stack-id: E63ytp5dj",
25
+ "git-stack-title: lemon color",
24
26
  "",
25
27
  "++ pick 4f98dd3e67d0",
26
28
  "banana sweet",
27
29
  "",
28
30
  "git-stack-id: E63ytp5dj",
31
+ "git-stack-title: lemon color",
29
32
  "",
30
33
  "++ pick f143d03c723c",
31
34
  "apple sweet",
32
35
  "",
33
36
  "git-stack-id: E63ytp5dj",
37
+ "git-stack-title: lemon color",
34
38
  ].join("\n")
35
39
  );
36
40
  });
@@ -48,6 +52,7 @@ test("git-revise-todo from commit range with single new commit in new group", ()
48
52
  "apple sweet",
49
53
  "",
50
54
  "git-stack-id: 6Ak-qn+5Z",
55
+ "git-stack-title: new group",
51
56
  ].join("\n")
52
57
  );
53
58
  });
@@ -93,6 +98,7 @@ const SINGLE_COMMIT_EXISTING_GROUP: CommitMetadata.CommitRange = {
93
98
  "full_message": "banana color\n\ngit-stack-id: AAWsYx1UU",
94
99
  "subject_line": "banana color",
95
100
  "branch_id": "AAWsYx1UU",
101
+ "title": null,
96
102
  },
97
103
  ],
98
104
  },
@@ -164,24 +170,28 @@ const SINGLE_COMMIT_EXISTING_GROUP: CommitMetadata.CommitRange = {
164
170
  "full_message": "lemon color\n\ngit-stack-id: E63ytp5dj",
165
171
  "subject_line": "lemon color",
166
172
  "branch_id": "E63ytp5dj",
173
+ "title": null,
167
174
  },
168
175
  {
169
176
  "sha": "d36d63499425bb46a1e62c2c9df1a4332b13004f",
170
177
  "full_message": "cantaloupe color\n\ngit-stack-id: E63ytp5dj",
171
178
  "subject_line": "cantaloupe color",
172
179
  "branch_id": "E63ytp5dj",
180
+ "title": null,
173
181
  },
174
182
  {
175
183
  "sha": "4f98dd3e67d03b79d7a12480c7d1c2fcbd186ac5",
176
184
  "full_message": "banana sweet\n\ngit-stack-id: E63ytp5dj",
177
185
  "subject_line": "banana sweet",
178
186
  "branch_id": "E63ytp5dj",
187
+ "title": null,
179
188
  },
180
189
  {
181
190
  "sha": "f143d03c723c9f5231a81c1e12098511611898cb",
182
191
  "full_message": "apple sweet",
183
192
  "subject_line": "apple sweet",
184
193
  "branch_id": null,
194
+ "title": null,
185
195
  },
186
196
  ],
187
197
  },
@@ -192,30 +202,35 @@ const SINGLE_COMMIT_EXISTING_GROUP: CommitMetadata.CommitRange = {
192
202
  "full_message": "banana color\n\ngit-stack-id: AAWsYx1UU",
193
203
  "subject_line": "banana color",
194
204
  "branch_id": "AAWsYx1UU",
205
+ "title": null,
195
206
  },
196
207
  {
197
208
  "sha": "3cb22661ecff6c872e96ce9c40b31c824938cab7",
198
209
  "full_message": "lemon color\n\ngit-stack-id: E63ytp5dj",
199
210
  "subject_line": "lemon color",
200
211
  "branch_id": "E63ytp5dj",
212
+ "title": null,
201
213
  },
202
214
  {
203
215
  "sha": "d36d63499425bb46a1e62c2c9df1a4332b13004f",
204
216
  "full_message": "cantaloupe color\n\ngit-stack-id: E63ytp5dj",
205
217
  "subject_line": "cantaloupe color",
206
218
  "branch_id": "E63ytp5dj",
219
+ "title": null,
207
220
  },
208
221
  {
209
222
  "sha": "4f98dd3e67d03b79d7a12480c7d1c2fcbd186ac5",
210
223
  "full_message": "banana sweet\n\ngit-stack-id: E63ytp5dj",
211
224
  "subject_line": "banana sweet",
212
225
  "branch_id": "E63ytp5dj",
226
+ "title": null,
213
227
  },
214
228
  {
215
229
  "sha": "f143d03c723c9f5231a81c1e12098511611898cb",
216
230
  "full_message": "apple sweet",
217
231
  "subject_line": "apple sweet",
218
232
  "branch_id": null,
233
+ "title": null,
219
234
  },
220
235
  ],
221
236
  "pr_lookup": {
@@ -348,6 +363,7 @@ const SINGLE_COMMIT_NEW_GROUP: CommitMetadata.CommitRange = {
348
363
  "full_message": "banana color\n\ngit-stack-id: AAWsYx1UU",
349
364
  "subject_line": "banana color",
350
365
  "branch_id": "AAWsYx1UU",
366
+ "title": null,
351
367
  },
352
368
  ],
353
369
  },
@@ -419,18 +435,21 @@ const SINGLE_COMMIT_NEW_GROUP: CommitMetadata.CommitRange = {
419
435
  "full_message": "lemon color\n\ngit-stack-id: E63ytp5dj",
420
436
  "subject_line": "lemon color",
421
437
  "branch_id": "E63ytp5dj",
438
+ "title": null,
422
439
  },
423
440
  {
424
441
  "sha": "d36d63499425bb46a1e62c2c9df1a4332b13004f",
425
442
  "full_message": "cantaloupe color\n\ngit-stack-id: E63ytp5dj",
426
443
  "subject_line": "cantaloupe color",
427
444
  "branch_id": "E63ytp5dj",
445
+ "title": null,
428
446
  },
429
447
  {
430
448
  "sha": "4f98dd3e67d03b79d7a12480c7d1c2fcbd186ac5",
431
449
  "full_message": "banana sweet\n\ngit-stack-id: E63ytp5dj",
432
450
  "subject_line": "banana sweet",
433
451
  "branch_id": "E63ytp5dj",
452
+ "title": null,
434
453
  },
435
454
  ],
436
455
  },
@@ -446,6 +465,7 @@ const SINGLE_COMMIT_NEW_GROUP: CommitMetadata.CommitRange = {
446
465
  "full_message": "apple sweet",
447
466
  "subject_line": "apple sweet",
448
467
  "branch_id": null,
468
+ "title": null,
449
469
  },
450
470
  ],
451
471
  },
@@ -456,30 +476,35 @@ const SINGLE_COMMIT_NEW_GROUP: CommitMetadata.CommitRange = {
456
476
  "full_message": "banana color\n\ngit-stack-id: AAWsYx1UU",
457
477
  "subject_line": "banana color",
458
478
  "branch_id": "AAWsYx1UU",
479
+ "title": null,
459
480
  },
460
481
  {
461
482
  "sha": "3cb22661ecff6c872e96ce9c40b31c824938cab7",
462
483
  "full_message": "lemon color\n\ngit-stack-id: E63ytp5dj",
463
484
  "subject_line": "lemon color",
464
485
  "branch_id": "E63ytp5dj",
486
+ "title": null,
465
487
  },
466
488
  {
467
489
  "sha": "d36d63499425bb46a1e62c2c9df1a4332b13004f",
468
490
  "full_message": "cantaloupe color\n\ngit-stack-id: E63ytp5dj",
469
491
  "subject_line": "cantaloupe color",
470
492
  "branch_id": "E63ytp5dj",
493
+ "title": null,
471
494
  },
472
495
  {
473
496
  "sha": "4f98dd3e67d03b79d7a12480c7d1c2fcbd186ac5",
474
497
  "full_message": "banana sweet\n\ngit-stack-id: E63ytp5dj",
475
498
  "subject_line": "banana sweet",
476
499
  "branch_id": "E63ytp5dj",
500
+ "title": null,
477
501
  },
478
502
  {
479
503
  "sha": "f143d03c723c9f5231a81c1e12098511611898cb",
480
504
  "full_message": "apple sweet",
481
505
  "subject_line": "apple sweet",
482
506
  "branch_id": null,
507
+ "title": null,
483
508
  },
484
509
  ],
485
510
  "pr_lookup": {
@@ -56,7 +56,8 @@ export function GitReviseTodo(args: Args): string {
56
56
 
57
57
  for (const commit of group.commits) {
58
58
  // update git commit message with stack id
59
- const message_with_id = Metadata.write(commit.full_message, group.id);
59
+ const metadata = { id: group.id, title: group.title };
60
+ const message_with_id = Metadata.write(commit.full_message, metadata);
60
61
 
61
62
  // get first 12 characters of commit sha
62
63
  const sha = commit.sha.slice(0, 12);
@@ -11,9 +11,15 @@ test("read handles bulleted lists", () => {
11
11
  "- move logic inside if branch",
12
12
  "",
13
13
  "git-stack-id: DdKIFyufW",
14
+ "git-stack-title: saved group title",
14
15
  ].join("\n");
15
16
 
16
- expect(Metadata.read(body)).toEqual("DdKIFyufW");
17
+ const metadata = Metadata.read(body);
18
+
19
+ expect(metadata).toEqual({
20
+ id: "DdKIFyufW",
21
+ title: "saved group title",
22
+ });
17
23
  });
18
24
 
19
25
  test("write handles bulleted lists", () => {
@@ -27,7 +33,12 @@ test("write handles bulleted lists", () => {
27
33
  "git-stack-id: DdKIFyufW",
28
34
  ].join("\n");
29
35
 
30
- expect(Metadata.write(body, "abcd1234")).toEqual(
36
+ const metadata = {
37
+ id: "abcd1234",
38
+ title: "banana",
39
+ };
40
+
41
+ expect(Metadata.write(body, metadata)).toEqual(
31
42
  [
32
43
  "[feat] implement various features",
33
44
  "",
@@ -36,6 +47,30 @@ test("write handles bulleted lists", () => {
36
47
  "- move logic inside if branch",
37
48
  "",
38
49
  "git-stack-id: abcd1234",
50
+ "git-stack-title: banana",
51
+ ].join("\n")
52
+ );
53
+ });
54
+
55
+ test("removes metadata", () => {
56
+ const body = [
57
+ "[feat] implement various features",
58
+ "",
59
+ "- keyboard modality escape key",
60
+ "- centralize settings",
61
+ "- move logic inside if branch",
62
+ "",
63
+ "git-stack-id: DdKIFyufW",
64
+ "git-stack-title: this is a PR title",
65
+ ].join("\n");
66
+
67
+ expect(Metadata.remove(body)).toEqual(
68
+ [
69
+ "[feat] implement various features",
70
+ "",
71
+ "- keyboard modality escape key",
72
+ "- centralize settings",
73
+ "- move logic inside if branch",
39
74
  ].join("\n")
40
75
  );
41
76
  });
@@ -1,7 +1,17 @@
1
1
  import { invariant } from "~/core/invariant";
2
2
  import { safe_quote } from "~/core/safe_quote";
3
3
 
4
- export function write(message: string, stack_id: string) {
4
+ type InputMetadataValues = {
5
+ id: string;
6
+ title?: string;
7
+ };
8
+
9
+ type OutputMetadataValues = {
10
+ id: null | string;
11
+ title: null | string;
12
+ };
13
+
14
+ export function write(message: string, values: InputMetadataValues) {
5
15
  let result = message;
6
16
 
7
17
  // escape double-quote for cli
@@ -10,23 +20,34 @@ export function write(message: string, stack_id: string) {
10
20
  // remove any previous metadata lines
11
21
  result = remove(result);
12
22
 
13
- const line_list = [result, "", TEMPLATE.stack_id(stack_id)];
23
+ const line_list = [result, "", TEMPLATE.stack_id(values.id)];
24
+
25
+ if (values.title) {
26
+ line_list.push(TEMPLATE.group_title(values.title));
27
+ }
28
+
14
29
  const new_message = line_list.join("\n");
15
30
 
16
31
  return new_message;
17
32
  }
18
33
 
19
- export function read(message: string): null | string {
20
- const match = message.match(RE.stack_id);
34
+ export function read(message: string): OutputMetadataValues {
35
+ const values: OutputMetadataValues = { id: null, title: null };
36
+
37
+ const match_id = message.match(RE.stack_id);
21
38
 
22
- if (!match?.groups) {
23
- return null;
39
+ if (match_id?.groups) {
40
+ values.id = match_id.groups["id"];
41
+ invariant(values.id, "id must exist");
24
42
  }
25
43
 
26
- const id = match.groups["id"];
27
- invariant(id, "id must exist");
44
+ const match_title = message.match(RE.group_title);
45
+
46
+ if (match_title?.groups) {
47
+ values.title = match_title.groups["title"];
48
+ }
28
49
 
29
- return id;
50
+ return values;
30
51
  }
31
52
 
32
53
  export function remove(message: string) {
@@ -34,6 +55,7 @@ export function remove(message: string) {
34
55
 
35
56
  // remove metadata
36
57
  result = result.replace(new RegExp(RE.stack_id, "gmi"), "");
58
+ result = result.replace(new RegExp(RE.group_title, "gmi"), "");
37
59
 
38
60
  result = result.trimEnd();
39
61
 
@@ -44,8 +66,13 @@ const TEMPLATE = {
44
66
  stack_id(id: string) {
45
67
  return `git-stack-id: ${id}`;
46
68
  },
69
+
70
+ group_title(title: string) {
71
+ return `git-stack-title: ${title}`;
72
+ },
47
73
  };
48
74
 
49
75
  const RE = {
50
76
  stack_id: new RegExp(TEMPLATE.stack_id("(?<id>[a-z0-9-+=]+)"), "i"),
77
+ group_title: new RegExp(TEMPLATE.group_title("(?<title>[^\\n^\\r]+)"), "i"),
51
78
  };