builtin / notes.con commit diff --stat: move the "total count" logic to the last loop (a20d3c0)
   1/*
   2 * Builtin "git notes"
   3 *
   4 * Copyright (c) 2010 Johan Herland <johan@herland.net>
   5 *
   6 * Based on git-notes.sh by Johannes Schindelin,
   7 * and builtin-tag.c by Kristian Høgsberg and Carlos Rica.
   8 */
   9
  10#include "cache.h"
  11#include "builtin.h"
  12#include "notes.h"
  13#include "blob.h"
  14#include "commit.h"
  15#include "refs.h"
  16#include "exec_cmd.h"
  17#include "run-command.h"
  18#include "parse-options.h"
  19#include "string-list.h"
  20#include "notes-merge.h"
  21
  22static void commit_notes(struct notes_tree *t, const char *msg);
  23static combine_notes_fn parse_combine_notes_fn(const char *v);
  24
  25static const char * const git_notes_usage[] = {
  26        N_("git notes [--ref <notes_ref>] [list [<object>]]"),
  27        N_("git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
  28        N_("git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"),
  29        N_("git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
  30        N_("git notes [--ref <notes_ref>] edit [<object>]"),
  31        N_("git notes [--ref <notes_ref>] show [<object>]"),
  32        N_("git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"),
  33        N_("git notes merge --commit [-v | -q]"),
  34        N_("git notes merge --abort [-v | -q]"),
  35        N_("git notes [--ref <notes_ref>] remove [<object>...]"),
  36        N_("git notes [--ref <notes_ref>] prune [-n | -v]"),
  37        N_("git notes [--ref <notes_ref>] get-ref"),
  38        NULL
  39};
  40
  41static const char * const git_notes_list_usage[] = {
  42        N_("git notes [list [<object>]]"),
  43        NULL
  44};
  45
  46static const char * const git_notes_add_usage[] = {
  47        N_("git notes add [<options>] [<object>]"),
  48        NULL
  49};
  50
  51static const char * const git_notes_copy_usage[] = {
  52        N_("git notes copy [<options>] <from-object> <to-object>"),
  53        N_("git notes copy --stdin [<from-object> <to-object>]..."),
  54        NULL
  55};
  56
  57static const char * const git_notes_append_usage[] = {
  58        N_("git notes append [<options>] [<object>]"),
  59        NULL
  60};
  61
  62static const char * const git_notes_edit_usage[] = {
  63        N_("git notes edit [<object>]"),
  64        NULL
  65};
  66
  67static const char * const git_notes_show_usage[] = {
  68        N_("git notes show [<object>]"),
  69        NULL
  70};
  71
  72static const char * const git_notes_merge_usage[] = {
  73        N_("git notes merge [<options>] <notes_ref>"),
  74        N_("git notes merge --commit [<options>]"),
  75        N_("git notes merge --abort [<options>]"),
  76        NULL
  77};
  78
  79static const char * const git_notes_remove_usage[] = {
  80        N_("git notes remove [<object>]"),
  81        NULL
  82};
  83
  84static const char * const git_notes_prune_usage[] = {
  85        N_("git notes prune [<options>]"),
  86        NULL
  87};
  88
  89static const char * const git_notes_get_ref_usage[] = {
  90        N_("git notes get-ref"),
  91        NULL
  92};
  93
  94static const char note_template[] =
  95        "\n"
  96        "#\n"
  97        "# Write/edit the notes for the following object:\n"
  98        "#\n";
  99
 100struct msg_arg {
 101        int given;
 102        int use_editor;
 103        struct strbuf buf;
 104};
 105
 106static int list_each_note(const unsigned char *object_sha1,
 107                const unsigned char *note_sha1, char *note_path,
 108                void *cb_data)
 109{
 110        printf("%s %s\n", sha1_to_hex(note_sha1), sha1_to_hex(object_sha1));
 111        return 0;
 112}
 113
 114static void write_note_data(int fd, const unsigned char *sha1)
 115{
 116        unsigned long size;
 117        enum object_type type;
 118        char *buf = read_sha1_file(sha1, &type, &size);
 119        if (buf) {
 120                if (size)
 121                        write_or_die(fd, buf, size);
 122                free(buf);
 123        }
 124}
 125
 126static void write_commented_object(int fd, const unsigned char *object)
 127{
 128        const char *show_args[5] =
 129                {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL};
 130        struct child_process show;
 131        struct strbuf buf = STRBUF_INIT;
 132        FILE *show_out;
 133
 134        /* Invoke "git show --stat --no-notes $object" */
 135        memset(&show, 0, sizeof(show));
 136        show.argv = show_args;
 137        show.no_stdin = 1;
 138        show.out = -1;
 139        show.err = 0;
 140        show.git_cmd = 1;
 141        if (start_command(&show))
 142                die(_("unable to start 'show' for object '%s'"),
 143                    sha1_to_hex(object));
 144
 145        /* Open the output as FILE* so strbuf_getline() can be used. */
 146        show_out = xfdopen(show.out, "r");
 147        if (show_out == NULL)
 148                die_errno(_("can't fdopen 'show' output fd"));
 149
 150        /* Prepend "# " to each output line and write result to 'fd' */
 151        while (strbuf_getline(&buf, show_out, '\n') != EOF) {
 152                write_or_die(fd, "# ", 2);
 153                write_or_die(fd, buf.buf, buf.len);
 154                write_or_die(fd, "\n", 1);
 155        }
 156        strbuf_release(&buf);
 157        if (fclose(show_out))
 158                die_errno(_("failed to close pipe to 'show' for object '%s'"),
 159                          sha1_to_hex(object));
 160        if (finish_command(&show))
 161                die(_("failed to finish 'show' for object '%s'"),
 162                    sha1_to_hex(object));
 163}
 164
 165static void create_note(const unsigned char *object, struct msg_arg *msg,
 166                        int append_only, const unsigned char *prev,
 167                        unsigned char *result)
 168{
 169        char *path = NULL;
 170
 171        if (msg->use_editor || !msg->given) {
 172                int fd;
 173
 174                /* write the template message before editing: */
 175                path = git_pathdup("NOTES_EDITMSG");
 176                fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 177                if (fd < 0)
 178                        die_errno(_("could not create file '%s'"), path);
 179
 180                if (msg->given)
 181                        write_or_die(fd, msg->buf.buf, msg->buf.len);
 182                else if (prev && !append_only)
 183                        write_note_data(fd, prev);
 184                write_or_die(fd, note_template, strlen(note_template));
 185
 186                write_commented_object(fd, object);
 187
 188                close(fd);
 189                strbuf_reset(&(msg->buf));
 190
 191                if (launch_editor(path, &(msg->buf), NULL)) {
 192                        die(_("Please supply the note contents using either -m" \
 193                            " or -F option"));
 194                }
 195                stripspace(&(msg->buf), 1);
 196        }
 197
 198        if (prev && append_only) {
 199                /* Append buf to previous note contents */
 200                unsigned long size;
 201                enum object_type type;
 202                char *prev_buf = read_sha1_file(prev, &type, &size);
 203
 204                strbuf_grow(&(msg->buf), size + 1);
 205                if (msg->buf.len && prev_buf && size)
 206                        strbuf_insert(&(msg->buf), 0, "\n", 1);
 207                if (prev_buf && size)
 208                        strbuf_insert(&(msg->buf), 0, prev_buf, size);
 209                free(prev_buf);
 210        }
 211
 212        if (!msg->buf.len) {
 213                fprintf(stderr, _("Removing note for object %s\n"),
 214                        sha1_to_hex(object));
 215                hashclr(result);
 216        } else {
 217                if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) {
 218                        error(_("unable to write note object"));
 219                        if (path)
 220                                error(_("The note contents has been left in %s"),
 221                                      path);
 222                        exit(128);
 223                }
 224        }
 225
 226        if (path) {
 227                unlink_or_warn(path);
 228                free(path);
 229        }
 230}
 231
 232static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 233{
 234        struct msg_arg *msg = opt->value;
 235
 236        strbuf_grow(&(msg->buf), strlen(arg) + 2);
 237        if (msg->buf.len)
 238                strbuf_addch(&(msg->buf), '\n');
 239        strbuf_addstr(&(msg->buf), arg);
 240        stripspace(&(msg->buf), 0);
 241
 242        msg->given = 1;
 243        return 0;
 244}
 245
 246static int parse_file_arg(const struct option *opt, const char *arg, int unset)
 247{
 248        struct msg_arg *msg = opt->value;
 249
 250        if (msg->buf.len)
 251                strbuf_addch(&(msg->buf), '\n');
 252        if (!strcmp(arg, "-")) {
 253                if (strbuf_read(&(msg->buf), 0, 1024) < 0)
 254                        die_errno(_("cannot read '%s'"), arg);
 255        } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
 256                die_errno(_("could not open or read '%s'"), arg);
 257        stripspace(&(msg->buf), 0);
 258
 259        msg->given = 1;
 260        return 0;
 261}
 262
 263static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
 264{
 265        struct msg_arg *msg = opt->value;
 266        char *buf;
 267        unsigned char object[20];
 268        enum object_type type;
 269        unsigned long len;
 270
 271        if (msg->buf.len)
 272                strbuf_addch(&(msg->buf), '\n');
 273
 274        if (get_sha1(arg, object))
 275                die(_("Failed to resolve '%s' as a valid ref."), arg);
 276        if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
 277                free(buf);
 278                die(_("Failed to read object '%s'."), arg);;
 279        }
 280        strbuf_add(&(msg->buf), buf, len);
 281        free(buf);
 282
 283        msg->given = 1;
 284        return 0;
 285}
 286
 287static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
 288{
 289        struct msg_arg *msg = opt->value;
 290        msg->use_editor = 1;
 291        return parse_reuse_arg(opt, arg, unset);
 292}
 293
 294static void commit_notes(struct notes_tree *t, const char *msg)
 295{
 296        struct strbuf buf = STRBUF_INIT;
 297        unsigned char commit_sha1[20];
 298
 299        if (!t)
 300                t = &default_notes_tree;
 301        if (!t->initialized || !t->ref || !*t->ref)
 302                die(_("Cannot commit uninitialized/unreferenced notes tree"));
 303        if (!t->dirty)
 304                return; /* don't have to commit an unchanged tree */
 305
 306        /* Prepare commit message and reflog message */
 307        strbuf_addstr(&buf, msg);
 308        if (buf.buf[buf.len - 1] != '\n')
 309                strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
 310
 311        create_notes_commit(t, NULL, &buf, commit_sha1);
 312        strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
 313        update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
 314
 315        strbuf_release(&buf);
 316}
 317
 318static combine_notes_fn parse_combine_notes_fn(const char *v)
 319{
 320        if (!strcasecmp(v, "overwrite"))
 321                return combine_notes_overwrite;
 322        else if (!strcasecmp(v, "ignore"))
 323                return combine_notes_ignore;
 324        else if (!strcasecmp(v, "concatenate"))
 325                return combine_notes_concatenate;
 326        else if (!strcasecmp(v, "cat_sort_uniq"))
 327                return combine_notes_cat_sort_uniq;
 328        else
 329                return NULL;
 330}
 331
 332static int notes_rewrite_config(const char *k, const char *v, void *cb)
 333{
 334        struct notes_rewrite_cfg *c = cb;
 335        if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
 336                c->enabled = git_config_bool(k, v);
 337                return 0;
 338        } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
 339                if (!v)
 340                        config_error_nonbool(k);
 341                c->combine = parse_combine_notes_fn(v);
 342                if (!c->combine) {
 343                        error(_("Bad notes.rewriteMode value: '%s'"), v);
 344                        return 1;
 345                }
 346                return 0;
 347        } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
 348                /* note that a refs/ prefix is implied in the
 349                 * underlying for_each_glob_ref */
 350                if (!prefixcmp(v, "refs/notes/"))
 351                        string_list_add_refs_by_glob(c->refs, v);
 352                else
 353                        warning(_("Refusing to rewrite notes in %s"
 354                                " (outside of refs/notes/)"), v);
 355                return 0;
 356        }
 357
 358        return 0;
 359}
 360
 361
 362struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
 363{
 364        struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
 365        const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
 366        const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
 367        c->cmd = cmd;
 368        c->enabled = 1;
 369        c->combine = combine_notes_concatenate;
 370        c->refs = xcalloc(1, sizeof(struct string_list));
 371        c->refs->strdup_strings = 1;
 372        c->refs_from_env = 0;
 373        c->mode_from_env = 0;
 374        if (rewrite_mode_env) {
 375                c->mode_from_env = 1;
 376                c->combine = parse_combine_notes_fn(rewrite_mode_env);
 377                if (!c->combine)
 378                        /* TRANSLATORS: The first %s is the name of the
 379                           environment variable, the second %s is its value */
 380                        error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT,
 381                                        rewrite_mode_env);
 382        }
 383        if (rewrite_refs_env) {
 384                c->refs_from_env = 1;
 385                string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
 386        }
 387        git_config(notes_rewrite_config, c);
 388        if (!c->enabled || !c->refs->nr) {
 389                string_list_clear(c->refs, 0);
 390                free(c->refs);
 391                free(c);
 392                return NULL;
 393        }
 394        c->trees = load_notes_trees(c->refs);
 395        string_list_clear(c->refs, 0);
 396        free(c->refs);
 397        return c;
 398}
 399
 400int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
 401                          const unsigned char *from_obj, const unsigned char *to_obj)
 402{
 403        int ret = 0;
 404        int i;
 405        for (i = 0; c->trees[i]; i++)
 406                ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
 407        return ret;
 408}
 409
 410void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)
 411{
 412        int i;
 413        for (i = 0; c->trees[i]; i++) {
 414                commit_notes(c->trees[i], "Notes added by 'git notes copy'");
 415                free_notes(c->trees[i]);
 416        }
 417        free(c->trees);
 418        free(c);
 419}
 420
 421static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 422{
 423        struct strbuf buf = STRBUF_INIT;
 424        struct notes_rewrite_cfg *c = NULL;
 425        struct notes_tree *t = NULL;
 426        int ret = 0;
 427
 428        if (rewrite_cmd) {
 429                c = init_copy_notes_for_rewrite(rewrite_cmd);
 430                if (!c)
 431                        return 0;
 432        } else {
 433                init_notes(NULL, NULL, NULL, 0);
 434                t = &default_notes_tree;
 435        }
 436
 437        while (strbuf_getline(&buf, stdin, '\n') != EOF) {
 438                unsigned char from_obj[20], to_obj[20];
 439                struct strbuf **split;
 440                int err;
 441
 442                split = strbuf_split(&buf, ' ');
 443                if (!split[0] || !split[1])
 444                        die(_("Malformed input line: '%s'."), buf.buf);
 445                strbuf_rtrim(split[0]);
 446                strbuf_rtrim(split[1]);
 447                if (get_sha1(split[0]->buf, from_obj))
 448                        die(_("Failed to resolve '%s' as a valid ref."), split[0]->buf);
 449                if (get_sha1(split[1]->buf, to_obj))
 450                        die(_("Failed to resolve '%s' as a valid ref."), split[1]->buf);
 451
 452                if (rewrite_cmd)
 453                        err = copy_note_for_rewrite(c, from_obj, to_obj);
 454                else
 455                        err = copy_note(t, from_obj, to_obj, force,
 456                                        combine_notes_overwrite);
 457
 458                if (err) {
 459                        error(_("Failed to copy notes from '%s' to '%s'"),
 460                              split[0]->buf, split[1]->buf);
 461                        ret = 1;
 462                }
 463
 464                strbuf_list_free(split);
 465        }
 466
 467        if (!rewrite_cmd) {
 468                commit_notes(t, "Notes added by 'git notes copy'");
 469                free_notes(t);
 470        } else {
 471                finish_copy_notes_for_rewrite(c);
 472        }
 473        return ret;
 474}
 475
 476static struct notes_tree *init_notes_check(const char *subcommand)
 477{
 478        struct notes_tree *t;
 479        init_notes(NULL, NULL, NULL, 0);
 480        t = &default_notes_tree;
 481
 482        if (prefixcmp(t->ref, "refs/notes/"))
 483                die("Refusing to %s notes in %s (outside of refs/notes/)",
 484                    subcommand, t->ref);
 485        return t;
 486}
 487
 488static int list(int argc, const char **argv, const char *prefix)
 489{
 490        struct notes_tree *t;
 491        unsigned char object[20];
 492        const unsigned char *note;
 493        int retval = -1;
 494        struct option options[] = {
 495                OPT_END()
 496        };
 497
 498        if (argc)
 499                argc = parse_options(argc, argv, prefix, options,
 500                                     git_notes_list_usage, 0);
 501
 502        if (1 < argc) {
 503                error(_("too many parameters"));
 504                usage_with_options(git_notes_list_usage, options);
 505        }
 506
 507        t = init_notes_check("list");
 508        if (argc) {
 509                if (get_sha1(argv[0], object))
 510                        die(_("Failed to resolve '%s' as a valid ref."), argv[0]);
 511                note = get_note(t, object);
 512                if (note) {
 513                        puts(sha1_to_hex(note));
 514                        retval = 0;
 515                } else
 516                        retval = error(_("No note found for object %s."),
 517                                       sha1_to_hex(object));
 518        } else
 519                retval = for_each_note(t, 0, list_each_note, NULL);
 520
 521        free_notes(t);
 522        return retval;
 523}
 524
 525static int append_edit(int argc, const char **argv, const char *prefix);
 526
 527static int add(int argc, const char **argv, const char *prefix)
 528{
 529        int retval = 0, force = 0;
 530        const char *object_ref;
 531        struct notes_tree *t;
 532        unsigned char object[20], new_note[20];
 533        char logmsg[100];
 534        const unsigned char *note;
 535        struct msg_arg msg = { 0, 0, STRBUF_INIT };
 536        struct option options[] = {
 537                { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
 538                        N_("note contents as a string"), PARSE_OPT_NONEG,
 539                        parse_msg_arg},
 540                { OPTION_CALLBACK, 'F', "file", &msg, N_("file"),
 541                        N_("note contents in a file"), PARSE_OPT_NONEG,
 542                        parse_file_arg},
 543                { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"),
 544                        N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
 545                        parse_reedit_arg},
 546                { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"),
 547                        N_("reuse specified note object"), PARSE_OPT_NONEG,
 548                        parse_reuse_arg},
 549                OPT__FORCE(&force, N_("replace existing notes")),
 550                OPT_END()
 551        };
 552
 553        argc = parse_options(argc, argv, prefix, options, git_notes_add_usage,
 554                             PARSE_OPT_KEEP_ARGV0);
 555
 556        if (2 < argc) {
 557                error(_("too many parameters"));
 558                usage_with_options(git_notes_add_usage, options);
 559        }
 560
 561        object_ref = argc > 1 ? argv[1] : "HEAD";
 562
 563        if (get_sha1(object_ref, object))
 564                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
 565
 566        t = init_notes_check("add");
 567        note = get_note(t, object);
 568
 569        if (note) {
 570                if (!force) {
 571                        if (!msg.given) {
 572                                /*
 573                                 * Redirect to "edit" subcommand.
 574                                 *
 575                                 * We only end up here if none of -m/-F/-c/-C
 576                                 * or -f are given. The original args are
 577                                 * therefore still in argv[0-1].
 578                                 */
 579                                argv[0] = "edit";
 580                                free_notes(t);
 581                                return append_edit(argc, argv, prefix);
 582                        }
 583                        retval = error(_("Cannot add notes. Found existing notes "
 584                                       "for object %s. Use '-f' to overwrite "
 585                                       "existing notes"), sha1_to_hex(object));
 586                        goto out;
 587                }
 588                fprintf(stderr, _("Overwriting existing notes for object %s\n"),
 589                        sha1_to_hex(object));
 590        }
 591
 592        create_note(object, &msg, 0, note, new_note);
 593
 594        if (is_null_sha1(new_note))
 595                remove_note(t, object);
 596        else if (add_note(t, object, new_note, combine_notes_overwrite))
 597                die("BUG: combine_notes_overwrite failed");
 598
 599        snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
 600                 is_null_sha1(new_note) ? "removed" : "added", "add");
 601        commit_notes(t, logmsg);
 602out:
 603        free_notes(t);
 604        strbuf_release(&(msg.buf));
 605        return retval;
 606}
 607
 608static int copy(int argc, const char **argv, const char *prefix)
 609{
 610        int retval = 0, force = 0, from_stdin = 0;
 611        const unsigned char *from_note, *note;
 612        const char *object_ref;
 613        unsigned char object[20], from_obj[20];
 614        struct notes_tree *t;
 615        const char *rewrite_cmd = NULL;
 616        struct option options[] = {
 617                OPT__FORCE(&force, N_("replace existing notes")),
 618                OPT_BOOLEAN(0, "stdin", &from_stdin, N_("read objects from stdin")),
 619                OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"),
 620                           N_("load rewriting config for <command> (implies "
 621                              "--stdin)")),
 622                OPT_END()
 623        };
 624
 625        argc = parse_options(argc, argv, prefix, options, git_notes_copy_usage,
 626                             0);
 627
 628        if (from_stdin || rewrite_cmd) {
 629                if (argc) {
 630                        error(_("too many parameters"));
 631                        usage_with_options(git_notes_copy_usage, options);
 632                } else {
 633                        return notes_copy_from_stdin(force, rewrite_cmd);
 634                }
 635        }
 636
 637        if (argc < 2) {
 638                error(_("too few parameters"));
 639                usage_with_options(git_notes_copy_usage, options);
 640        }
 641        if (2 < argc) {
 642                error(_("too many parameters"));
 643                usage_with_options(git_notes_copy_usage, options);
 644        }
 645
 646        if (get_sha1(argv[0], from_obj))
 647                die(_("Failed to resolve '%s' as a valid ref."), argv[0]);
 648
 649        object_ref = 1 < argc ? argv[1] : "HEAD";
 650
 651        if (get_sha1(object_ref, object))
 652                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
 653
 654        t = init_notes_check("copy");
 655        note = get_note(t, object);
 656
 657        if (note) {
 658                if (!force) {
 659                        retval = error(_("Cannot copy notes. Found existing "
 660                                       "notes for object %s. Use '-f' to "
 661                                       "overwrite existing notes"),
 662                                       sha1_to_hex(object));
 663                        goto out;
 664                }
 665                fprintf(stderr, _("Overwriting existing notes for object %s\n"),
 666                        sha1_to_hex(object));
 667        }
 668
 669        from_note = get_note(t, from_obj);
 670        if (!from_note) {
 671                retval = error(_("Missing notes on source object %s. Cannot "
 672                               "copy."), sha1_to_hex(from_obj));
 673                goto out;
 674        }
 675
 676        if (add_note(t, object, from_note, combine_notes_overwrite))
 677                die("BUG: combine_notes_overwrite failed");
 678        commit_notes(t, "Notes added by 'git notes copy'");
 679out:
 680        free_notes(t);
 681        return retval;
 682}
 683
 684static int append_edit(int argc, const char **argv, const char *prefix)
 685{
 686        const char *object_ref;
 687        struct notes_tree *t;
 688        unsigned char object[20], new_note[20];
 689        const unsigned char *note;
 690        char logmsg[100];
 691        const char * const *usage;
 692        struct msg_arg msg = { 0, 0, STRBUF_INIT };
 693        struct option options[] = {
 694                { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
 695                        N_("note contents as a string"), PARSE_OPT_NONEG,
 696                        parse_msg_arg},
 697                { OPTION_CALLBACK, 'F', "file", &msg, N_("file"),
 698                        N_("note contents in a file"), PARSE_OPT_NONEG,
 699                        parse_file_arg},
 700                { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"),
 701                        N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
 702                        parse_reedit_arg},
 703                { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"),
 704                        N_("reuse specified note object"), PARSE_OPT_NONEG,
 705                        parse_reuse_arg},
 706                OPT_END()
 707        };
 708        int edit = !strcmp(argv[0], "edit");
 709
 710        usage = edit ? git_notes_edit_usage : git_notes_append_usage;
 711        argc = parse_options(argc, argv, prefix, options, usage,
 712                             PARSE_OPT_KEEP_ARGV0);
 713
 714        if (2 < argc) {
 715                error(_("too many parameters"));
 716                usage_with_options(usage, options);
 717        }
 718
 719        if (msg.given && edit)
 720                fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
 721                        "for the 'edit' subcommand.\n"
 722                        "Please use 'git notes add -f -m/-F/-c/-C' instead.\n"));
 723
 724        object_ref = 1 < argc ? argv[1] : "HEAD";
 725
 726        if (get_sha1(object_ref, object))
 727                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
 728
 729        t = init_notes_check(argv[0]);
 730        note = get_note(t, object);
 731
 732        create_note(object, &msg, !edit, note, new_note);
 733
 734        if (is_null_sha1(new_note))
 735                remove_note(t, object);
 736        else if (add_note(t, object, new_note, combine_notes_overwrite))
 737                die("BUG: combine_notes_overwrite failed");
 738
 739        snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
 740                 is_null_sha1(new_note) ? "removed" : "added", argv[0]);
 741        commit_notes(t, logmsg);
 742        free_notes(t);
 743        strbuf_release(&(msg.buf));
 744        return 0;
 745}
 746
 747static int show(int argc, const char **argv, const char *prefix)
 748{
 749        const char *object_ref;
 750        struct notes_tree *t;
 751        unsigned char object[20];
 752        const unsigned char *note;
 753        int retval;
 754        struct option options[] = {
 755                OPT_END()
 756        };
 757
 758        argc = parse_options(argc, argv, prefix, options, git_notes_show_usage,
 759                             0);
 760
 761        if (1 < argc) {
 762                error(_("too many parameters"));
 763                usage_with_options(git_notes_show_usage, options);
 764        }
 765
 766        object_ref = argc ? argv[0] : "HEAD";
 767
 768        if (get_sha1(object_ref, object))
 769                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
 770
 771        t = init_notes_check("show");
 772        note = get_note(t, object);
 773
 774        if (!note)
 775                retval = error(_("No note found for object %s."),
 776                               sha1_to_hex(object));
 777        else {
 778                const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
 779                retval = execv_git_cmd(show_args);
 780        }
 781        free_notes(t);
 782        return retval;
 783}
 784
 785static int merge_abort(struct notes_merge_options *o)
 786{
 787        int ret = 0;
 788
 789        /*
 790         * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call
 791         * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
 792         */
 793
 794        if (delete_ref("NOTES_MERGE_PARTIAL", NULL, 0))
 795                ret += error("Failed to delete ref NOTES_MERGE_PARTIAL");
 796        if (delete_ref("NOTES_MERGE_REF", NULL, REF_NODEREF))
 797                ret += error("Failed to delete ref NOTES_MERGE_REF");
 798        if (notes_merge_abort(o))
 799                ret += error("Failed to remove 'git notes merge' worktree");
 800        return ret;
 801}
 802
 803static int merge_commit(struct notes_merge_options *o)
 804{
 805        struct strbuf msg = STRBUF_INIT;
 806        unsigned char sha1[20], parent_sha1[20];
 807        struct notes_tree *t;
 808        struct commit *partial;
 809        struct pretty_print_context pretty_ctx;
 810        void *local_ref_to_free;
 811        int ret;
 812
 813        /*
 814         * Read partial merge result from .git/NOTES_MERGE_PARTIAL,
 815         * and target notes ref from .git/NOTES_MERGE_REF.
 816         */
 817
 818        if (get_sha1("NOTES_MERGE_PARTIAL", sha1))
 819                die("Failed to read ref NOTES_MERGE_PARTIAL");
 820        else if (!(partial = lookup_commit_reference(sha1)))
 821                die("Could not find commit from NOTES_MERGE_PARTIAL.");
 822        else if (parse_commit(partial))
 823                die("Could not parse commit from NOTES_MERGE_PARTIAL.");
 824
 825        if (partial->parents)
 826                hashcpy(parent_sha1, partial->parents->item->object.sha1);
 827        else
 828                hashclr(parent_sha1);
 829
 830        t = xcalloc(1, sizeof(struct notes_tree));
 831        init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 832
 833        o->local_ref = local_ref_to_free =
 834                resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
 835        if (!o->local_ref)
 836                die("Failed to resolve NOTES_MERGE_REF");
 837
 838        if (notes_merge_commit(o, t, partial, sha1))
 839                die("Failed to finalize notes merge");
 840
 841        /* Reuse existing commit message in reflog message */
 842        memset(&pretty_ctx, 0, sizeof(pretty_ctx));
 843        format_commit_message(partial, "%s", &msg, &pretty_ctx);
 844        strbuf_trim(&msg);
 845        strbuf_insert(&msg, 0, "notes: ", 7);
 846        update_ref(msg.buf, o->local_ref, sha1,
 847                   is_null_sha1(parent_sha1) ? NULL : parent_sha1,
 848                   0, DIE_ON_ERR);
 849
 850        free_notes(t);
 851        strbuf_release(&msg);
 852        ret = merge_abort(o);
 853        free(local_ref_to_free);
 854        return ret;
 855}
 856
 857static int merge(int argc, const char **argv, const char *prefix)
 858{
 859        struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
 860        unsigned char result_sha1[20];
 861        struct notes_tree *t;
 862        struct notes_merge_options o;
 863        int do_merge = 0, do_commit = 0, do_abort = 0;
 864        int verbosity = 0, result;
 865        const char *strategy = NULL;
 866        struct option options[] = {
 867                OPT_GROUP(N_("General options")),
 868                OPT__VERBOSITY(&verbosity),
 869                OPT_GROUP(N_("Merge options")),
 870                OPT_STRING('s', "strategy", &strategy, N_("strategy"),
 871                           N_("resolve notes conflicts using the given strategy "
 872                              "(manual/ours/theirs/union/cat_sort_uniq)")),
 873                OPT_GROUP(N_("Committing unmerged notes")),
 874                { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL,
 875                        N_("finalize notes merge by committing unmerged notes"),
 876                        PARSE_OPT_NOARG | PARSE_OPT_NONEG },
 877                OPT_GROUP(N_("Aborting notes merge resolution")),
 878                { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL,
 879                        N_("abort notes merge"),
 880                        PARSE_OPT_NOARG | PARSE_OPT_NONEG },
 881                OPT_END()
 882        };
 883
 884        argc = parse_options(argc, argv, prefix, options,
 885                             git_notes_merge_usage, 0);
 886
 887        if (strategy || do_commit + do_abort == 0)
 888                do_merge = 1;
 889        if (do_merge + do_commit + do_abort != 1) {
 890                error("cannot mix --commit, --abort or -s/--strategy");
 891                usage_with_options(git_notes_merge_usage, options);
 892        }
 893
 894        if (do_merge && argc != 1) {
 895                error("Must specify a notes ref to merge");
 896                usage_with_options(git_notes_merge_usage, options);
 897        } else if (!do_merge && argc) {
 898                error("too many parameters");
 899                usage_with_options(git_notes_merge_usage, options);
 900        }
 901
 902        init_notes_merge_options(&o);
 903        o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT;
 904
 905        if (do_abort)
 906                return merge_abort(&o);
 907        if (do_commit)
 908                return merge_commit(&o);
 909
 910        o.local_ref = default_notes_ref();
 911        strbuf_addstr(&remote_ref, argv[0]);
 912        expand_notes_ref(&remote_ref);
 913        o.remote_ref = remote_ref.buf;
 914
 915        if (strategy) {
 916                if (!strcmp(strategy, "manual"))
 917                        o.strategy = NOTES_MERGE_RESOLVE_MANUAL;
 918                else if (!strcmp(strategy, "ours"))
 919                        o.strategy = NOTES_MERGE_RESOLVE_OURS;
 920                else if (!strcmp(strategy, "theirs"))
 921                        o.strategy = NOTES_MERGE_RESOLVE_THEIRS;
 922                else if (!strcmp(strategy, "union"))
 923                        o.strategy = NOTES_MERGE_RESOLVE_UNION;
 924                else if (!strcmp(strategy, "cat_sort_uniq"))
 925                        o.strategy = NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ;
 926                else {
 927                        error("Unknown -s/--strategy: %s", strategy);
 928                        usage_with_options(git_notes_merge_usage, options);
 929                }
 930        }
 931
 932        t = init_notes_check("merge");
 933
 934        strbuf_addf(&msg, "notes: Merged notes from %s into %s",
 935                    remote_ref.buf, default_notes_ref());
 936        strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */
 937
 938        result = notes_merge(&o, t, result_sha1);
 939
 940        if (result >= 0) /* Merge resulted (trivially) in result_sha1 */
 941                /* Update default notes ref with new commit */
 942                update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
 943                           0, DIE_ON_ERR);
 944        else { /* Merge has unresolved conflicts */
 945                /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
 946                update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
 947                           0, DIE_ON_ERR);
 948                /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
 949                if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
 950                        die("Failed to store link to current notes ref (%s)",
 951                            default_notes_ref());
 952                printf("Automatic notes merge failed. Fix conflicts in %s and "
 953                       "commit the result with 'git notes merge --commit', or "
 954                       "abort the merge with 'git notes merge --abort'.\n",
 955                       git_path(NOTES_MERGE_WORKTREE));
 956        }
 957
 958        free_notes(t);
 959        strbuf_release(&remote_ref);
 960        strbuf_release(&msg);
 961        return result < 0; /* return non-zero on conflicts */
 962}
 963
 964#define IGNORE_MISSING 1
 965
 966static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
 967{
 968        int status;
 969        unsigned char sha1[20];
 970        if (get_sha1(name, sha1))
 971                return error(_("Failed to resolve '%s' as a valid ref."), name);
 972        status = remove_note(t, sha1);
 973        if (status)
 974                fprintf(stderr, _("Object %s has no note\n"), name);
 975        else
 976                fprintf(stderr, _("Removing note for object %s\n"), name);
 977        return (flag & IGNORE_MISSING) ? 0 : status;
 978}
 979
 980static int remove_cmd(int argc, const char **argv, const char *prefix)
 981{
 982        unsigned flag = 0;
 983        int from_stdin = 0;
 984        struct option options[] = {
 985                OPT_BIT(0, "ignore-missing", &flag,
 986                        N_("attempt to remove non-existent note is not an error"),
 987                        IGNORE_MISSING),
 988                OPT_BOOLEAN(0, "stdin", &from_stdin,
 989                            N_("read object names from the standard input")),
 990                OPT_END()
 991        };
 992        struct notes_tree *t;
 993        int retval = 0;
 994
 995        argc = parse_options(argc, argv, prefix, options,
 996                             git_notes_remove_usage, 0);
 997
 998        t = init_notes_check("remove");
 999
1000        if (!argc && !from_stdin) {
1001                retval = remove_one_note(t, "HEAD", flag);
1002        } else {
1003                while (*argv) {
1004                        retval |= remove_one_note(t, *argv, flag);
1005                        argv++;
1006                }
1007        }
1008        if (from_stdin) {
1009                struct strbuf sb = STRBUF_INIT;
1010                while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
1011                        strbuf_rtrim(&sb);
1012                        retval |= remove_one_note(t, sb.buf, flag);
1013                }
1014                strbuf_release(&sb);
1015        }
1016        if (!retval)
1017                commit_notes(t, "Notes removed by 'git notes remove'");
1018        free_notes(t);
1019        return retval;
1020}
1021
1022static int prune(int argc, const char **argv, const char *prefix)
1023{
1024        struct notes_tree *t;
1025        int show_only = 0, verbose = 0;
1026        struct option options[] = {
1027                OPT__DRY_RUN(&show_only, "do not remove, show only"),
1028                OPT__VERBOSE(&verbose, "report pruned notes"),
1029                OPT_END()
1030        };
1031
1032        argc = parse_options(argc, argv, prefix, options, git_notes_prune_usage,
1033                             0);
1034
1035        if (argc) {
1036                error(_("too many parameters"));
1037                usage_with_options(git_notes_prune_usage, options);
1038        }
1039
1040        t = init_notes_check("prune");
1041
1042        prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
1043                (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
1044        if (!show_only)
1045                commit_notes(t, "Notes removed by 'git notes prune'");
1046        free_notes(t);
1047        return 0;
1048}
1049
1050static int get_ref(int argc, const char **argv, const char *prefix)
1051{
1052        struct option options[] = { OPT_END() };
1053        argc = parse_options(argc, argv, prefix, options,
1054                             git_notes_get_ref_usage, 0);
1055
1056        if (argc) {
1057                error("too many parameters");
1058                usage_with_options(git_notes_get_ref_usage, options);
1059        }
1060
1061        puts(default_notes_ref());
1062        return 0;
1063}
1064
1065int cmd_notes(int argc, const char **argv, const char *prefix)
1066{
1067        int result;
1068        const char *override_notes_ref = NULL;
1069        struct option options[] = {
1070                OPT_STRING(0, "ref", &override_notes_ref, N_("notes_ref"),
1071                           N_("use notes from <notes_ref>")),
1072                OPT_END()
1073        };
1074
1075        git_config(git_default_config, NULL);
1076        argc = parse_options(argc, argv, prefix, options, git_notes_usage,
1077                             PARSE_OPT_STOP_AT_NON_OPTION);
1078
1079        if (override_notes_ref) {
1080                struct strbuf sb = STRBUF_INIT;
1081                strbuf_addstr(&sb, override_notes_ref);
1082                expand_notes_ref(&sb);
1083                setenv("GIT_NOTES_REF", sb.buf, 1);
1084                strbuf_release(&sb);
1085        }
1086
1087        if (argc < 1 || !strcmp(argv[0], "list"))
1088                result = list(argc, argv, prefix);
1089        else if (!strcmp(argv[0], "add"))
1090                result = add(argc, argv, prefix);
1091        else if (!strcmp(argv[0], "copy"))
1092                result = copy(argc, argv, prefix);
1093        else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit"))
1094                result = append_edit(argc, argv, prefix);
1095        else if (!strcmp(argv[0], "show"))
1096                result = show(argc, argv, prefix);
1097        else if (!strcmp(argv[0], "merge"))
1098                result = merge(argc, argv, prefix);
1099        else if (!strcmp(argv[0], "remove"))
1100                result = remove_cmd(argc, argv, prefix);
1101        else if (!strcmp(argv[0], "prune"))
1102                result = prune(argc, argv, prefix);
1103        else if (!strcmp(argv[0], "get-ref"))
1104                result = get_ref(argc, argv, prefix);
1105        else {
1106                result = error(_("Unknown subcommand: %s"), argv[0]);
1107                usage_with_options(git_notes_usage, options);
1108        }
1109
1110        return result ? 1 : 0;
1111}