Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions Documentation/git-notes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ SYNOPSIS
--------
[verse]
'git notes' [list [<object>]]
'git notes' add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
'git notes' add [-f] [--allow-empty] [--separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
'git notes' copy [-f] ( --stdin | <from-object> [<to-object>] )
'git notes' append [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
'git notes' edit [--allow-empty] [<object>]
'git notes' append [--allow-empty] [--separator=<paragraph-break>] [--[no-]stripspace] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
'git notes' edit [--allow-empty] [<object>] [--[no-]stripspace]
'git notes' show [<object>]
'git notes' merge [-v | -q] [-s <strategy> ] <notes-ref>
'git notes' merge --commit [-v | -q]
Expand Down Expand Up @@ -65,7 +65,9 @@ add::
However, if you're using `add` interactively (using an editor
to supply the notes contents), then - instead of aborting -
the existing notes will be opened in the editor (like the `edit`
subcommand).
subcommand). If you specify multiple `-m` and `-F`, a blank
line will be inserted between the messages. Use the `--separator`
option to insert other delimiters.

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

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

edit::
Edit the notes for a given object (defaults to HEAD).
Expand Down Expand Up @@ -135,20 +141,26 @@ OPTIONS
If multiple `-m` options are given, their values
are concatenated as separate paragraphs.
Lines starting with `#` and empty lines other than a
single line between paragraphs will be stripped out.
single line between paragraphs will be stripped out,
if you wish to keep them verbatim, use `--no-stripspace`.

-F <file>::
--file=<file>::
Take the note message from the given file. Use '-' to
read the note message from the standard input.
Lines starting with `#` and empty lines other than a
single line between paragraphs will be stripped out.
single line between paragraphs will be stripped out,
if you wish to keep them verbatim, use with
`--no-stripspace` option.

-C <object>::
--reuse-message=<object>::
Take the given blob object (for example, another note) as the
note message. (Use `git notes copy <object>` instead to
copy notes between objects.)
copy notes between objects.). By default, message will be
copied verbatim, but if you wish to strip out the lines
starting with `#` and empty lines other than a single line
between paragraphs, use with`--stripspace` option.

-c <object>::
--reedit-message=<object>::
Expand All @@ -159,6 +171,18 @@ OPTIONS
Allow an empty note object to be stored. The default behavior is
to automatically remove empty notes.

--separator <paragraph-break>::
Specify a string used as a custom inter-paragraph separator
(a newline is added at the end as needed). Defaults to a
blank line.

--[no-]stripspace::
Strip leading and trailing whitespace from the note message.
Also strip out empty lines other than a single line between
paragraphs. For lines starting with `#` will be stripped out
in non-editor cases like "-m", "-F" and "-C", but not in
editor case like "git notes edit", "-c", etc.

--ref <ref>::
Manipulate the notes tree in <ref>. This overrides
`GIT_NOTES_REF` and the "core.notesRef" configuration. The ref
Expand Down
141 changes: 108 additions & 33 deletions builtin/notes.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

#include "cache.h"
#include "alloc.h"
#include "config.h"
#include "builtin.h"
#include "gettext.h"
Expand All @@ -27,11 +28,12 @@
#include "worktree.h"
#include "write-or-die.h"

static const char *separator = "\n";
static const char * const git_notes_usage[] = {
N_("git notes [--ref <notes-ref>] [list [<object>]]"),
N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--separator=<paragraph-break>] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"),
N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
N_("git notes [--ref <notes-ref>] append [--allow-empty] [--separator=<paragraph-break>] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"),
N_("git notes [--ref <notes-ref>] show [<object>]"),
N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"),
Expand Down Expand Up @@ -99,11 +101,26 @@ static const char * const git_notes_get_ref_usage[] = {
static const char note_template[] =
N_("Write/edit the notes for the following object:");

enum notes_stripspace {
UNSPECIFIED = -1,
NO_STRIPSPACE = 0,
STRIPSPACE = 1,
};

struct note_msg {
enum notes_stripspace stripspace;
struct strbuf buf;
};

struct note_data {
int given;
int use_editor;
int stripspace;
char *edit_path;
struct strbuf buf;
struct note_msg **messages;
size_t msg_nr;
size_t msg_alloc;
};

static void free_note_data(struct note_data *d)
Expand All @@ -113,6 +130,12 @@ static void free_note_data(struct note_data *d)
free(d->edit_path);
}
strbuf_release(&d->buf);

