Code

builtin/notes.c: Refactor creation of notes commits.
[git.git] / builtin / notes.c
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  */
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"
22 static const char * const git_notes_usage[] = {
23         "git notes [--ref <notes_ref>] [list [<object>]]",
24         "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
25         "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>",
26         "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
27         "git notes [--ref <notes_ref>] edit [<object>]",
28         "git notes [--ref <notes_ref>] show [<object>]",
29         "git notes [--ref <notes_ref>] merge [-v | -q] <notes_ref>",
30         "git notes [--ref <notes_ref>] remove [<object>]",
31         "git notes [--ref <notes_ref>] prune [-n | -v]",
32         NULL
33 };
35 static const char * const git_notes_list_usage[] = {
36         "git notes [list [<object>]]",
37         NULL
38 };
40 static const char * const git_notes_add_usage[] = {
41         "git notes add [<options>] [<object>]",
42         NULL
43 };
45 static const char * const git_notes_copy_usage[] = {
46         "git notes copy [<options>] <from-object> <to-object>",
47         "git notes copy --stdin [<from-object> <to-object>]...",
48         NULL
49 };
51 static const char * const git_notes_append_usage[] = {
52         "git notes append [<options>] [<object>]",
53         NULL
54 };
56 static const char * const git_notes_edit_usage[] = {
57         "git notes edit [<object>]",
58         NULL
59 };
61 static const char * const git_notes_show_usage[] = {
62         "git notes show [<object>]",
63         NULL
64 };
66 static const char * const git_notes_merge_usage[] = {
67         "git notes merge [<options>] <notes_ref>",
68         NULL
69 };
71 static const char * const git_notes_remove_usage[] = {
72         "git notes remove [<object>]",
73         NULL
74 };
76 static const char * const git_notes_prune_usage[] = {
77         "git notes prune [<options>]",
78         NULL
79 };
81 static const char note_template[] =
82         "\n"
83         "#\n"
84         "# Write/edit the notes for the following object:\n"
85         "#\n";
87 struct msg_arg {
88         int given;
89         int use_editor;
90         struct strbuf buf;
91 };
93 static void expand_notes_ref(struct strbuf *sb)
94 {
95         if (!prefixcmp(sb->buf, "refs/notes/"))
96                 return; /* we're happy */
97         else if (!prefixcmp(sb->buf, "notes/"))
98                 strbuf_insert(sb, 0, "refs/", 5);
99         else
100                 strbuf_insert(sb, 0, "refs/notes/", 11);
103 static int list_each_note(const unsigned char *object_sha1,
104                 const unsigned char *note_sha1, char *note_path,
105                 void *cb_data)
107         printf("%s %s\n", sha1_to_hex(note_sha1), sha1_to_hex(object_sha1));
108         return 0;
111 static void write_note_data(int fd, const unsigned char *sha1)
113         unsigned long size;
114         enum object_type type;
115         char *buf = read_sha1_file(sha1, &type, &size);
116         if (buf) {
117                 if (size)
118                         write_or_die(fd, buf, size);
119                 free(buf);
120         }
123 static void write_commented_object(int fd, const unsigned char *object)
125         const char *show_args[5] =
126                 {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL};
127         struct child_process show;
128         struct strbuf buf = STRBUF_INIT;
129         FILE *show_out;
131         /* Invoke "git show --stat --no-notes $object" */
132         memset(&show, 0, sizeof(show));
133         show.argv = show_args;
134         show.no_stdin = 1;
135         show.out = -1;
136         show.err = 0;
137         show.git_cmd = 1;
138         if (start_command(&show))
139                 die("unable to start 'show' for object '%s'",
140                     sha1_to_hex(object));
142         /* Open the output as FILE* so strbuf_getline() can be used. */
143         show_out = xfdopen(show.out, "r");
144         if (show_out == NULL)
145                 die_errno("can't fdopen 'show' output fd");
147         /* Prepend "# " to each output line and write result to 'fd' */
148         while (strbuf_getline(&buf, show_out, '\n') != EOF) {
149                 write_or_die(fd, "# ", 2);
150                 write_or_die(fd, buf.buf, buf.len);
151                 write_or_die(fd, "\n", 1);
152         }
153         strbuf_release(&buf);
154         if (fclose(show_out))
155                 die_errno("failed to close pipe to 'show' for object '%s'",
156                           sha1_to_hex(object));
157         if (finish_command(&show))
158                 die("failed to finish 'show' for object '%s'",
159                     sha1_to_hex(object));
162 static void create_note(const unsigned char *object, struct msg_arg *msg,
163                         int append_only, const unsigned char *prev,
164                         unsigned char *result)
166         char *path = NULL;
168         if (msg->use_editor || !msg->given) {
169                 int fd;
171                 /* write the template message before editing: */
172                 path = git_pathdup("NOTES_EDITMSG");
173                 fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
174                 if (fd < 0)
175                         die_errno("could not create file '%s'", path);
177                 if (msg->given)
178                         write_or_die(fd, msg->buf.buf, msg->buf.len);
179                 else if (prev && !append_only)
180                         write_note_data(fd, prev);
181                 write_or_die(fd, note_template, strlen(note_template));
183                 write_commented_object(fd, object);
185                 close(fd);
186                 strbuf_reset(&(msg->buf));
188                 if (launch_editor(path, &(msg->buf), NULL)) {
189                         die("Please supply the note contents using either -m" \
190                             " or -F option");
191                 }
192                 stripspace(&(msg->buf), 1);
193         }
195         if (prev && append_only) {
196                 /* Append buf to previous note contents */
197                 unsigned long size;
198                 enum object_type type;
199                 char *prev_buf = read_sha1_file(prev, &type, &size);
201                 strbuf_grow(&(msg->buf), size + 1);
202                 if (msg->buf.len && prev_buf && size)
203                         strbuf_insert(&(msg->buf), 0, "\n", 1);
204                 if (prev_buf && size)
205                         strbuf_insert(&(msg->buf), 0, prev_buf, size);
206                 free(prev_buf);
207         }
209         if (!msg->buf.len) {
210                 fprintf(stderr, "Removing note for object %s\n",
211                         sha1_to_hex(object));
212                 hashclr(result);
213         } else {
214                 if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) {
215                         error("unable to write note object");
216                         if (path)
217                                 error("The note contents has been left in %s",
218                                       path);
219                         exit(128);
220                 }
221         }
223         if (path) {
224                 unlink_or_warn(path);
225                 free(path);
226         }
229 static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
231         struct msg_arg *msg = opt->value;
233         strbuf_grow(&(msg->buf), strlen(arg) + 2);
234         if (msg->buf.len)
235                 strbuf_addch(&(msg->buf), '\n');
236         strbuf_addstr(&(msg->buf), arg);
237         stripspace(&(msg->buf), 0);
239         msg->given = 1;
240         return 0;
243 static int parse_file_arg(const struct option *opt, const char *arg, int unset)
245         struct msg_arg *msg = opt->value;
247         if (msg->buf.len)
248                 strbuf_addch(&(msg->buf), '\n');
249         if (!strcmp(arg, "-")) {
250                 if (strbuf_read(&(msg->buf), 0, 1024) < 0)
251                         die_errno("cannot read '%s'", arg);
252         } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
253                 die_errno("could not open or read '%s'", arg);
254         stripspace(&(msg->buf), 0);
256         msg->given = 1;
257         return 0;
260 static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
262         struct msg_arg *msg = opt->value;
263         char *buf;
264         unsigned char object[20];
265         enum object_type type;
266         unsigned long len;
268         if (msg->buf.len)
269                 strbuf_addch(&(msg->buf), '\n');
271         if (get_sha1(arg, object))
272                 die("Failed to resolve '%s' as a valid ref.", arg);
273         if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
274                 free(buf);
275                 die("Failed to read object '%s'.", arg);;
276         }
277         strbuf_add(&(msg->buf), buf, len);
278         free(buf);
280         msg->given = 1;
281         return 0;
284 static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
286         struct msg_arg *msg = opt->value;
287         msg->use_editor = 1;
288         return parse_reuse_arg(opt, arg, unset);
291 void commit_notes(struct notes_tree *t, const char *msg)
293         struct strbuf buf = STRBUF_INIT;
294         unsigned char commit_sha1[20];
296         if (!t)
297                 t = &default_notes_tree;
298         if (!t->initialized || !t->ref || !*t->ref)
299                 die("Cannot commit uninitialized/unreferenced notes tree");
300         if (!t->dirty)
301                 return; /* don't have to commit an unchanged tree */
303         /* Prepare commit message and reflog message */
304         strbuf_addstr(&buf, "notes: "); /* commit message starts at index 7 */
305         strbuf_addstr(&buf, msg);
306         if (buf.buf[buf.len - 1] != '\n')
307                 strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */
309         create_notes_commit(t, NULL, buf.buf + 7, commit_sha1);
310         update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
312         strbuf_release(&buf);
315 combine_notes_fn parse_combine_notes_fn(const char *v)
317         if (!strcasecmp(v, "overwrite"))
318                 return combine_notes_overwrite;
319         else if (!strcasecmp(v, "ignore"))
320                 return combine_notes_ignore;
321         else if (!strcasecmp(v, "concatenate"))
322                 return combine_notes_concatenate;
323         else
324                 return NULL;
327 static int notes_rewrite_config(const char *k, const char *v, void *cb)
329         struct notes_rewrite_cfg *c = cb;
330         if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
331                 c->enabled = git_config_bool(k, v);
332                 return 0;
333         } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
334                 if (!v)
335                         config_error_nonbool(k);
336                 c->combine = parse_combine_notes_fn(v);
337                 if (!c->combine) {
338                         error("Bad notes.rewriteMode value: '%s'", v);
339                         return 1;
340                 }
341                 return 0;
342         } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
343                 /* note that a refs/ prefix is implied in the
344                  * underlying for_each_glob_ref */
345                 if (!prefixcmp(v, "refs/notes/"))
346                         string_list_add_refs_by_glob(c->refs, v);
347                 else
348                         warning("Refusing to rewrite notes in %s"
349                                 " (outside of refs/notes/)", v);
350                 return 0;
351         }
353         return 0;
357 struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
359         struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
360         const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
361         const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
362         c->cmd = cmd;
363         c->enabled = 1;
364         c->combine = combine_notes_concatenate;
365         c->refs = xcalloc(1, sizeof(struct string_list));
366         c->refs->strdup_strings = 1;
367         c->refs_from_env = 0;
368         c->mode_from_env = 0;
369         if (rewrite_mode_env) {
370                 c->mode_from_env = 1;
371                 c->combine = parse_combine_notes_fn(rewrite_mode_env);
372                 if (!c->combine)
373                         error("Bad " GIT_NOTES_REWRITE_MODE_ENVIRONMENT
374                               " value: '%s'", rewrite_mode_env);
375         }
376         if (rewrite_refs_env) {
377                 c->refs_from_env = 1;
378                 string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
379         }
380         git_config(notes_rewrite_config, c);
381         if (!c->enabled || !c->refs->nr) {
382                 string_list_clear(c->refs, 0);
383                 free(c->refs);
384                 free(c);
385                 return NULL;
386         }
387         c->trees = load_notes_trees(c->refs);
388         string_list_clear(c->refs, 0);
389         free(c->refs);
390         return c;
393 int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
394                           const unsigned char *from_obj, const unsigned char *to_obj)
396         int ret = 0;
397         int i;
398         for (i = 0; c->trees[i]; i++)
399                 ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
400         return ret;
403 void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)
405         int i;
406         for (i = 0; c->trees[i]; i++) {
407                 commit_notes(c->trees[i], "Notes added by 'git notes copy'");
408                 free_notes(c->trees[i]);
409         }
410         free(c->trees);
411         free(c);
414 int notes_copy_from_stdin(int force, const char *rewrite_cmd)
416         struct strbuf buf = STRBUF_INIT;
417         struct notes_rewrite_cfg *c = NULL;
418         struct notes_tree *t = NULL;
419         int ret = 0;
421         if (rewrite_cmd) {
422                 c = init_copy_notes_for_rewrite(rewrite_cmd);
423                 if (!c)
424                         return 0;
425         } else {
426                 init_notes(NULL, NULL, NULL, 0);
427                 t = &default_notes_tree;
428         }
430         while (strbuf_getline(&buf, stdin, '\n') != EOF) {
431                 unsigned char from_obj[20], to_obj[20];
432                 struct strbuf **split;
433                 int err;
435                 split = strbuf_split(&buf, ' ');
436                 if (!split[0] || !split[1])
437                         die("Malformed input line: '%s'.", buf.buf);
438                 strbuf_rtrim(split[0]);
439                 strbuf_rtrim(split[1]);
440                 if (get_sha1(split[0]->buf, from_obj))
441                         die("Failed to resolve '%s' as a valid ref.", split[0]->buf);
442                 if (get_sha1(split[1]->buf, to_obj))
443                         die("Failed to resolve '%s' as a valid ref.", split[1]->buf);
445                 if (rewrite_cmd)
446                         err = copy_note_for_rewrite(c, from_obj, to_obj);
447                 else
448                         err = copy_note(t, from_obj, to_obj, force,
449                                         combine_notes_overwrite);
451                 if (err) {
452                         error("Failed to copy notes from '%s' to '%s'",
453                               split[0]->buf, split[1]->buf);
454                         ret = 1;
455                 }
457                 strbuf_list_free(split);
458         }
460         if (!rewrite_cmd) {
461                 commit_notes(t, "Notes added by 'git notes copy'");
462                 free_notes(t);
463         } else {
464                 finish_copy_notes_for_rewrite(c);
465         }
466         return ret;
469 static struct notes_tree *init_notes_check(const char *subcommand)
471         struct notes_tree *t;
472         init_notes(NULL, NULL, NULL, 0);
473         t = &default_notes_tree;
475         if (prefixcmp(t->ref, "refs/notes/"))
476                 die("Refusing to %s notes in %s (outside of refs/notes/)",
477                     subcommand, t->ref);
478         return t;
481 static int list(int argc, const char **argv, const char *prefix)
483         struct notes_tree *t;
484         unsigned char object[20];
485         const unsigned char *note;
486         int retval = -1;
487         struct option options[] = {
488                 OPT_END()
489         };
491         if (argc)
492                 argc = parse_options(argc, argv, prefix, options,
493                                      git_notes_list_usage, 0);
495         if (1 < argc) {
496                 error("too many parameters");
497                 usage_with_options(git_notes_list_usage, options);
498         }
500         t = init_notes_check("list");
501         if (argc) {
502                 if (get_sha1(argv[0], object))
503                         die("Failed to resolve '%s' as a valid ref.", argv[0]);
504                 note = get_note(t, object);
505                 if (note) {
506                         puts(sha1_to_hex(note));
507                         retval = 0;
508                 } else
509                         retval = error("No note found for object %s.",
510                                        sha1_to_hex(object));
511         } else
512                 retval = for_each_note(t, 0, list_each_note, NULL);
514         free_notes(t);
515         return retval;
518 static int add(int argc, const char **argv, const char *prefix)
520         int retval = 0, force = 0;
521         const char *object_ref;
522         struct notes_tree *t;
523         unsigned char object[20], new_note[20];
524         char logmsg[100];
525         const unsigned char *note;
526         struct msg_arg msg = { 0, 0, STRBUF_INIT };
527         struct option options[] = {
528                 { OPTION_CALLBACK, 'm', "message", &msg, "MSG",
529                         "note contents as a string", PARSE_OPT_NONEG,
530                         parse_msg_arg},
531                 { OPTION_CALLBACK, 'F', "file", &msg, "FILE",
532                         "note contents in a file", PARSE_OPT_NONEG,
533                         parse_file_arg},
534                 { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT",
535                         "reuse and edit specified note object", PARSE_OPT_NONEG,
536                         parse_reedit_arg},
537                 { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
538                         "reuse specified note object", PARSE_OPT_NONEG,
539                         parse_reuse_arg},
540                 OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
541                 OPT_END()
542         };
544         argc = parse_options(argc, argv, prefix, options, git_notes_add_usage,
545                              0);
547         if (1 < argc) {
548                 error("too many parameters");
549                 usage_with_options(git_notes_add_usage, options);
550         }
552         object_ref = argc ? argv[0] : "HEAD";
554         if (get_sha1(object_ref, object))
555                 die("Failed to resolve '%s' as a valid ref.", object_ref);
557         t = init_notes_check("add");
558         note = get_note(t, object);
560         if (note) {
561                 if (!force) {
562                         retval = error("Cannot add notes. Found existing notes "
563                                        "for object %s. Use '-f' to overwrite "
564                                        "existing notes", sha1_to_hex(object));
565                         goto out;
566                 }
567                 fprintf(stderr, "Overwriting existing notes for object %s\n",
568                         sha1_to_hex(object));
569         }
571         create_note(object, &msg, 0, note, new_note);
573         if (is_null_sha1(new_note))
574                 remove_note(t, object);
575         else if (add_note(t, object, new_note, combine_notes_overwrite))
576                 die("BUG: combine_notes_overwrite failed");
578         snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
579                  is_null_sha1(new_note) ? "removed" : "added", "add");
580         commit_notes(t, logmsg);
581 out:
582         free_notes(t);
583         strbuf_release(&(msg.buf));
584         return retval;
587 static int copy(int argc, const char **argv, const char *prefix)
589         int retval = 0, force = 0, from_stdin = 0;
590         const unsigned char *from_note, *note;
591         const char *object_ref;
592         unsigned char object[20], from_obj[20];
593         struct notes_tree *t;
594         const char *rewrite_cmd = NULL;
595         struct option options[] = {
596                 OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
597                 OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
598                 OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
599                            "load rewriting config for <command> (implies "
600                            "--stdin)"),
601                 OPT_END()
602         };
604         argc = parse_options(argc, argv, prefix, options, git_notes_copy_usage,
605                              0);
607         if (from_stdin || rewrite_cmd) {
608                 if (argc) {
609                         error("too many parameters");
610                         usage_with_options(git_notes_copy_usage, options);
611                 } else {
612                         return notes_copy_from_stdin(force, rewrite_cmd);
613                 }
614         }
616         if (argc < 2) {
617                 error("too few parameters");
618                 usage_with_options(git_notes_copy_usage, options);
619         }
620         if (2 < argc) {
621                 error("too many parameters");
622                 usage_with_options(git_notes_copy_usage, options);
623         }
625         if (get_sha1(argv[0], from_obj))
626                 die("Failed to resolve '%s' as a valid ref.", argv[0]);
628         object_ref = 1 < argc ? argv[1] : "HEAD";
630         if (get_sha1(object_ref, object))
631                 die("Failed to resolve '%s' as a valid ref.", object_ref);
633         t = init_notes_check("copy");
634         note = get_note(t, object);
636         if (note) {
637                 if (!force) {
638                         retval = error("Cannot copy notes. Found existing "
639                                        "notes for object %s. Use '-f' to "
640                                        "overwrite existing notes",
641                                        sha1_to_hex(object));
642                         goto out;
643                 }
644                 fprintf(stderr, "Overwriting existing notes for object %s\n",
645                         sha1_to_hex(object));
646         }
648         from_note = get_note(t, from_obj);
649         if (!from_note) {
650                 retval = error("Missing notes on source object %s. Cannot "
651                                "copy.", sha1_to_hex(from_obj));
652                 goto out;
653         }
655         if (add_note(t, object, from_note, combine_notes_overwrite))
656                 die("BUG: combine_notes_overwrite failed");
657         commit_notes(t, "Notes added by 'git notes copy'");
658 out:
659         free_notes(t);
660         return retval;
663 static int append_edit(int argc, const char **argv, const char *prefix)
665         const char *object_ref;
666         struct notes_tree *t;
667         unsigned char object[20], new_note[20];
668         const unsigned char *note;
669         char logmsg[100];
670         const char * const *usage;
671         struct msg_arg msg = { 0, 0, STRBUF_INIT };
672         struct option options[] = {
673                 { OPTION_CALLBACK, 'm', "message", &msg, "MSG",
674                         "note contents as a string", PARSE_OPT_NONEG,
675                         parse_msg_arg},
676                 { OPTION_CALLBACK, 'F', "file", &msg, "FILE",
677                         "note contents in a file", PARSE_OPT_NONEG,
678                         parse_file_arg},
679                 { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT",
680                         "reuse and edit specified note object", PARSE_OPT_NONEG,
681                         parse_reedit_arg},
682                 { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
683                         "reuse specified note object", PARSE_OPT_NONEG,
684                         parse_reuse_arg},
685                 OPT_END()
686         };
687         int edit = !strcmp(argv[0], "edit");
689         usage = edit ? git_notes_edit_usage : git_notes_append_usage;
690         argc = parse_options(argc, argv, prefix, options, usage,
691                              PARSE_OPT_KEEP_ARGV0);
693         if (2 < argc) {
694                 error("too many parameters");
695                 usage_with_options(usage, options);
696         }
698         if (msg.given && edit)
699                 fprintf(stderr, "The -m/-F/-c/-C options have been deprecated "
700                         "for the 'edit' subcommand.\n"
701                         "Please use 'git notes add -f -m/-F/-c/-C' instead.\n");
703         object_ref = 1 < argc ? argv[1] : "HEAD";
705         if (get_sha1(object_ref, object))
706                 die("Failed to resolve '%s' as a valid ref.", object_ref);
708         t = init_notes_check(argv[0]);
709         note = get_note(t, object);
711         create_note(object, &msg, !edit, note, new_note);
713         if (is_null_sha1(new_note))
714                 remove_note(t, object);
715         else if (add_note(t, object, new_note, combine_notes_overwrite))
716                 die("BUG: combine_notes_overwrite failed");
718         snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
719                  is_null_sha1(new_note) ? "removed" : "added", argv[0]);
720         commit_notes(t, logmsg);
721         free_notes(t);
722         strbuf_release(&(msg.buf));
723         return 0;
726 static int show(int argc, const char **argv, const char *prefix)
728         const char *object_ref;
729         struct notes_tree *t;
730         unsigned char object[20];
731         const unsigned char *note;
732         int retval;
733         struct option options[] = {
734                 OPT_END()
735         };
737         argc = parse_options(argc, argv, prefix, options, git_notes_show_usage,
738                              0);
740         if (1 < argc) {
741                 error("too many parameters");
742                 usage_with_options(git_notes_show_usage, options);
743         }
745         object_ref = argc ? argv[0] : "HEAD";
747         if (get_sha1(object_ref, object))
748                 die("Failed to resolve '%s' as a valid ref.", object_ref);
750         t = init_notes_check("show");
751         note = get_note(t, object);
753         if (!note)
754                 retval = error("No note found for object %s.",
755                                sha1_to_hex(object));
756         else {
757                 const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
758                 retval = execv_git_cmd(show_args);
759         }
760         free_notes(t);
761         return retval;
764 static int merge(int argc, const char **argv, const char *prefix)
766         struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
767         unsigned char result_sha1[20];
768         struct notes_merge_options o;
769         int verbosity = 0, result;
770         struct option options[] = {
771                 OPT__VERBOSITY(&verbosity),
772                 OPT_END()
773         };
775         argc = parse_options(argc, argv, prefix, options,
776                              git_notes_merge_usage, 0);
778         if (argc != 1) {
779                 error("Must specify a notes ref to merge");
780                 usage_with_options(git_notes_merge_usage, options);
781         }
783         init_notes_merge_options(&o);
784         o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT;
786         o.local_ref = default_notes_ref();
787         strbuf_addstr(&remote_ref, argv[0]);
788         expand_notes_ref(&remote_ref);
789         o.remote_ref = remote_ref.buf;
791         result = notes_merge(&o, result_sha1);
793         strbuf_addf(&msg, "notes: Merged notes from %s into %s",
794                     remote_ref.buf, default_notes_ref());
795         if (result == 0) { /* Merge resulted (trivially) in result_sha1 */
796                 /* Update default notes ref with new commit */
797                 update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
798                            0, DIE_ON_ERR);
799         } else {
800                 /* TODO: */
801                 die("'git notes merge' cannot yet handle non-trivial merges!");
802         }
804         strbuf_release(&remote_ref);
805         strbuf_release(&msg);
806         return 0;
809 static int remove_cmd(int argc, const char **argv, const char *prefix)
811         struct option options[] = {
812                 OPT_END()
813         };
814         const char *object_ref;
815         struct notes_tree *t;
816         unsigned char object[20];
818         argc = parse_options(argc, argv, prefix, options,
819                              git_notes_remove_usage, 0);
821         if (1 < argc) {
822                 error("too many parameters");
823                 usage_with_options(git_notes_remove_usage, options);
824         }
826         object_ref = argc ? argv[0] : "HEAD";
828         if (get_sha1(object_ref, object))
829                 die("Failed to resolve '%s' as a valid ref.", object_ref);
831         t = init_notes_check("remove");
833         fprintf(stderr, "Removing note for object %s\n", sha1_to_hex(object));
834         remove_note(t, object);
836         commit_notes(t, "Notes removed by 'git notes remove'");
837         free_notes(t);
838         return 0;
841 static int prune(int argc, const char **argv, const char *prefix)
843         struct notes_tree *t;
844         int show_only = 0, verbose = 0;
845         struct option options[] = {
846                 OPT_BOOLEAN('n', "dry-run", &show_only,
847                             "do not remove, show only"),
848                 OPT_BOOLEAN('v', "verbose", &verbose, "report pruned notes"),
849                 OPT_END()
850         };
852         argc = parse_options(argc, argv, prefix, options, git_notes_prune_usage,
853                              0);
855         if (argc) {
856                 error("too many parameters");
857                 usage_with_options(git_notes_prune_usage, options);
858         }
860         t = init_notes_check("prune");
862         prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) |
863                 (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) );
864         if (!show_only)
865                 commit_notes(t, "Notes removed by 'git notes prune'");
866         free_notes(t);
867         return 0;
870 int cmd_notes(int argc, const char **argv, const char *prefix)
872         int result;
873         const char *override_notes_ref = NULL;
874         struct option options[] = {
875                 OPT_STRING(0, "ref", &override_notes_ref, "notes_ref",
876                            "use notes from <notes_ref>"),
877                 OPT_END()
878         };
880         git_config(git_default_config, NULL);
881         argc = parse_options(argc, argv, prefix, options, git_notes_usage,
882                              PARSE_OPT_STOP_AT_NON_OPTION);
884         if (override_notes_ref) {
885                 struct strbuf sb = STRBUF_INIT;
886                 strbuf_addstr(&sb, override_notes_ref);
887                 expand_notes_ref(&sb);
888                 setenv("GIT_NOTES_REF", sb.buf, 1);
889                 strbuf_release(&sb);
890         }
892         if (argc < 1 || !strcmp(argv[0], "list"))
893                 result = list(argc, argv, prefix);
894         else if (!strcmp(argv[0], "add"))
895                 result = add(argc, argv, prefix);
896         else if (!strcmp(argv[0], "copy"))
897                 result = copy(argc, argv, prefix);
898         else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit"))
899                 result = append_edit(argc, argv, prefix);
900         else if (!strcmp(argv[0], "show"))
901                 result = show(argc, argv, prefix);
902         else if (!strcmp(argv[0], "merge"))
903                 result = merge(argc, argv, prefix);
904         else if (!strcmp(argv[0], "remove"))
905                 result = remove_cmd(argc, argv, prefix);
906         else if (!strcmp(argv[0], "prune"))
907                 result = prune(argc, argv, prefix);
908         else {
909                 result = error("Unknown subcommand: %s", argv[0]);
910                 usage_with_options(git_notes_usage, options);
911         }
913         return result ? 1 : 0;