ident.c: add support for IPv6
[gitweb.git] / builtin / notes.c
index acdedbdf5a3cdd4bf4acc3bf2a3bde5773f9cd18..3608c64785283ffe7cec6df425618a279d1616c5 100644 (file)
 #include "string-list.h"
 #include "notes-merge.h"
 #include "notes-utils.h"
+#include "branch.h"
 
 static const char * const git_notes_usage[] = {
-       N_("git notes [--ref <notes_ref>] [list [<object>]]"),
-       N_("git notes [--ref <notes_ref>] add [-f] [-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 [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
-       N_("git notes [--ref <notes_ref>] edit [<object>]"),
-       N_("git notes [--ref <notes_ref>] show [<object>]"),
-       N_("git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"),
+       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>] 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>] 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>"),
        N_("git notes merge --commit [-v | -q]"),
        N_("git notes merge --abort [-v | -q]"),
-       N_("git notes [--ref <notes_ref>] remove [<object>...]"),
-       N_("git notes [--ref <notes_ref>] prune [-n | -v]"),
-       N_("git notes [--ref <notes_ref>] get-ref"),
+       N_("git notes [--ref <notes-ref>] remove [<object>...]"),
+       N_("git notes [--ref <notes-ref>] prune [-n | -v]"),
+       N_("git notes [--ref <notes-ref>] get-ref"),
        NULL
 };
 
@@ -68,7 +69,7 @@ static const char * const git_notes_show_usage[] = {
 };
 
 static const char * const git_notes_merge_usage[] = {
-       N_("git notes merge [<options>] <notes_ref>"),
+       N_("git notes merge [<options>] <notes-ref>"),
        N_("git notes merge --commit [<options>]"),
        N_("git notes merge --abort [<options>]"),
        NULL
@@ -159,9 +160,8 @@ static void write_commented_object(int fd, const unsigned char *object)
                    sha1_to_hex(object));
 }
 
