Code

builtin-notes: Add -c/-C options for reusing notes
authorJohan Herland <johan@herland.net>
Sat, 13 Feb 2010 21:28:36 +0000 (22:28 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sun, 14 Feb 2010 03:36:16 +0000 (19:36 -0800)
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>
Documentation/git-notes.txt
builtin-notes.c
t/t3301-notes.sh

index 53c5d9014dfca7cf3650e7091abf51f0c441dee9..15de4b344a093354df1332c6e037e8f29508c8d5 100644 (file)
@@ -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
 ------
index 190c46c3bebc8150a65460add564e5e6d87f1e3b..98de1154c989851a4bd405036f9b844a4d57483f 100644 (file)
@@ -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));
        }
 
index 07090e399730d8fa39737a60e232c3f96599583e..6447e5f54e45414089677d6bcff9738538e47e15 100755 (executable)
@@ -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