builtin-notes: Add -c/-C options for reusing notes
Inspired by the -c/-C options to "git commit", we teach these options to "git notes add/append" to allow reuse of note objects. With this patch in place, it is now easy to copy or move notes between objects. For example, to copy object A's notes to object B: git notes add [-f] -C $(git notes list A) B To move instead of copying, you simply remove the notes from the source object afterwards, e.g.: git notes remove A The patch includes tests verifying correct behaviour of the new functionality. Signed-off-by: Johan Herland <johan@herland.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
348f199b2d
commit
0691cff7dc
@ -9,8 +9,8 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git notes' [list [<object>]]
|
||||
'git notes' add [-f] [-F <file> | -m <msg>] [<object>]
|
||||
'git notes' append [-F <file> | -m <msg>] [<object>]
|
||||
'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
|
||||
'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
|
||||
'git notes' edit [<object>]
|
||||
'git notes' show [<object>]
|
||||
'git notes' remove [<object>]
|
||||
@ -84,6 +84,14 @@ OPTIONS
|
||||
Take the note message from the given file. Use '-' to
|
||||
read the note message from the standard input.
|
||||
|
||||
-C <object>::
|
||||
--reuse-message=<object>::
|
||||
Reuse the note message from the given note object.
|
||||
|
||||
-c <object>::
|
||||
--reedit-message=<object>::
|
||||
Like '-C', but with '-c' the editor is invoked, so that
|
||||
the user can further edit the note message.
|
||||
|
||||
Author
|
||||
------
|
||||
|
@ -19,8 +19,8 @@
|
||||
|
||||
static const char * const git_notes_usage[] = {
|
||||
"git notes [list [<object>]]",
|
||||
"git notes add [-f] [-m <msg> | -F <file>] [<object>]",
|
||||
"git notes append [-m <msg> | -F <file>] [<object>]",
|
||||
"git notes add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
|
||||
"git notes append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
|
||||
"git notes edit [<object>]",
|
||||
"git notes show [<object>]",
|
||||
"git notes remove [<object>]",
|
||||
@ -36,6 +36,7 @@ static const char note_template[] =
|
||||
|
||||
struct msg_arg {
|
||||
int given;
|
||||
int use_editor;
|
||||
struct strbuf buf;
|
||||
};
|
||||
|
||||
@ -104,7 +105,7 @@ static void create_note(const unsigned char *object, struct msg_arg *msg,
|
||||
{
|
||||
char *path = NULL;
|
||||
|
||||
if (!msg->given) {
|
||||
if (msg->use_editor || !msg->given) {
|
||||
int fd;
|
||||
|
||||
/* write the template message before editing: */
|
||||
@ -113,13 +114,16 @@ static void create_note(const unsigned char *object, struct msg_arg *msg,
|
||||
if (fd < 0)
|
||||
die_errno("could not create file '%s'", path);
|
||||
|
||||
if (prev && !append_only)
|
||||
if (msg->given)
|
||||
write_or_die(fd, msg->buf.buf, msg->buf.len);
|
||||
else if (prev && !append_only)
|
||||
write_note_data(fd, prev);
|
||||
write_or_die(fd, note_template, strlen(note_template));
|
||||
|
||||
write_commented_object(fd, object);
|
||||
|
||||
close(fd);
|
||||
strbuf_reset(&(msg->buf));
|
||||
|
||||
if (launch_editor(path, &(msg->buf), NULL)) {
|
||||
die("Please supply the note contents using either -m" \
|
||||
@ -199,6 +203,40 @@ static int parse_file_arg(const struct option *opt, const char *arg, int unset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct msg_arg *msg = opt->value;
|
||||
char *buf;
|
||||
unsigned char object[20];
|
||||
enum object_type type;
|
||||
unsigned long len;
|
||||
|
||||
if (!arg)
|
||||
return -1;
|
||||
|
||||
if (msg->buf.len)
|
||||
strbuf_addstr(&(msg->buf), "\n");
|
||||
|
||||
if (get_sha1(arg, object))
|
||||
die("Failed to resolve '%s' as a valid ref.", arg);
|
||||
if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
|
||||
free(buf);
|
||||
die("Failed to read object '%s'.", arg);;
|
||||
}
|
||||
strbuf_add(&(msg->buf), buf, len);
|
||||
free(buf);
|
||||
|
||||
msg->given = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct msg_arg *msg = opt->value;
|
||||
msg->use_editor = 1;
|
||||
return parse_reuse_arg(opt, arg, unset);
|
||||
}
|
||||
|
||||
int commit_notes(struct notes_tree *t, const char *msg)
|
||||
{
|
||||
struct commit_list *parent;
|
||||
@ -250,13 +288,17 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
|
||||
int list = 0, add = 0, append = 0, edit = 0, show = 0, remove = 0,
|
||||
prune = 0, force = 0;
|
||||
int given_object;
|
||||
struct msg_arg msg = { 0, STRBUF_INIT };
|
||||
struct msg_arg msg = { 0, 0, STRBUF_INIT };
|
||||
struct option options[] = {
|
||||
OPT_GROUP("Notes options"),
|
||||
OPT_CALLBACK('m', "message", &msg, "MSG",
|
||||
"note contents as a string", parse_msg_arg),
|
||||
OPT_CALLBACK('F', "file", &msg, "FILE",
|
||||
"note contents in a file", parse_file_arg),
|
||||
OPT_CALLBACK('c', "reedit-message", &msg, "OBJECT",
|
||||
"reuse and edit specified note object", parse_reedit_arg),
|
||||
OPT_CALLBACK('C', "reuse-message", &msg, "OBJECT",
|
||||
"reuse specified note object", parse_reuse_arg),
|
||||
OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
|
||||
OPT_END()
|
||||
};
|
||||
@ -286,17 +328,17 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
|
||||
usage_with_options(git_notes_usage, options);
|
||||
|
||||
if (msg.given && !(add || append || edit)) {
|
||||
error("cannot use -m/-F options with %s subcommand.", argv[0]);
|
||||
error("cannot use -m/-F/-c/-C options with %s subcommand.",
|
||||
argv[0]);
|
||||
usage_with_options(git_notes_usage, options);
|
||||
}
|
||||
|
||||
if (msg.given && edit) {
|
||||
fprintf(stderr, "The -m and -F options has been deprecated for"
|
||||
" the 'edit' subcommand.\n"
|
||||
"Please use 'git notes add -f -m/-F' instead.\n");
|
||||
fprintf(stderr, "The -m/-F/-c/-C options have been deprecated "
|
||||
"for the 'edit' subcommand.\n"
|
||||
"Please use 'git notes add -f -m/-F/-c/-C' instead.\n");
|
||||
}
|
||||
|
||||
|
||||
if (force && !add) {
|
||||
error("cannot use -f option with %s subcommand.", argv[0]);
|
||||
usage_with_options(git_notes_usage, options);
|
||||
@ -359,6 +401,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (remove) {
|
||||
msg.given = 1;
|
||||
msg.use_editor = 0;
|
||||
strbuf_reset(&(msg.buf));
|
||||
}
|
||||
|
||||
|
116
t/t3301-notes.sh
116
t/t3301-notes.sh
@ -465,4 +465,120 @@ test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 2ede89468182a62d0bde2583c736089bcf7d7e92
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:19:13 2005 -0700
|
||||
|
||||
7th
|
||||
|
||||
Notes:
|
||||
other note
|
||||
EOF
|
||||
|
||||
test_expect_success 'create note from other note with "git notes add -C"' '
|
||||
: > a7 &&
|
||||
git add a7 &&
|
||||
test_tick &&
|
||||
git commit -m 7th &&
|
||||
git notes add -C $(git notes list HEAD^) &&
|
||||
git log -1 > actual &&
|
||||
test_cmp expect actual &&
|
||||
test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
|
||||
'
|
||||
|
||||
test_expect_success 'create note from non-existing note with "git notes add -C" fails' '
|
||||
: > a8 &&
|
||||
git add a8 &&
|
||||
test_tick &&
|
||||
git commit -m 8th &&
|
||||
test_must_fail git notes add -C deadbeef &&
|
||||
test_must_fail git notes list HEAD
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:21:13 2005 -0700
|
||||
|
||||
9th
|
||||
|
||||
Notes:
|
||||
yet another note
|
||||
EOF
|
||||
|
||||
test_expect_success 'create note from other note with "git notes add -c"' '
|
||||
: > a9 &&
|
||||
git add a9 &&
|
||||
test_tick &&
|
||||
git commit -m 9th &&
|
||||
MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
|
||||
git log -1 > actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'create note from non-existing note with "git notes add -c" fails' '
|
||||
: > a10 &&
|
||||
git add a10 &&
|
||||
test_tick &&
|
||||
git commit -m 10th &&
|
||||
test_must_fail MSG="yet another note" git notes add -c deadbeef &&
|
||||
test_must_fail git notes list HEAD
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:21:13 2005 -0700
|
||||
|
||||
9th
|
||||
|
||||
Notes:
|
||||
yet another note
|
||||
$whitespace
|
||||
yet another note
|
||||
EOF
|
||||
|
||||
test_expect_success 'append to note from other note with "git notes append -C"' '
|
||||
git notes append -C $(git notes list HEAD^) HEAD^ &&
|
||||
git log -1 HEAD^ > actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit ffed603236bfa3891c49644257a83598afe8ae5a
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:22:13 2005 -0700
|
||||
|
||||
10th
|
||||
|
||||
Notes:
|
||||
other note
|
||||
EOF
|
||||
|
||||
test_expect_success 'create note from other note with "git notes append -c"' '
|
||||
MSG="other note" git notes append -c $(git notes list HEAD^) &&
|
||||
git log -1 > actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
commit ffed603236bfa3891c49644257a83598afe8ae5a
|
||||
Author: A U Thor <author@example.com>
|
||||
Date: Thu Apr 7 15:22:13 2005 -0700
|
||||
|
||||
10th
|
||||
|
||||
Notes:
|
||||
other note
|
||||
$whitespace
|
||||
yet another note
|
||||
EOF
|
||||
|
||||
test_expect_success 'append to note from other note with "git notes append -c"' '
|
||||
MSG="yet another note" git notes append -c $(git notes list HEAD) &&
|
||||
git log -1 > actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user