Skip to content

Commit 90bc19b

Browse files
dyronegitster
authored andcommitted
notes.c: introduce '--separator=<paragraph-break>' option
When adding new notes or appending to an existing notes, we will insert a blank line between the paragraphs, like: $ git notes add -m foo -m bar $ git notes show HEAD foo bar The default behavour sometimes is not enough, the user may want to use a custom delimiter between paragraphs, like when specifying '-m', '-F', '-C', '-c' options. So this commit introduce a new '--separator' option for 'git notes add' and 'git notes append', for example when executing: $ git notes add -m foo -m bar --separator="-" $ git notes show HEAD foo - bar a newline is added to the value given to --separator if it does not end with one already. So when executing: $ git notes add -m foo -m bar --separator="-" and $ export LF=" " $ git notes add -m foo -m bar --separator="-$LF" Both the two exections produce the same result. The reason we use a "strbuf" array to concat but not "string_list", is that the binary file content may contain '\0' in the middle, this will cause the corrupt result if using a string to save. Signed-off-by: Teng Long <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5958704 commit 90bc19b

File tree

3 files changed

+227
-29
lines changed

3 files changed

+227
-29
lines changed

Documentation/git-notes.txt

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ SYNOPSIS
99
--------
1010
[verse]
1111
'git notes' [list [<object>]]
12-
'git notes' add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
12+
'git notes' add [-f] [--allow-empty] [--separator=<paragraph-break>] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
1313
'git notes' copy [-f] ( --stdin | <from-object> [<to-object>] )
14-
'git notes' append [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
14+
'git notes' append [--allow-empty] [--separator=<paragraph-break>] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
1515
'git notes' edit [--allow-empty] [<object>]
1616
'git notes' show [<object>]
1717
'git notes' merge [-v | -q] [-s <strategy> ] <notes-ref>
@@ -65,7 +65,9 @@ add::
6565
However, if you're using `add` interactively (using an editor
6666
to supply the notes contents), then - instead of aborting -
6767
the existing notes will be opened in the editor (like the `edit`
68-
subcommand).
68+
subcommand). If you specify multiple `-m` and `-F`, a blank
69+
line will be inserted between the messages. Use the `--separator`
70+
option to insert other delimiters.
6971

7072
copy::
7173
Copy the notes for the first object onto the second object (defaults to
@@ -85,8 +87,12 @@ corresponding <to-object>. (The optional `<rest>` is ignored so that
8587
the command can read the input given to the `post-rewrite` hook.)
8688

8789
append::
88-
Append to the notes of an existing object (defaults to HEAD).
89-
Creates a new notes object if needed.
90+
Append new message(s) given by `-m` or `-F` options to an
91+
existing note, or add them as a new note if one does not
92+
exist, for the object (defaults to HEAD). When appending to
93+
an existing note, a blank line is added before each new
94+
message as an inter-paragraph separator. The separator can
95+
be customized with the `--separator` option.
9096

9197
edit::
9298
Edit the notes for a given object (defaults to HEAD).
@@ -159,6 +165,11 @@ OPTIONS
159165
Allow an empty note object to be stored. The default behavior is
160166
to automatically remove empty notes.
161167

168+
--separator <paragraph-break>::
169+
Specify a string used as a custom inter-paragraph separator
170+
(a newline is added at the end as needed). Defaults to a
171+
blank line.
172+
162173
--ref <ref>::
163174
Manipulate the notes tree in <ref>. This overrides
164175
`GIT_NOTES_REF` and the "core.notesRef" configuration. The ref

builtin/notes.c

Lines changed: 85 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#include "cache.h"
11+
#include "alloc.h"
1112
#include "config.h"
1213
#include "builtin.h"
1314
#include "gettext.h"
@@ -27,11 +28,12 @@
2728
#include "worktree.h"
2829
#include "write-or-die.h"
2930

31+
static char *separator = "\n";
3032
static const char * const git_notes_usage[] = {
3133
N_("git notes [--ref <notes-ref>] [list [<object>]]"),
32-
N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
34+
N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--separator=<paragraph-break>] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
3335
N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
34-
N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
36+
N_("git notes [--ref <notes-ref>] append [--allow-empty] [--separator=<paragraph-break>] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
3537
N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
3638
N_("git notes [--ref <notes-ref>] show [<object>]"),
3739
N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
@@ -99,11 +101,19 @@ static const char * const git_notes_get_ref_usage[] = {
99101
static const char note_template[] =
100102
N_("Write/edit the notes for the following object:");
101103

104+
struct note_msg {
105+
int stripspace;
106+
struct strbuf buf;
107+
};
108+
102109
struct note_data {
103110
int given;
104111
int use_editor;
105112
char *edit_path;
106113
struct strbuf buf;
114+
struct note_msg **messages;
115+
size_t msg_nr;
116+
size_t msg_alloc;
107117
};
108118

109119
static void free_note_data(struct note_data *d)
@@ -113,6 +123,12 @@ static void free_note_data(struct note_data *d)
113123
free(d->edit_path);
114124
}
115125
strbuf_release(&d->buf);
126+
127+
while (d->msg_nr--) {
128+
strbuf_release(&d->messages[d->msg_nr]->buf);
129+
free(d->messages[d->msg_nr]);
130+
}
131+
free(d->messages);
116132
}
117133

118134
static int list_each_note(const struct object_id *object_oid,
@@ -213,65 +229,97 @@ static void write_note_data(struct note_data *d, struct object_id *oid)
213229
}
214230
}
215231