while (d->msg_nr--) {
strbuf_release(&d->messages[d->msg_nr]->buf);
free(d->messages[d->msg_nr]);
}
free(d->messages);
}

static int list_each_note(const struct object_id *object_oid,
Expand Down Expand Up @@ -197,7 +220,8 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
if (launch_editor(d->edit_path, &d->buf, NULL)) {
die(_("please supply the note contents using either -m or -F option"));
}
strbuf_stripspace(&d->buf, 1);
if (d->stripspace)
strbuf_stripspace(&d->buf, 1);
}
}

Expand All @@ -213,66 +237,98 @@ static void write_note_data(struct note_data *d, struct object_id *oid)
}
}

static void append_separator(struct strbuf *message)
{
if (separator[strlen(separator) - 1] == '\n')
strbuf_addstr(message, separator);
else
strbuf_addf(message, "%s%s", separator, "\n");
}

static void concat_messages(struct note_data *d)
{
struct strbuf msg = STRBUF_INIT;
size_t i;

for (i = 0; i < d->msg_nr ; i++) {
if (d->buf.len)
append_separator(&d->buf);
strbuf_add(&msg, d->messages[i]->buf.buf, d->messages[i]->buf.len);
strbuf_addbuf(&d->buf, &msg);
if ((d->stripspace == UNSPECIFIED &&
d->messages[i]->stripspace == STRIPSPACE) ||
d->stripspace == STRIPSPACE)
strbuf_stripspace(&d->buf, 0);
strbuf_reset(&msg);
}
strbuf_release(&msg);
}

static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
struct note_msg *msg = xmalloc(sizeof(*msg));

BUG_ON_OPT_NEG(unset);

strbuf_grow(&d->buf, strlen(arg) + 2);
if (d->buf.len)
strbuf_addch(&d->buf, '\n');
strbuf_addstr(&d->buf, arg);
strbuf_stripspace(&d->buf, 0);

d->given = 1;
strbuf_init(&msg->buf, strlen(arg));
strbuf_addstr(&msg->buf, arg);
ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
d->messages[d->msg_nr - 1] = msg;
msg->stripspace = STRIPSPACE;
return 0;
}

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

BUG_ON_OPT_NEG(unset);

if (d->buf.len)
strbuf_addch(&d->buf, '\n');
strbuf_init(&msg->buf , 0);
if (!strcmp(arg, "-")) {
if (strbuf_read(&d->buf, 0, 1024) < 0)
if (strbuf_read(&msg->buf, 0, 1024) < 0)
die_errno(_("cannot read '%s'"), arg);
} else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
} else if (strbuf_read_file(&msg->buf, arg, 1024) < 0)
die_errno(_("could not open or read '%s'"), arg);
strbuf_stripspace(&d->buf, 0);

d->given = 1;
ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
d->messages[d->msg_nr - 1] = msg;
msg->stripspace = STRIPSPACE;
return 0;
}

