notes.c: introduce "--[no-]stripspace" option

This commit introduces a new option "--[no-]stripspace" to git notes append, git notes edit, and git notes add. This option allows users to control whether the note message need to stripped out. For the consideration of backward compatibility, let's look at the behavior about "stripspace" in "git notes" command: 1. "Edit Message" case: using the default editor to edit the note message. In "edit" case, the edited message will always be stripped out, the implementation which can be found in the "prepare_note_data()". In addition, the "-c" option supports to reuse an existing blob as a note message, then open the editor to make a further edition on it, the edited message will be stripped. This commit doesn't change the default behavior of "edit" case by using an enum "notes_stripspace", only when "--no-stripspace" option is specified, the note message will not be stripped out. If you do not specify the option or you specify "--stripspace", clearly, the note message will be stripped out. 2. "Assign Message" case: using the "-m"/"-F"/"-C" option to specify the note message. In "assign" case, when specify message by "-m" or "-F", the message will be stripped out by default, but when specify message by "-C", the message will be copied verbatim, in other word, the message will not be stripped out. One more thing need to note is "the order of the options matter", that is, if you specify "-C" before "-m" or "-F", the reused message by "-C" will be stripped out together, because everytime concat "-m" or "-F" message, the concated message will be stripped together. Oppositely, if you specify "-m" or "-F" before "-C", the reused message by "-C" will not be stripped out. This commit doesn't change the default behavior of "assign" case by extending the "stripspace" field in "struct note_msg", so we can distinguish the different behavior of "-m"/"-F" and "-C" options when we need to parse and concat the message. Signed-off-by: Teng Long <dyroneteng@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Teng Long committed May 27, 2023 at 15:57 UTC c4e2aa7d45fadf89f6c7d7cda109eccc735a65f8
3 files changed +332 -19
Documentation/git-notes.txt
+19 -6
@@ -9,10 +9,10 @@ SYNOPSIS
9 --------
10 [verse]
11 'git notes' [list [<object>]]
12 -'git notes' add [-f] [--allow-empty] [--separator=<paragraph-break>] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
12 +'git notes' add [-f] [--allow-empty] [--separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
13 'git notes' copy [-f] ( --stdin | <from-object> [<to-object>] )
14 -'git notes' append [--allow-empty] [--separator=<paragraph-break>] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
15 -'git notes' edit [--allow-empty] [<object>]
14 +'git notes' append [--allow-empty] [--separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
15 +'git notes' edit [--allow-empty] [<object>] [--[no-]stripspace]
16 'git notes' show [<object>]
17 'git notes' merge [-v | -q] [-s <strategy> ] <notes-ref>
18 'git notes' merge --commit [-v | -q]
@@ -141,20 +141,26 @@ OPTIONS
141 If multiple `-m` options are given, their values
142 are concatenated as separate paragraphs.
143 Lines starting with `#` and empty lines other than a
144 - single line between paragraphs will be stripped out.
144 + single line between paragraphs will be stripped out,
145 + if you wish to keep them verbatim, use `--no-stripspace`.
146
147 -F <file>::
148 --file=<file>::
149 Take the note message from the given file. Use '-' to
150 read the note message from the standard input.
151 Lines starting with `#` and empty lines other than a
151 - single line between paragraphs will be stripped out.
152 + single line between paragraphs will be stripped out,
153 + if you wish to keep them verbatim, use with
154 + `--no-stripspace` option.
155
156 -C <object>::
157 --reuse-message=<object>::
158 Take the given blob object (for example, another note) as the
159 note message. (Use `git notes copy <object>` instead to
157 - copy notes between objects.)
160 + copy notes between objects.). By default, message will be
161 + copied verbatim, but if you wish to strip out the lines
162 + starting with `#` and empty lines other than a single line
163 + between paragraphs, use with`--stripspace` option.
164
165 -c <object>::
166 --reedit-message=<object>::
@@ -170,6 +176,13 @@ OPTIONS
176 (a newline is added at the end as needed). Defaults to a
177 blank line.
178
179 +--[no-]stripspace::
180 + Strip leading and trailing whitespace from the note message.
181 + Also strip out empty lines other than a single line between
182 + paragraphs. For lines starting with `#` will be stripped out
183 + in non-editor cases like "-m", "-F" and "-C", but not in
184 + editor case like "git notes edit", "-c", etc.
185 +
186 --ref <ref>::
187 Manipulate the notes tree in <ref>. This overrides
188 `GIT_NOTES_REF` and the "core.notesRef" configuration. The ref
builtin/notes.c
+22 -8
@@ -101,14 +101,21 @@ static const char * const git_notes_get_ref_usage[] = {
101 static const char note_template[] =
102 N_("Write/edit the notes for the following object:");
103
104 +enum notes_stripspace {
105 + UNSPECIFIED = -1,
106 + NO_STRIPSPACE = 0,
107 + STRIPSPACE = 1,
108 +};
109 +
110 struct note_msg {
105 - int stripspace;
111 + enum notes_stripspace stripspace;
112 struct strbuf buf;
113 };
114
115 struct note_data {
116 int given;
117 int use_editor;
118 + int stripspace;
119 char *edit_path;
120 struct strbuf buf;
121 struct note_msg **messages;
@@ -213,7 +220,8 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
220 if (launch_editor(d->edit_path, &d->buf, NULL)) {
221 die(_("please supply the note contents using either -m or -F option"));
222 }
216 - strbuf_stripspace(&d->buf, 1);
223 + if (d->stripspace)
224 + strbuf_stripspace(&d->buf, 1);
225 }
226 }
227
@@ -248,7 +256,9 @@ static void concat_messages(struct note_data *d)
256 append_separator(&d->buf);
257 strbuf_add(&msg, d->messages[i]->buf.buf, d->messages[i]->buf.len);
258 strbuf_addbuf(&d->buf, &msg);
251 - if (d->messages[i]->stripspace)
259 + if ((d->stripspace == UNSPECIFIED &&
260 + d->messages[i]->stripspace == STRIPSPACE) ||
261 + d->stripspace == STRIPSPACE)
262 strbuf_stripspace(&d->buf, 0);
263 strbuf_reset(&msg);
264 }
@@ -266,7 +276,7 @@ static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
276 strbuf_addstr(&msg->buf, arg);
277 ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
278 d->messages[d->msg_nr - 1] = msg;
269 - msg->stripspace = 1;
279 + msg->stripspace = STRIPSPACE;
280 return 0;
281 }
282
@@ -286,7 +296,7 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset)
296
297 ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
298 d->messages[d->msg_nr - 1] = msg;
289 - msg->stripspace = 1;
299 + msg->stripspace = STRIPSPACE;
300 return 0;
301 }
302
@@ -319,7 +329,7 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
329 msg->buf.len = len;
330 ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
331 d->messages[d->msg_nr - 1] = msg;
322 - msg->stripspace = 0;
332 + msg->stripspace = NO_STRIPSPACE;
333 return 0;
334 }
335
@@ -453,7 +463,7 @@ static int add(int argc, const char **argv, const char *prefix)
463 struct notes_tree *t;
464 struct object_id object, new_note;
465 const struct object_id *note;
456 - struct note_data d = { .buf = STRBUF_INIT };
466 + struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
467
468 struct option options[] = {
469 OPT_CALLBACK_F('m', "message", &d, N_("message"),
@@ -473,6 +483,8 @@ static int add(int argc, const char **argv, const char *prefix)
483 OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
484 OPT_STRING(0, "separator", &separator, N_("separator"),
485 N_("insert <paragraph-break> between paragraphs")),
486 + OPT_BOOL(0, "stripspace", &d.stripspace,
487 + N_("remove unnecessary whitespace")),
488 OPT_END()
489 };
490
@@ -626,7 +638,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
638 const struct object_id *note;
639 char *logmsg;
640 const char * const *usage;
629 - struct note_data d = { .buf = STRBUF_INIT };
641 + struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
642 struct option options[] = {
643 OPT_CALLBACK_F('m', "message", &d, N_("message"),
644 N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -644,6 +656,8 @@ static int append_edit(int argc, const char **argv, const char *prefix)
656 N_("allow storing empty note")),
657 OPT_STRING(0, "separator", &separator, N_("separator"),
658 N_("insert <paragraph-break> between paragraphs")),
659 + OPT_BOOL(0, "stripspace", &d.stripspace,
660 + N_("remove unnecessary whitespace")),
661 OPT_END()
662 };
663 int edit = !strcmp(argv[0], "edit");
t/t3321-notes-stripspace.sh
+291 -5
@@ -32,7 +32,7 @@ test_expect_success 'add note by editor' '
32 test_cmp expect actual
33 '
34
35 -test_expect_success 'add note by specifying single "-m"' '
35 +test_expect_success 'add note by specifying single "-m", "--stripspace" is the default behavior' '
36 test_when_finished "git notes remove" &&
37 cat >expect <<-EOF &&
38 first-line
@@ -42,10 +42,26 @@ test_expect_success 'add note by specifying single "-m"' '
42
43 git notes add -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
44 git notes show >actual &&
45 + test_cmp expect actual &&
46 + git notes remove &&
47 + git notes add --stripspace -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
48 + git notes show >actual &&
49 test_cmp expect actual
50 '
51
48 -test_expect_success 'add note by specifying multiple "-m"' '
52 +test_expect_success 'add note by specifying single "-m" and "--no-stripspace" ' '
53 + test_when_finished "git notes remove" &&
54 + cat >expect <<-EOF &&
55 + ${LF}first-line${MULTI_LF}second-line
56 + EOF
57 +
58 + git notes add --no-stripspace \
59 + -m "${LF}first-line${MULTI_LF}second-line${LF}" &&
60 + git notes show >actual &&
61 + test_cmp expect actual
62 +'
63 +
64 +test_expect_success 'add note by specifying multiple "-m", "--stripspace" is the default behavior' '
65 test_when_finished "git notes remove" &&
66 cat >expect <<-EOF &&
67 first-line
@@ -59,9 +75,156 @@ test_expect_success 'add note by specifying multiple "-m"' '
75 -m "second-line" \
76 -m "${LF}" &&
77 git notes show >actual &&
78 + test_cmp expect actual &&
79 + git notes remove &&
80 + git notes add --stripspace -m "${LF}" \
81 + -m "first-line" \
82 + -m "${MULTI_LF}" \
83 + -m "second-line" \
84 + -m "${LF}" &&
85 + git notes show >actual &&
86 test_cmp expect actual
87 '
88
89 +test_expect_success 'add notes by specifying multiple "-m" and "--no-stripspace"' '
90 + test_when_finished "git notes remove" &&
91 + cat >expect <<-EOF &&
92 + ${LF}
93 + first-line
94 + ${MULTI_LF}
95 + second-line${LF}
96 + EOF
97 +
98 + git notes add --no-stripspace \
99 + -m "${LF}" \
100 + -m "first-line" \
101 + -m "${MULTI_LF}" \
102 + -m "second-line" \
103 + -m "${LF}" &&
104 + git notes show >actual &&
105 + test_cmp expect actual
106 +'
107 +
108 +test_expect_success 'add note by specifying single "-F", "--stripspace" is the default behavior' '
109 + test_when_finished "git notes remove" &&
110 + cat >expect <<-EOF &&
111 + first-line
112 +
113 + second-line
114 + EOF
115 +
116 + cat >note-file <<-EOF &&
117 + ${LF}
118 + first-line
119 + ${MULTI_LF}
120 + second-line
121 + ${LF}
122 + EOF
123 +
124 + git notes add -F note-file &&
125 + git notes show >actual &&
126 + test_cmp expect actual &&
127 + git notes remove &&
128 + git notes add --stripspace -F note-file &&
129 + git notes show >actual
130 +'
131 +
132 +test_expect_success 'add note by specifying single "-F" and "--no-stripspace"' '
133 + test_when_finished "git notes remove" &&
134 + cat >expect <<-EOF &&
135 + ${LF}
136 + first-line
137 + ${MULTI_LF}
138 + second-line
139 + ${LF}
140 + EOF
141 +
142 + cat >note-file <<-EOF &&
143 + ${LF}
144 + first-line
145 + ${MULTI_LF}
146 + second-line
147 + ${LF}
148 + EOF
149 +
150 + git notes add --no-stripspace -F note-file &&
151 + git notes show >actual &&
152 + test_cmp expect actual
153 +'
154 +
155 +test_expect_success 'add note by specifying multiple "-F", "--stripspace" is the default behavior' '
156 + test_when_finished "git notes remove" &&
157 + cat >expect <<-EOF &&
158 + file-1-first-line
159 +
160 + file-1-second-line
161 +
162 + file-2-first-line
163 +
164 + file-2-second-line
165 + EOF
166 +
167 + cat >note-file-1 <<-EOF &&
168 + ${LF}
169 + file-1-first-line
170 + ${MULTI_LF}
171 + file-1-second-line
172 + ${LF}
173 + EOF
174 +
175 + cat >note-file-2 <<-EOF &&
176 + ${LF}
177 + file-2-first-line
178 + ${MULTI_LF}
179 + file-2-second-line
180 + ${LF}
181 + EOF
182 +
183 + git notes add -F note-file-1 -F note-file-2 &&
184 + git notes show >actual &&
185 + test_cmp expect actual &&
186 + git notes remove &&
187 + git notes add --stripspace -F note-file-1 -F note-file-2 &&
188 + git notes show >actual &&
189 + test_cmp expect actual
190 +'
191 +
192 +test_expect_success 'add note by specifying multiple "-F" with "--no-stripspace"' '
193 + test_when_finished "git notes remove" &&
194 + cat >expect <<-EOF &&
195 + ${LF}
196 + file-1-first-line
197 + ${MULTI_LF}
198 + file-1-second-line
199 + ${LF}
200 +
201 + ${LF}
202 + file-2-first-line
203 + ${MULTI_LF}
204 + file-2-second-line
205 + ${LF}
206 + EOF
207 +
208 + cat >note-file-1 <<-EOF &&
209 + ${LF}
210 + file-1-first-line
211 + ${MULTI_LF}
212 + file-1-second-line
213 + ${LF}
214 + EOF
215 +
216 + cat >note-file-2 <<-EOF &&
217 + ${LF}
218 + file-2-first-line
219 + ${MULTI_LF}
220 + file-2-second-line
221 + ${LF}
222 + EOF
223 +
224 + git notes add --no-stripspace -F note-file-1 -F note-file-2 &&
225 + git notes show >actual &&
226 + test_cmp expect actual
227 +'
228
229 test_expect_success 'append note by editor' '
230 test_when_finished "git notes remove" &&
@@ -221,6 +384,45 @@ test_expect_success 'append notes by specifying multiple "-F"' '
384 test_cmp expect actual
385 '
386
387 +test_expect_success 'append note by specifying multiple "-F" with "--no-stripspace"' '
388 + test_when_finished "git notes remove" &&
389 + cat >expect <<-EOF &&
390 + initial-line
391 + ${LF}${LF}
392 + file-1-first-line
393 + ${MULTI_LF}
394 + file-1-second-line
395 + ${LF}
396 +
397 + ${LF}
398 + file-2-first-line
399 + ${MULTI_LF}
400 + file-2-second-line
401 + ${LF}
402 + EOF
403 +
404 + cat >note-file-1 <<-EOF &&
405 + ${LF}
406 + file-1-first-line
407 + ${MULTI_LF}
408 + file-1-second-line
409 + ${LF}
410 + EOF
411 +
412 + cat >note-file-2 <<-EOF &&
413 + ${LF}
414 + file-2-first-line
415 + ${MULTI_LF}
416 + file-2-second-line
417 + ${LF}
418 + EOF
419 +
420 + git notes add -m "initial-line" &&
421 + git notes append --no-stripspace -F note-file-1 -F note-file-2 &&
422 + git notes show >actual &&
423 + test_cmp expect actual
424 +'
425 +
426 test_expect_success 'add notes with empty messages' '
427 rev=$(git rev-parse HEAD) &&
428 git notes add -m "${LF}" \
@@ -229,7 +431,7 @@ test_expect_success 'add notes with empty messages' '
431 test_i18ngrep "Removing note for object" actual
432 '
433
232 -test_expect_success 'add note by specifying "-C" , do not stripspace is the default behavior' '
434 +test_expect_success 'add note by specifying "-C", "--no-stripspace" is the default behavior' '
435 test_when_finished "git notes remove" &&
436 cat >expect <<-EOF &&
437 ${LF}
@@ -242,10 +444,36 @@ test_expect_success 'add note by specifying "-C" , do not stripspace is the defa
444 cat expect | git hash-object -w --stdin >blob &&
445 git notes add -C $(cat blob) &&
446 git notes show >actual &&
447 + test_cmp expect actual &&
448 + git notes remove &&
449 + git notes add --no-stripspace -C $(cat blob) &&
450 + git notes show >actual &&
451 + test_cmp expect actual
452 +'
453 +
454 +test_expect_success 'reuse note by specifying "-C" and "--stripspace"' '
455 + test_when_finished "git notes remove" &&
456 + cat >data <<-EOF &&
457 + ${LF}
458 + first-line
459 + ${MULTI_LF}
460 + second-line
461 + ${LF}
462 + EOF
463 +
464 + cat >expect <<-EOF &&
465 + first-line
466 +
467 + second-line
468 + EOF
469 +
470 + cat data | git hash-object -w --stdin >blob &&
471 + git notes add --stripspace -C $(cat blob) &&
472 + git notes show >actual &&
473 test_cmp expect actual
474 '
475
248 -test_expect_success 'add notes with "-C" and "-m", "-m" will stripspace all together' '
476 +test_expect_success 'reuse with "-C" and add note with "-m", "-m" will stripspace all together' '
477 test_when_finished "git notes remove" &&
478 cat >data <<-EOF &&
479 ${LF}
@@ -269,7 +497,7 @@ test_expect_success 'add notes with "-C" and "-m", "-m" will stripspace all toge
497 test_cmp expect actual
498 '
499
272 -test_expect_success 'add notes with "-m" and "-C", "-C" will not stripspace all together' '
500 +test_expect_success 'add note with "-m" and reuse note with "-C", "-C" will not stripspace all together' '
501 test_when_finished "git notes remove" &&
502 cat >data <<-EOF &&
503
@@ -288,4 +516,62 @@ test_expect_success 'add notes with "-m" and "-C", "-C" will not stripspace all
516 test_cmp expect actual
517 '
518
519 +test_expect_success 'add note by specifying "-c", "--stripspace" is the default behavior' '
520 + test_when_finished "git notes remove" &&
521 + cat >expect <<-EOF &&
522 + first-line
523 +
524 + second-line
525 + EOF
526 +
527 + echo "initial-line" | git hash-object -w --stdin >blob &&
528 + MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add -c $(cat blob) &&
529 + git notes show >actual &&
530 + test_cmp expect actual &&
531 + git notes remove &&
532 + MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --stripspace -c $(cat blob) &&
533 + git notes show >actual &&
534 + test_cmp expect actual
535 +'
536 +
537 +test_expect_success 'add note by specifying "-c" with "--no-stripspace"' '
538 + test_when_finished "git notes remove" &&
539 + cat >expect <<-EOF &&
540 + ${LF}first-line${MULTI_LF}second-line${LF}
541 + EOF
542 +
543 + echo "initial-line" | git hash-object -w --stdin >blob &&
544 + MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace -c $(cat blob) &&
545 + git notes show >actual &&
546 + test_cmp expect actual
547 +'
548 +
549 +test_expect_success 'edit note by specifying "-c", "--stripspace" is the default behavior' '
550 + test_when_finished "git notes remove" &&
551 + cat >expect <<-EOF &&
552 + first-line
553 +
554 + second-line
555 + EOF
556 +
557 + MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit &&
558 + git notes show >actual &&
559 + test_cmp expect actual &&
560 + git notes remove &&
561 + MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes edit --stripspace &&
562 + git notes show >actual &&
563 + test_cmp expect actual
564 +'
565 +
566 +test_expect_success 'edit note by specifying "-c" with "--no-stripspace"' '
567 + test_when_finished "git notes remove" &&
568 + cat >expect <<-EOF &&
569 + ${LF}first-line${MULTI_LF}second-line${LF}
570 + EOF
571 +
572 + MSG="${LF}first-line${MULTI_LF}second-line${LF}" git notes add --no-stripspace &&
573 + git notes show >actual &&
574 + test_cmp expect actual
575 +'
576 +
577 test_done