232+
static void insert_separator(struct strbuf *message, size_t pos)
233+
{
234+
size_t sep_len = strlen(separator);
235+
if (sep_len && separator[sep_len - 1] == '\n')
236+
strbuf_insertstr(message, pos, separator);
237+
else
238+
strbuf_insertf(message, pos, "%s%s", separator, "\n");
239+
}
240+
241+
static void concat_messages(struct note_data *d)
242+
{
243+
struct strbuf msg = STRBUF_INIT;
244+
245+
size_t i;
246+
for (i = 0; i < d->msg_nr ; i++) {
247+
if (d->buf.len)
248+
insert_separator(&d->buf, d->buf.len);
249+
strbuf_add(&msg, d->messages[i]->buf.buf, d->messages[i]->buf.len);
250+
strbuf_addbuf(&d->buf, &msg);
251+
if (d->messages[i]->stripspace)
252+
strbuf_stripspace(&d->buf, 0);
253+
strbuf_reset(&msg);
254+
}
255+
strbuf_release(&msg);
256+
}
257+
216258
static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
217259
{
218260
struct note_data *d = opt->value;
261+
struct note_msg *msg = xmalloc(sizeof(*msg));
219262

220263
BUG_ON_OPT_NEG(unset);
221264

222-
if (d->buf.len)
223-
strbuf_addch(&d->buf, '\n');
224-
strbuf_addstr(&d->buf, arg);
225-
strbuf_stripspace(&d->buf, 0);
226-
227-
d->given = 1;
265+
strbuf_init(&msg->buf, strlen(arg));
266+
strbuf_addstr(&msg->buf, arg);
267+
ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
268+
d->messages[d->msg_nr - 1] = msg;
269+
msg->stripspace = 1;
228270
return 0;
229271
}
230272

231273
static int parse_file_arg(const struct option *opt, const char *arg, int unset)
232274
{
233275
struct note_data *d = opt->value;
276+
struct note_msg *msg = xmalloc(sizeof(*msg));
234277

235278
BUG_ON_OPT_NEG(unset);
236279

237-
if (d->buf.len)
238-
strbuf_addch(&d->buf, '\n');
280+
strbuf_init(&msg->buf , 0);
239281
if (!strcmp(arg, "-")) {
240-
if (strbuf_read(&d->buf, 0, 1024) < 0)
282+
if (strbuf_read(&msg->buf, 0, 1024) < 0)
241283
die_errno(_("cannot read '%s'"), arg);
242-
} else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
284+
} else if (strbuf_read_file(&msg->buf, arg, 1024) < 0)
243285
die_errno(_("could not open or read '%s'"), arg);
244-
strbuf_stripspace(&d->buf, 0);
245286