-static void create_note(const unsigned char *object, struct note_data *d,
-                       int append_only, const unsigned char *prev,
-                       unsigned char *result)
+static void prepare_note_data(const unsigned char *object, struct note_data *d,
+               const unsigned char *old_note)
 {
        if (d->use_editor || !d->given) {
                int fd;
@@ -175,8 +175,8 @@ static void create_note(const unsigned char *object, struct note_data *d,
 
                if (d->given)
                        write_or_die(fd, d->buf.buf, d->buf.len);
-               else if (prev && !append_only)
-                       copy_obj_to_fd(fd, prev);
+               else if (old_note)
+                       copy_obj_to_fd(fd, old_note);
 
                strbuf_addch(&buf, '\n');
                strbuf_add_commented_lines(&buf, note_template, strlen(note_template));
@@ -194,33 +194,16 @@ static void create_note(const unsigned char *object, struct note_data *d,
                }
                stripspace(&d->buf, 1);
        }
+}
 
-       if (prev && append_only) {
-               /* Append buf to previous note contents */
-               unsigned long size;
-               enum object_type type;
-               char *prev_buf = read_sha1_file(prev, &type, &size);
-
-               strbuf_grow(&d->buf, size + 1);
-               if (d->buf.len && prev_buf && size)
-                       strbuf_insert(&d->buf, 0, "\n", 1);
-               if (prev_buf && size)
-                       strbuf_insert(&d->buf, 0, prev_buf, size);
-               free(prev_buf);
-       }
-
-       if (!d->buf.len) {
-               fprintf(stderr, _("Removing note for object %s\n"),
-                       sha1_to_hex(object));
-               hashclr(result);
-       } else {
-               if (write_sha1_file(d->buf.buf, d->buf.len, blob_type, result)) {
-                       error(_("unable to write note object"));
-                       if (d->edit_path)
-                               error(_("The note contents have been left in %s"),
-                                     d->edit_path);
-                       exit(128);
-               }
+static void write_note_data(struct note_data *d, unsigned char *sha1)
+{
+       if (write_sha1_file(d->buf.buf, d->buf.len, blob_type, sha1)) {
+               error(_("unable to write note object"));
+               if (d->edit_path)
+                       error(_("The note contents have been left in %s"),
+                               d->edit_path);
+               exit(128);
        }
 }
 
@@ -399,11 +382,10 @@ static int append_edit(int argc, const char **argv, const char *prefix);
 
 static int add(int argc, const char **argv, const char *prefix)
 {
-       int force = 0;
+       int force = 0, allow_empty = 0;
        const char *object_ref;
        struct notes_tree *t;
        unsigned char object[20], new_note[20];
-       char logmsg[100];
        const unsigned char *note;
        struct note_data d = { 0, 0, NULL, STRBUF_INIT };
        struct option options[] = {
@@ -419,6 +401,8 @@ static int add(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
                        N_("reuse specified note object"), PARSE_OPT_NONEG,
                        parse_reuse_arg},
+               OPT_BOOL(0, "allow-empty", &allow_empty,
+                       N_("allow storing empty note")),
                OPT__FORCE(&force, N_("replace existing notes")),
                OPT_END()
        };
@@ -463,17 +447,20 @@ static int add(int argc, const char **argv, const char *prefix)
                        sha1_to_hex(object));
        }
 
-       create_note(object, &d, 0, note, new_note);
-       free_note_data(&d);
-
-       if (is_null_sha1(new_note))
+       prepare_note_data(object, &d, note);
+       if (d.buf.len || allow_empty) {
+               write_note_data(&d, new_note);
+               if (add_note(t, object, new_note, combine_notes_overwrite))
+                       die("BUG: combine_notes_overwrite failed");
+               commit_notes(t, "Notes added by 'git notes add'");
+       } else {
+               fprintf(stderr, _("Removing note for object %s\n"),
+                       sha1_to_hex(object));
                remove_note(t, object);
-       else if (add_note(t, object, new_note, combine_notes_overwrite))
-               die("BUG: combine_notes_overwrite failed");
+               commit_notes(t, "Notes removed by 'git notes add'");
+       }
 
-       snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
-                is_null_sha1(new_note) ? "removed" : "added", "add");
-       commit_notes(t, logmsg);
+       free_note_data(&d);
        free_notes(t);
        return 0;
 }
@@ -556,6 +543,7 @@ static int copy(int argc, const char **argv, const char *prefix)
 
 static int append_edit(int argc, const char **argv, const char *prefix)
 {
+       int allow_empty = 0;
        const char *object_ref;
        struct notes_tree *t;
        unsigned char object[20], new_note[20];
@@ -576,6 +564,8 @@ static int append_edit(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
                        N_("reuse specified note object"), PARSE_OPT_NONEG,
                        parse_reuse_arg},
+               OPT_BOOL(0, "allow-empty", &allow_empty,
+                       N_("allow storing empty note")),
                OPT_END()
        };
        int edit = !strcmp(argv[0], "edit");
@@ -602,17 +592,38 @@ static int append_edit(int argc, const char **argv, const char *prefix)
        t = init_notes_check(argv[0]);
        note = get_note(t, object);
 
-       create_note(object, &d, !edit, note, new_note);
-       free_note_data(&d);
+       prepare_note_data(object, &d, edit ? note : NULL);
 
