19
#include "string-list.h"
20
21
static const char * const git_notes_usage[] = {
22
+ "git notes [--ref <notes_ref>] [list [<object>]]",
23
+ "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
24
+ "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>",
25
+ "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
26
+ "git notes [--ref <notes_ref>] edit [<object>]",
27
+ "git notes [--ref <notes_ref>] show [<object>]",
28
+ "git notes [--ref <notes_ref>] remove [<object>]",
29
+ "git notes [--ref <notes_ref>] prune",
30
+ NULL
31
+};
32
+
33
+static const char * const git_notes_list_usage[] = {
34
"git notes [list [<object>]]",
23
- "git notes add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
24
- "git notes copy [-f] <from-object> <to-object>",
25
- "git notes append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
35
+ NULL
36
+};
37
+
38
+static const char * const git_notes_add_usage[] = {
39
+ "git notes add [<options>] [<object>]",
40
+ NULL
41
+};
42
+
43
+static const char * const git_notes_copy_usage[] = {
44
+ "git notes copy [<options>] <from-object> <to-object>",
45
+ "git notes copy --stdin [<from-object> <to-object>]...",
46
+ NULL
47
+};
48
+
49
+static const char * const git_notes_append_usage[] = {
50
+ "git notes append [<options>] [<object>]",
51
+ NULL
52
+};
53
+
54
+static const char * const git_notes_edit_usage[] = {
55
"git notes edit [<object>]",
56
+ NULL
57
+};
58
+
59
+static const char * const git_notes_show_usage[] = {
60
"git notes show [<object>]",
61
+ NULL
62
+};
63
+
64
+static const char * const git_notes_remove_usage[] = {
65
"git notes remove [<object>]",
66
+ NULL
67
+};
68
+
69
+static const char * const git_notes_prune_usage[] = {
70
"git notes prune",
71
NULL
72
};
322
return 0;
323
}
324
284
-
325
combine_notes_fn *parse_combine_notes_fn(const char *v)
326
{
327
if (!strcasecmp(v, "overwrite"))
476
return ret;
477
}
478
439
-int cmd_notes(int argc, const char **argv, const char *prefix)
479
+static struct notes_tree *init_notes_check(const char *subcommand)
480
{
481
struct notes_tree *t;
442
- unsigned char object[20], from_obj[20], new_note[20];
443
- const unsigned char *note;
444
- const char *object_ref;
445
- char logmsg[100];
482
+ init_notes(NULL, NULL, NULL, 0);
483
+ t = &default_notes_tree;
484
447
- int list = 0, add = 0, copy = 0, append = 0, edit = 0, show = 0,
448
- remove = 0, prune = 0, force = 0, from_stdin = 0;
449
- int given_object = 0, i = 1, retval = 0;
450
- struct msg_arg msg = { 0, 0, STRBUF_INIT };
451
- const char *rewrite_cmd = NULL;
452
- const char *override_notes_ref = NULL;
485
+ if (prefixcmp(t->ref, "refs/notes/"))
486
+ die("Refusing to %s notes in %s (outside of refs/notes/)",
487
+ subcommand, t->ref);
488
+ return t;
489
+}
490
+
491
+static int list(int argc, const char **argv, const char *prefix)
492
+{
493
+ struct notes_tree *t;
494
+ unsigned char object[20];
495
+ const unsigned char *note;
496
+ int retval = -1;
497
struct option options[] = {
454
- OPT_GROUP("Notes options"),
455
- OPT_CALLBACK('m', "message", &msg, "MSG",
456
- "note contents as a string", parse_msg_arg),
457
- OPT_CALLBACK('F', "file", &msg, "FILE",
458
- "note contents in a file", parse_file_arg),
459
- OPT_CALLBACK('c', "reedit-message", &msg, "OBJECT",
460
- "reuse and edit specified note object", parse_reedit_arg),
461
- OPT_CALLBACK('C', "reuse-message", &msg, "OBJECT",
462
- "reuse specified note object", parse_reuse_arg),
463
- OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
464
- OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
465
- OPT_STRING(0, "ref", &override_notes_ref, "notes_ref",
466
- "use notes from <notes_ref>"),
467
- OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
468
- "load rewriting config for <command> (implies --stdin)"),
498
OPT_END()
499
};
500
472
- git_config(git_default_config, NULL);
473
-
474
- argc = parse_options(argc, argv, prefix, options, git_notes_usage, 0);
501
+ if (argc)
502
+ argc = parse_options(argc, argv, prefix, options,
503
+ git_notes_list_usage, 0);
504
476
- if (override_notes_ref) {
477
- struct strbuf sb = STRBUF_INIT;
478
- if (!prefixcmp(override_notes_ref, "refs/notes/"))
479
- /* we're happy */;
480
- else if (!prefixcmp(override_notes_ref, "notes/"))
481
- strbuf_addstr(&sb, "refs/");
482
- else
483
- strbuf_addstr(&sb, "refs/notes/");
484
- strbuf_addstr(&sb, override_notes_ref);
485
- setenv("GIT_NOTES_REF", sb.buf, 1);
486
- strbuf_release(&sb);
505
+ if (1 < argc) {
506
+ error("too many parameters");
507
+ usage_with_options(git_notes_list_usage, options);
508
}
509
489
- if (argc && !strcmp(argv[0], "list"))
490
- list = 1;
491
- else if (argc && !strcmp(argv[0], "add"))
492
- add = 1;
493
- else if (argc && !strcmp(argv[0], "copy"))
494
- copy = 1;
495
- else if (argc && !strcmp(argv[0], "append"))
496
- append = 1;
497
- else if (argc && !strcmp(argv[0], "edit"))
498
- edit = 1;
499
- else if (argc && !strcmp(argv[0], "show"))
500
- show = 1;
501
- else if (argc && !strcmp(argv[0], "remove"))
502
- remove = 1;
503
- else if (argc && !strcmp(argv[0], "prune"))
504
- prune = 1;
505
- else if (!argc) {
506
- list = 1; /* Default to 'list' if no other subcommand given */
507
- i = 0;
508
- }
510
+ t = init_notes_check("list");
511
+ if (argc) {
512
+ if (get_sha1(argv[0], object))
513
+ die("Failed to resolve '%s' as a valid ref.", argv[0]);
514
+ note = get_note(t, object);
515
+ if (note) {
516
+ puts(sha1_to_hex(note));
517
+ retval = 0;
518
+ } else
519
+ retval = error("No note found for object %s.",
520
+ sha1_to_hex(object));
521
+ } else
522
+ retval = for_each_note(t, 0, list_each_note, NULL);
523
510
- if (list + add + copy + append + edit + show + remove + prune != 1)
511
- usage_with_options(git_notes_usage, options);
524
+ free_notes(t);
525
+ return retval;
526
+}
527
513
- if (msg.given && !(add || append || edit)) {
514
- error("cannot use -m/-F/-c/-C options with %s subcommand.",
515
- argv[0]);
516
- usage_with_options(git_notes_usage, options);
517
- }
528
+static int add(int argc, const char **argv, const char *prefix)
529
+{
530
+ int retval = 0, force = 0;
531
+ const char *object_ref;
532
+ struct notes_tree *t;
533
+ unsigned char object[20], new_note[20];
534
+ char logmsg[100];
535
+ const unsigned char *note;
536
+ struct msg_arg msg = { 0, 0, STRBUF_INIT };
537
+ struct option options[] = {
538
+ { OPTION_CALLBACK, 'm', "message", &msg, "MSG",
539
+ "note contents as a string", PARSE_OPT_NONEG,
540
+ parse_msg_arg},
541
+ { OPTION_CALLBACK, 'F', "file", &msg, "FILE",
542
+ "note contents in a file", PARSE_OPT_NONEG,
543
+ parse_file_arg},
544
+ { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT",
545
+ "reuse and edit specified note object", PARSE_OPT_NONEG,
546
+ parse_reedit_arg},
547
+ { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
548
+ "reuse specified note object", PARSE_OPT_NONEG,
549
+ parse_reuse_arg},
550
+ OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
551
+ OPT_END()
552
+ };
553
519
- if (msg.given && edit) {
520
- fprintf(stderr, "The -m/-F/-c/-C options have been deprecated "
521
- "for the 'edit' subcommand.\n"
522
- "Please use 'git notes add -f -m/-F/-c/-C' instead.\n");
523
- }
554
+ argc = parse_options(argc, argv, prefix, options, git_notes_add_usage,
555
+ 0);
556
525
- if (force && !(add || copy)) {
526
- error("cannot use -f option with %s subcommand.", argv[0]);
527
- usage_with_options(git_notes_usage, options);
557
+ if (1 < argc) {
558
+ error("too many parameters");
559
+ usage_with_options(git_notes_add_usage, options);
560
}
561
530
- if (!copy && rewrite_cmd) {
531
- error("cannot use --for-rewrite with %s subcommand.", argv[0]);
532
- usage_with_options(git_notes_usage, options);
533
- }
534
- if (!copy && from_stdin) {
535
- error("cannot use --stdin with %s subcommand.", argv[0]);
536
- usage_with_options(git_notes_usage, options);
537
- }
562
+ object_ref = argc ? argv[0] : "HEAD";
563
539
- if (copy) {
540
- const char *from_ref;
541
- if (from_stdin || rewrite_cmd) {
542
- if (argc > 1) {
543
- error("too many parameters");
544
- usage_with_options(git_notes_usage, options);
545
- } else {
546
- return notes_copy_from_stdin(force, rewrite_cmd);
547
- }
548
- }
549
- if (argc < 3) {
550
- error("too few parameters");
551
- usage_with_options(git_notes_usage, options);
552
- }
553
- from_ref = argv[i++];
554
- if (get_sha1(from_ref, from_obj))
555
- die("Failed to resolve '%s' as a valid ref.", from_ref);
556
- }
564
+ if (get_sha1(object_ref, object))
565
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
566
558
- given_object = argc > i;
559
- object_ref = given_object ? argv[i++] : "HEAD";
567
+ t = init_notes_check("add");
568
+ note = get_note(t, object);
569
561
- if (argc > i || (prune && given_object)) {
562
- error("too many parameters");
563
- usage_with_options(git_notes_usage, options);
570
+ if (note) {
571
+ if (!force) {
572
+ retval = error("Cannot add notes. Found existing notes "
573
+ "for object %s. Use '-f' to overwrite "
574
+ "existing notes", sha1_to_hex(object));
575
+ goto out;
576
+ }
577
+ fprintf(stderr, "Overwriting existing notes for object %s\n",
578
+ sha1_to_hex(object));
579
}
580
566
- if (get_sha1(object_ref, object))
567
- die("Failed to resolve '%s' as a valid ref.", object_ref);
581
+ create_note(object, &msg, 0, note, new_note);
582
569
- init_notes(NULL, NULL, NULL, 0);
570
- t = &default_notes_tree;
583
+ if (is_null_sha1(new_note))
584
+ remove_note(t, object);
585
+ else
586
+ add_note(t, object, new_note, combine_notes_overwrite);
587
572
- if (prefixcmp(t->ref, "refs/notes/"))
573
- die("Refusing to %s notes in %s (outside of refs/notes/)",
574
- argv[0], t->ref);
588
+ snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
589
+ is_null_sha1(new_note) ? "removed" : "added", "add");
590
+ commit_notes(t, logmsg);
591
+out:
592
+ free_notes(t);
593
+ strbuf_release(&(msg.buf));
594
+ return retval;
595
+}
596
576
- note = get_note(t, object);
597
+static int copy(int argc, const char **argv, const char *prefix)
598
+{
599
+ int retval = 0, force = 0, from_stdin = 0;
600
+ const unsigned char *from_note, *note;
601
+ const char *object_ref;
602
+ unsigned char object[20], from_obj[20];
603
+ struct notes_tree *t;
604
+ const char *rewrite_cmd = NULL;
605
+ struct option options[] = {
606
+ OPT_BOOLEAN('f', "force", &force, "replace existing notes"),
607
+ OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
608
+ OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
609
+ "load rewriting config for <command> (implies "
610
+ "--stdin)"),
611
+ OPT_END()
612
+ };
613
578
- /* list command */
614
+ argc = parse_options(argc, argv, prefix, options, git_notes_copy_usage,
615
+ 0);
616
580
- if (list) {
581
- if (given_object) {
582
- if (note) {
583
- puts(sha1_to_hex(note));
584
- goto end;
585
- }
617
+ if (from_stdin || rewrite_cmd) {
618
+ if (argc) {
619
+ error("too many parameters");
620
+ usage_with_options(git_notes_copy_usage, options);
621
} else {
587
- retval = for_each_note(t, 0, list_each_note, NULL);
588
- goto end;
622
+ return notes_copy_from_stdin(force, rewrite_cmd);
623
}
624
}
625
592
- /* show command */
593
-
594
- if ((list || show) && !note) {
595
- error("No note found for object %s.", sha1_to_hex(object));
596
- retval = 1;
597
- goto end;
598
- } else if (show) {
599
- const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
600
- retval = execv_git_cmd(show_args);
601
- goto end;
626
+ if (2 < argc) {
627
+ error("too many parameters");
628
+ usage_with_options(git_notes_copy_usage, options);
629
}
630
604
- /* add/append/edit/remove/prune command */
631
+ if (get_sha1(argv[0], from_obj))
632
+ die("Failed to resolve '%s' as a valid ref.", argv[0]);
633
+
634
+ object_ref = 1 < argc ? argv[1] : "HEAD";
635
+
636
+ if (get_sha1(object_ref, object))
637
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
638
606
- if ((add || copy) && note) {
639
+ t = init_notes_check("copy");
640
+ note = get_note(t, object);
641
+
642
+ if (note) {
643
if (!force) {
608
- error("Cannot %s notes. Found existing notes for object"
609
- " %s. Use '-f' to overwrite existing notes",
610
- argv[0], sha1_to_hex(object));
611
- retval = 1;
612
- goto end;
644
+ retval = error("Cannot copy notes. Found existing "
645
+ "notes for object %s. Use '-f' to "
646
+ "overwrite existing notes",
647
+ sha1_to_hex(object));
648
+ goto out;
649
}
650
fprintf(stderr, "Overwriting existing notes for object %s\n",
651
sha1_to_hex(object));
652
}
653
618
- if (remove) {
619
- msg.given = 1;
620
- msg.use_editor = 0;
621
- strbuf_reset(&(msg.buf));
654
+ from_note = get_note(t, from_obj);
655
+ if (!from_note) {
656
+ retval = error("Missing notes on source object %s. Cannot "
657
+ "copy.", sha1_to_hex(from_obj));
658
+ goto out;
659
}
660
624
- if (prune) {
625
- hashclr(new_note);
626
- prune_notes(t);
627
- goto commit;
628
- } else if (copy) {
629
- const unsigned char *from_note = get_note(t, from_obj);
630
- if (!from_note) {
631
- error("Missing notes on source object %s. Cannot copy.",
632
- sha1_to_hex(from_obj));
633
- retval = 1;
634
- goto end;
635
- }
636
- hashcpy(new_note, from_note);
637
- } else
638
- create_note(object, &msg, append, note, new_note);
661
+ add_note(t, object, from_note, combine_notes_overwrite);
662
+ commit_notes(t, "Notes added by 'git notes copy'");
663
+out:
664
+ free_notes(t);
665
+ return retval;
666
+}
667
+
668
+static int append_edit(int argc, const char **argv, const char *prefix)
669
+{
670
+ const char *object_ref;
671
+ struct notes_tree *t;
672
+ unsigned char object[20], new_note[20];
673
+ const unsigned char *note;
674
+ char logmsg[100];
675
+ const char * const *usage;
676
+ struct msg_arg msg = { 0, 0, STRBUF_INIT };
677
+ struct option options[] = {
678
+ { OPTION_CALLBACK, 'm', "message", &msg, "MSG",
679
+ "note contents as a string", PARSE_OPT_NONEG,
680
+ parse_msg_arg},
681
+ { OPTION_CALLBACK, 'F', "file", &msg, "FILE",
682
+ "note contents in a file", PARSE_OPT_NONEG,
683
+ parse_file_arg},
684
+ { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT",
685
+ "reuse and edit specified note object", PARSE_OPT_NONEG,
686
+ parse_reedit_arg},
687
+ { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
688
+ "reuse specified note object", PARSE_OPT_NONEG,
689
+ parse_reuse_arg},
690
+ OPT_END()
691
+ };
692
+ int edit = !strcmp(argv[0], "edit");
693
+
694
+ usage = edit ? git_notes_edit_usage : git_notes_append_usage;
695
+ argc = parse_options(argc, argv, prefix, options, usage,
696
+ PARSE_OPT_KEEP_ARGV0);
697
+
698
+ if (2 < argc) {
699
+ error("too many parameters");
700
+ usage_with_options(usage, options);
701
+ }
702
+
703
+ if (msg.given && edit)
704
+ fprintf(stderr, "The -m/-F/-c/-C options have been deprecated "
705
+ "for the 'edit' subcommand.\n"
706
+ "Please use 'git notes add -f -m/-F/-c/-C' instead.\n");
707
+
708
+ object_ref = 1 < argc ? argv[1] : "HEAD";
709
+
710
+ if (get_sha1(object_ref, object))
711
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
712
+
713
+ t = init_notes_check(argv[0]);
714
+ note = get_note(t, object);
715
+
716
+ create_note(object, &msg, !edit, note, new_note);
717
718
if (is_null_sha1(new_note))
719
remove_note(t, object);
720
else
721
add_note(t, object, new_note, combine_notes_overwrite);
722
645
-commit:
723
snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
724
is_null_sha1(new_note) ? "removed" : "added", argv[0]);
725
commit_notes(t, logmsg);
649
-
650
-end:
726
free_notes(t);
727
strbuf_release(&(msg.buf));
728
+ return 0;
729
+}
730
+
731
+static int show(int argc, const char **argv, const char *prefix)
732
+{
733
+ const char *object_ref;
734
+ struct notes_tree *t;
735
+ unsigned char object[20];
736
+ const unsigned char *note;
737
+ int retval;
738
+ struct option options[] = {
739
+ OPT_END()
740
+ };
741
+
742
+ argc = parse_options(argc, argv, prefix, options, git_notes_show_usage,
743
+ 0);
744
+
745
+ if (1 < argc) {
746
+ error("too many parameters");
747
+ usage_with_options(git_notes_show_usage, options);
748
+ }
749
+
750
+ object_ref = argc ? argv[0] : "HEAD";
751
+
752
+ if (get_sha1(object_ref, object))
753
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
754
+
755
+ t = init_notes_check("show");
756
+ note = get_note(t, object);
757
+
758
+ if (!note)
759
+ retval = error("No note found for object %s.",
760
+ sha1_to_hex(object));
761
+ else {
762
+ const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
763
+ retval = execv_git_cmd(show_args);
764
+ }
765
+ free_notes(t);
766
return retval;
767
}
768
+
769
+static int remove_cmd(int argc, const char **argv, const char *prefix)
770
+{
771
+ struct option options[] = {
772
+ OPT_END()
773
+ };
774
+ const char *object_ref;
775
+ struct notes_tree *t;
776
+ unsigned char object[20];
777
+
778
+ argc = parse_options(argc, argv, prefix, options,
779
+ git_notes_remove_usage, 0);
780
+
781
+ if (1 < argc) {
782
+ error("too many parameters");
783
+ usage_with_options(git_notes_remove_usage, options);
784
+ }
785
+
786
+ object_ref = argc ? argv[0] : "HEAD";
787
+
788
+ if (get_sha1(object_ref, object))
789
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
790
+
791
+ t = init_notes_check("remove");
792
+
793
+ fprintf(stderr, "Removing note for object %s\n", sha1_to_hex(object));
794
+ remove_note(t, object);
795
+
796
+ commit_notes(t, "Notes removed by 'git notes remove'");
797
+ free_notes(t);
798
+ return 0;
799
+}
800
+
801
+static int prune(int argc, const char **argv, const char *prefix)
802
+{
803
+ struct notes_tree *t;
804
+ struct option options[] = {
805
+ OPT_END()
806
+ };
807
+
808
+ argc = parse_options(argc, argv, prefix, options, git_notes_prune_usage,
809
+ 0);
810
+
811
+ if (argc) {
812
+ error("too many parameters");
813
+ usage_with_options(git_notes_prune_usage, options);
814
+ }
815
+
816
+ t = init_notes_check("prune");
817
+
818
+ prune_notes(t);
819
+ commit_notes(t, "Notes removed by 'git notes prune'");
820
+ free_notes(t);
821
+ return 0;
822
+}
823
+
824
+int cmd_notes(int argc, const char **argv, const char *prefix)
825
+{
826
+ int result;
827
+ const char *override_notes_ref = NULL;
828
+ struct option options[] = {
829
+ OPT_STRING(0, "ref", &override_notes_ref, "notes_ref",
830
+ "use notes from <notes_ref>"),
831
+ OPT_END()
832
+ };
833
+
834
+ git_config(git_default_config, NULL);
835
+ argc = parse_options(argc, argv, prefix, options, git_notes_usage,
836
+ PARSE_OPT_STOP_AT_NON_OPTION);
837
+
838
+ if (override_notes_ref) {
839
+ struct strbuf sb = STRBUF_INIT;
840
+ if (!prefixcmp(override_notes_ref, "refs/notes/"))
841
+ /* we're happy */;
842
+ else if (!prefixcmp(override_notes_ref, "notes/"))
843
+ strbuf_addstr(&sb, "refs/");
844
+ else
845
+ strbuf_addstr(&sb, "refs/notes/");
846
+ strbuf_addstr(&sb, override_notes_ref);
847
+ setenv("GIT_NOTES_REF", sb.buf, 1);
848
+ strbuf_release(&sb);
849
+ }
850
+
851
+ if (argc < 1 || !strcmp(argv[0], "list"))
852
+ result = list(argc, argv, prefix);
853
+ else if (!strcmp(argv[0], "add"))
854
+ result = add(argc, argv, prefix);
855
+ else if (!strcmp(argv[0], "copy"))
856
+ result = copy(argc, argv, prefix);
857
+ else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit"))
858
+ result = append_edit(argc, argv, prefix);
859
+ else if (!strcmp(argv[0], "show"))
860
+ result = show(argc, argv, prefix);
861
+ else if (!strcmp(argv[0], "remove"))
862
+ result = remove_cmd(argc, argv, prefix);
863
+ else if (!strcmp(argv[0], "prune"))
864
+ result = prune(argc, argv, prefix);
865
+ else {
866
+ result = error("Unknown subcommand: %s", argv[0]);
867
+ usage_with_options(git_notes_usage, options);
868
+ }
869
+
870
+ return result ? 1 : 0;
871
+}