git-stack-cli 1.3.1 → 1.3.3

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
@@ -27943,6 +27960,12 @@ echo "$GIT_REVISE_TODO" > "$git_revise_todo_path"
27943
27960
  `git`,
27944
27961
  `revise --edit -i ${rebase_merge_base}`,
27945
27962
  ]);
27963
+ // early return since we do not need to sync
27964
+ if (!argv.sync) {
27965
+ return;
27966
+ }
27967
+ // in order to sync we walk from rebase_group_index to HEAD
27968
+ // checking out each group and syncing to github
27946
27969
  // start from HEAD and work backward to rebase_group_index
27947
27970
  const push_group_list = [];
27948
27971
  let lookback_index = 0;
@@ -27999,7 +28022,8 @@ echo "$GIT_REVISE_TODO" > "$git_revise_todo_path"
27999
28022
  await cli(`rm ${PATCH_FILE}`);
28000
28023
  // add all changes to stage
28001
28024
  await cli(`git add --all`);
28002
- const new_message = write$1(commit.full_message, group.id);
28025
+ const metadata = { id: group.id, title: group.title };
28026
+ const new_message = write$1(commit.full_message, metadata);
28003
28027
  const git_commit_comand = [`git commit -m "${new_message}"`];
28004
28028
  if (argv.verify === false) {
28005
28029
  git_commit_comand.push("--no-verify");
@@ -28578,6 +28602,7 @@ function SelectCommitRanges() {
28578
28602
  }
28579
28603
  function SelectCommitRangesInternal(props) {
28580
28604
  const actions = Store.useActions();
28605
+ const argv = Store.useState((state) => state.argv);
28581
28606
  const [selected_group_id, set_selected_group_id] = reactExports.useState(props.commit_range.UNASSIGNED);
28582
28607
  const [group_input, set_group_input] = reactExports.useState(false);
28583
28608
  const [new_group_list, create_group] = reactExports.useReducer((group_list, group) => {
@@ -28619,7 +28644,7 @@ function SelectCommitRangesInternal(props) {
28619
28644
  }
28620
28645
  group_list.push({
28621
28646
  id: group.id,
28622
- title: group.pr?.title || group.id,
28647
+ title: group.pr?.title || group.title || group.id,
28623
28648
  });
28624
28649
  }
28625
28650
  let current_index = group_list.findIndex((g) => g.id === selected_group_id);
@@ -28723,13 +28748,17 @@ function SelectCommitRangesInternal(props) {
28723
28748
  create: (reactExports.createElement(Text, { bold: true, color: colors.green },
28724
28749
  reactExports.createElement(Parens, null, "c"),
28725
28750
  "reate")),
28726
- } })) : (reactExports.createElement(reactExports.Fragment, null,
28727
- reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, null), message: "\uD83C\uDF89 Done! Press {s} to {sync} the commits to Github", values: {
28728
- s: (reactExports.createElement(Text, { bold: true, color: colors.green }, "s")),
28729
- sync: (reactExports.createElement(Text, { bold: true, color: colors.green },
28730
- reactExports.createElement(Parens, null, "s"),
28731
- "ync")),
28732
- } }))),
28751
+ } })) : (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: {
28752
+ s: (reactExports.createElement(Text, { bold: true, color: colors.green }, "s")),
28753
+ sync: (reactExports.createElement(Text, { bold: true, color: colors.green },
28754
+ reactExports.createElement(Parens, null, "s"),
28755
+ "ync")),
28756
+ } })) : (reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, null), message: "\uD83C\uDF89 Done! Press {s} to {save} the commits locally", values: {
28757
+ s: (reactExports.createElement(Text, { bold: true, color: colors.green }, "s")),
28758
+ save: (reactExports.createElement(Text, { bold: true, color: colors.green },
28759
+ reactExports.createElement(Parens, null, "s"),
28760
+ "save")),
28761
+ } })))),
28733
28762
  !group_input ? null : (reactExports.createElement(reactExports.Fragment, null,
28734
28763
  reactExports.createElement(Box, { height: 1 }),
28735
28764
  reactExports.createElement(FormatText, { wrapper: reactExports.createElement(Text, { color: colors.gray }), message: "Enter a title for the PR {note}", values: {
@@ -28787,13 +28816,6 @@ async function run() {
28787
28816
  needs_rebase = true;
28788
28817
  }
28789
28818
  }
28790
- for (let i = 0; i < commit_range.commit_list.length; i++) {
28791
- const commit = commit_range.commit_list[i];
28792
- const commit_pr = commit_range.pr_lookup[commit.branch_id || ""];
28793
- if (commit.branch_id && !commit_pr) {
28794
- needs_rebase = true;
28795
- }
28796
- }
28797
28819
  if (argv.check) {
28798
28820
  actions.exit(0);
28799
28821
  }
@@ -34319,7 +34341,7 @@ async function command() {
34319
34341
  .wrap(123)
34320
34342
  // disallow unknown options
34321
34343
  .strict()
34322
- .version("1.3.1" )
34344
+ .version("1.3.3" )
34323
34345
  .showHidden("show-hidden", "Show hidden options via `git stack help --show-hidden`")
34324
34346
  .help("help", "Show usage via `git stack help`").argv);
34325
34347
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/magus/git-stack-cli.git"
9
+ "url": "git+https://github.com/magus/git-stack-cli.git"
10
10
  },
11
11
  "type": "module",
12
12
  "bin": {
@@ -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
  }
@@ -155,6 +155,14 @@ async function run() {
155
155
  `revise --edit -i ${rebase_merge_base}`,
156
156
  ]);
157
157
 
158
+ // early return since we do not need to sync
159
+ if (!argv.sync) {
160
+ return;
161
+ }
162
+
163
+ // in order to sync we walk from rebase_group_index to HEAD
164
+ // checking out each group and syncing to github
165
+
158
166
  // start from HEAD and work backward to rebase_group_index
159
167
  const push_group_list = [];
160
168
  let lookback_index = 0;
@@ -241,7 +249,8 @@ async function run() {
241
249
  // add all changes to stage
242
250
  await cli(`git add --all`);
243
251
 
244
- const new_message = Metadata.write(commit.full_message, group.id);
252
+ const metadata = { id: group.id, title: group.title };
253
+ const new_message = Metadata.write(commit.full_message, metadata);
245
254
  const git_commit_comand = [`git commit -m "${new_message}"`];
246
255
 
247
256
  if (argv.verify === false) {
@@ -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
  };