-       if (is_null_sha1(new_note))
-               remove_note(t, object);
-       else if (add_note(t, object, new_note, combine_notes_overwrite))
-               die("BUG: combine_notes_overwrite failed");
+       if (note && !edit) {
+               /* Append buf to previous note contents */
+               unsigned long size;
+               enum object_type type;
+               char *prev_buf = read_sha1_file(note, &type, &size);
 
-       snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
-                is_null_sha1(new_note) ? "removed" : "added", argv[0]);
+               strbuf_grow(&d.buf, size + 1);
+               if (d.buf.len && prev_buf && size)
+                       strbuf_insert(&d.buf, 0, "\n", 1);
+               if (prev_buf && size)
+                       strbuf_insert(&d.buf, 0, prev_buf, size);
+               free(prev_buf);
+       }
+
+       if (d.buf.len || allow_empty) {
+               write_note_data(&d, new_note);
+               if (add_note(t, object, new_note, combine_notes_overwrite))
+                       die("BUG: combine_notes_overwrite failed");
+               snprintf(logmsg, sizeof(logmsg), "Notes added by 'git notes %s'",
+                       argv[0]);
+       } else {
+               fprintf(stderr, _("Removing note for object %s\n"),
+                       sha1_to_hex(object));
+               remove_note(t, object);
+               snprintf(logmsg, sizeof(logmsg), "Notes removed by 'git notes %s'",
+                       argv[0]);
+       }
        commit_notes(t, logmsg);
+
+       free_note_data(&d);
        free_notes(t);
        return 0;
 }
@@ -727,6 +738,19 @@ static int merge_commit(struct notes_merge_options *o)
        return ret;
 }
 
+static int git_config_get_notes_strategy(const char *key,
+                                        enum notes_merge_strategy *strategy)
+{
+       const char *value;
+
+       if (git_config_get_string_const(key, &value))
+               return 1;
+       if (parse_notes_merge_strategy(value, strategy))
+               git_die_config(key, "unknown notes merge strategy %s", value);
+
+       return 0;
+}
+
 static int merge(int argc, const char **argv, const char *prefix)
 {
        struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
@@ -785,24 +809,28 @@ static int merge(int argc, const char **argv, const char *prefix)
        expand_notes_ref(&remote_ref);
        o.remote_ref = remote_ref.buf;
 
+       t = init_notes_check("merge");
+
        if (strategy) {
-               if (!strcmp(strategy, "manual"))
-                       o.strategy = NOTES_MERGE_RESOLVE_MANUAL;
-               else if (!strcmp(strategy, "ours"))
-                       o.strategy = NOTES_MERGE_RESOLVE_OURS;
-               else if (!strcmp(strategy, "theirs"))
-                       o.strategy = NOTES_MERGE_RESOLVE_THEIRS;
-               else if (!strcmp(strategy, "union"))
-                       o.strategy = NOTES_MERGE_RESOLVE_UNION;
-               else if (!strcmp(strategy, "cat_sort_uniq"))
-                       o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
-               else {
+               if (parse_notes_merge_strategy(strategy, &o.strategy)) {
                        error("Unknown -s/--strategy: %s", strategy);
                        usage_with_options(git_notes_merge_usage, options);
                }
-       }
+       } else {
+               struct strbuf merge_key = STRBUF_INIT;
+               const char *short_ref = NULL;
 
-       t = init_notes_check("merge");
+               if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref))
+                       die("BUG: local ref %s is outside of refs/notes/",
+                           o.local_ref);
+
+               strbuf_addf(&merge_key, "notes.%s.mergeStrategy", short_ref);
+
+               if (git_config_get_notes_strategy(merge_key.buf, &o.strategy))
+                       git_config_get_notes_strategy("notes.mergeStrategy", &o.strategy);
+
+               strbuf_release(&merge_key);
+       }
 
        strbuf_addf(&msg, "notes: Merged notes from %s into %s",
                    remote_ref.buf, default_notes_ref());
@@ -815,10 +843,15 @@ static int merge(int argc, const char **argv, const char *prefix)
                update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
        else { /* Merge has unresolved conflicts */
+               char *existing;
                /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
                update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
                /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
+               existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
+               if (existing)
+                       die(_("A notes merge into %s is already in-progress at %s"),
+                           default_notes_ref(), existing);
                if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
                        die("Failed to store link to current notes ref (%s)",
                            default_notes_ref());
@@ -941,7 +974,7 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
        const char *override_notes_ref = NULL;
        struct option options[] = {
                OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"),
-                          N_("use notes from <notes_ref>")),
+                          N_("use notes from <notes-ref>")),
                OPT_END()
        };