static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
{
struct note_data *d = opt->value;
char *buf;
struct note_msg *msg = xmalloc(sizeof(*msg));
char *value;
struct object_id object;
enum object_type type;
unsigned long len;

BUG_ON_OPT_NEG(unset);

if (d->buf.len)
strbuf_addch(&d->buf, '\n');

strbuf_init(&msg->buf, 0);
if (repo_get_oid(the_repository, arg, &object))
die(_("failed to resolve '%s' as a valid ref."), arg);
if (!(buf = repo_read_object_file(the_repository, &object, &type, &len)))
if (!(value = repo_read_object_file(the_repository, &object, &type, &len)))
die(_("failed to read object '%s'."), arg);
if (type != OBJ_BLOB) {
free(buf);
strbuf_release(&msg->buf);
free(value);
free(msg);
die(_("cannot read note data from non-blob object '%s'."), arg);
}
strbuf_add(&d->buf, buf, len);
free(buf);

d->given = 1;
strbuf_add(&msg->buf, value, len);
free(value);

msg->buf.len = len;
ALLOC_GROW_BY(d->messages, d->msg_nr, 1, d->msg_alloc);
d->messages[d->msg_nr - 1] = msg;
msg->stripspace = NO_STRIPSPACE;
return 0;
}

Expand Down Expand Up @@ -406,7 +462,8 @@ static int add(int argc, const char **argv, const char *prefix)
struct notes_tree *t;
struct object_id object, new_note;
const struct object_id *note;
struct note_data d = { 0, 0, NULL, STRBUF_INIT };
struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };

struct option options[] = {
OPT_CALLBACK_F('m', "message", &d, N_("message"),
N_("note contents as a string"), PARSE_OPT_NONEG,
Expand All @@ -423,6 +480,10 @@ static int add(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
OPT_STRING(0, "separator", &separator, N_("separator"),
N_("insert <paragraph-break> between paragraphs")),
OPT_BOOL(0, "stripspace", &d.stripspace,
N_("remove unnecessary whitespace")),
OPT_END()
};

Expand All @@ -434,6 +495,10 @@ static int add(int argc, const char **argv, const char *prefix)
usage_with_options(git_notes_add_usage, options);
}

if (d.msg_nr)
concat_messages(&d);
d.given = !!d.buf.len;

object_ref = argc > 1 ? argv[1] : "HEAD";

if (repo_get_oid(the_repository, object_ref, &object))
Expand Down Expand Up @@ -572,7 +637,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
const struct object_id *note;
char *logmsg;
const char * const *usage;
struct note_data d = { 0, 0, NULL, STRBUF_INIT };
struct note_data d = { .buf = STRBUF_INIT, .stripspace = UNSPECIFIED };
struct option options[] = {
OPT_CALLBACK_F('m', "message", &d, N_("message"),
N_("note contents as a string"), PARSE_OPT_NONEG,
Expand All @@ -588,6 +653,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
parse_reuse_arg),
OPT_BOOL(0, "allow-empty", &allow_empty,
N_("allow storing empty note")),
OPT_STRING(0, "separator", &separator, N_("separator"),
N_("insert <paragraph-break> between paragraphs")),
OPT_BOOL(0, "stripspace", &d.stripspace,
N_("remove unnecessary whitespace")),
OPT_END()
};
int edit = !strcmp(argv[0], "edit");
Expand All @@ -601,6 +670,10 @@ static int append_edit(int argc, const char **argv, const char *prefix)
usage_with_options(usage, options);
}

if (d.msg_nr)
concat_messages(&d);
d.given = !!d.buf.len;

if (d.given && edit)
fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
"for the 'edit' subcommand.\n"
Expand All @@ -620,15 +693,17 @@ static int append_edit(int argc, const char **argv, const char *prefix)
/* Append buf to previous note contents */
unsigned long size;
enum object_type type;
char *prev_buf = repo_read_object_file(the_repository, note,
&type, &size);
struct strbuf buf = STRBUF_INIT;
char *prev_buf = repo_read_object_file(the_repository, note, &type, &size);

strbuf_grow(&d.buf, size + 1);
if (d.buf.len && prev_buf && size)
strbuf_insertstr(&d.buf, 0, "\n");
if (prev_buf && size)
strbuf_insert(&d.buf, 0, prev_buf, size);
strbuf_add(&buf, prev_buf, size);
if (d.buf.len && prev_buf && size)
append_separator(&buf);
strbuf_insert(&d.buf, 0, buf.buf, buf.len);

free(prev_buf);
strbuf_release(&buf);
}

if (d.buf.len || allow_empty) {
Expand Down
Loading