246-
d->given = 1;
287+
ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
288+
d->messages[d->msg_nr - 1] = msg;
289+
msg->stripspace = 1;
247290
return 0;
248291
}
249292

250293
static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
251294
{
252295
struct note_data *d = opt->value;
253-
char *buf;
296+
struct note_msg *msg = xmalloc(sizeof(*msg));
297+
char *value;
254298
struct object_id object;
255299
enum object_type type;
256300
unsigned long len;
257301

258302
BUG_ON_OPT_NEG(unset);
259303

260-
if (d->buf.len)
261-
strbuf_addch(&d->buf, '\n');
262-
304+
strbuf_init(&msg->buf, 0);
263305
if (repo_get_oid(the_repository, arg, &object))
264306
die(_("failed to resolve '%s' as a valid ref."), arg);
265-
if (!(buf = repo_read_object_file(the_repository, &object, &type, &len)))
307+
if (!(value = repo_read_object_file(the_repository, &object, &type, &len)))
266308
die(_("failed to read object '%s'."), arg);
267309
if (type != OBJ_BLOB) {
268-
free(buf);
310+
strbuf_release(&msg->buf);
311+
free(value);
312+
free(msg);
269313
die(_("cannot read note data from non-blob object '%s'."), arg);
270314
}
271-
strbuf_add(&d->buf, buf, len);
272-
free(buf);
273315

274-
d->given = 1;
316+
strbuf_add(&msg->buf, value, len);
317+
free(value);
318+
319+
msg->buf.len = len;
320+
ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
321+
d->messages[d->msg_nr - 1] = msg;
322+
msg->stripspace = 0;
275323
return 0;
276324
}
277325

@@ -406,6 +454,7 @@ static int add(int argc, const char **argv, const char *prefix)
406454
struct object_id object, new_note;
407455
const struct object_id *note;
408456
struct note_data d = { .buf = STRBUF_INIT };
457+
409458
struct option options[] = {
410459
OPT_CALLBACK_F('m', "message", &d, N_("message"),
411460
N_("note contents as a string"), PARSE_OPT_NONEG,
@@ -422,6 +471,8 @@ static int add(int argc, const char **argv, const char *prefix)
422471
OPT_BOOL(0, "allow-empty", &allow_empty,
423472
N_("allow storing empty note")),
424473
OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
474+
OPT_STRING(0, "separator", &separator, N_("separator"),
475+
N_("insert <paragraph-break> between paragraphs")),
425476
OPT_END()
426477
};
427478

@@ -433,6 +484,10 @@ static int add(int argc, const char **argv, const char *prefix)
433484
usage_with_options(git_notes_add_usage, options);
434485
}
435486

487+
if (d.msg_nr)
488+
concat_messages(&d);
489+
d.given = !!d.buf.len;
490+
436491
object_ref = argc > 1 ? argv[1] : "HEAD";
437492

438493
if (repo_get_oid(the_repository, object_ref, &object))
@@ -587,6 +642,8 @@ static int append_edit(int argc, const char **argv, const char *prefix)
587642
parse_reuse_arg),
588643
OPT_BOOL(0, "allow-empty", &allow_empty,
589644
N_("allow storing empty note")),
645+
OPT_STRING(0, "separator", &separator, N_("separator"),
646+
N_("insert <paragraph-break> between paragraphs")),
590647
OPT_END()
591648
};
592649
int edit = !strcmp(argv[0], "edit");
@@ -600,6 +657,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
600657
usage_with_options(usage, options);
601658
}
602659

660+
if (d.msg_nr)
661+
concat_messages(&d);
662+
d.given = !!d.buf.len;
663+
603664
if (d.given && edit)
604665
fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
605666
"for the 'edit' subcommand.\n"
@@ -623,7 +684,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
623684
&type, &size);
624685

625686
if (d.buf.len && prev_buf && size)
626-
strbuf_insertstr(&d.buf, 0, "\n");
687+
insert_separator(&d.buf, 0);
627688
if (prev_buf && size)
628689
strbuf_insert(&d.buf, 0, prev_buf, size);
629690
free(prev_buf);

0 commit comments

Comments
 (0)