Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'jc/notes-batch-removal'

* jc/notes-batch-removal:
  show: --ignore-missing
  notes remove: --stdin reads from the standard input
  notes remove: --ignore-missing
  notes remove: allow removing more than one
  • Loading branch information...
commit 3d109dd8ef012eac37702a2afa980b545ac08467 2 parents 01f9ffb + cc243c3
Junio C Hamano authored
16 Documentation/git-notes.txt
@@ -17,7 +17,7 @@ SYNOPSIS
17 17 'git notes' merge [-v | -q] [-s <strategy> ] <notes_ref>
18 18 'git notes' merge --commit [-v | -q]
19 19 'git notes' merge --abort [-v | -q]
20   -'git notes' remove [<object>]
  20 +'git notes' remove [--ignore-missing] [--stdin] [<object>...]
21 21 'git notes' prune [-n | -v]
22 22 'git notes' get-ref
23 23
@@ -106,8 +106,9 @@ When done, the user can either finalize the merge with
106 106 'git notes merge --abort'.
107 107
108 108 remove::
109   - Remove the notes for a given object (defaults to HEAD).
110   - This is equivalent to specifying an empty note message to
  109 + Remove the notes for given objects (defaults to HEAD). When
  110 + giving zero or one object from the command line, this is
  111 + equivalent to specifying an empty note message to
111 112 the `edit` subcommand.
112 113
113 114 prune::
@@ -154,6 +155,15 @@ OPTIONS
154 155 'GIT_NOTES_REF' and the "core.notesRef" configuration. The ref
155 156 is taken to be in `refs/notes/` if it is not qualified.
156 157
  158 +--ignore-missing::
  159 + Do not consider it an error to request removing notes from an
  160 + object that does not have notes attached to it.
  161 +
  162 +--stdin::
  163 + Also read the object names to remove notes from from the standard
  164 + input (there is no reason you cannot combine this with object
  165 + names from the command line).
  166 +
157 167 -n::
158 168 --dry-run::
159 169 Do not remove anything; just report the object names whose notes
1  Documentation/git-rev-list.txt
@@ -29,6 +29,7 @@ SYNOPSIS
29 29 [ \--tags[=<pattern>] ]
30 30 [ \--remotes[=<pattern>] ]
31 31 [ \--glob=<glob-pattern> ]
  32 + [ \--ignore-missing ]
32 33 [ \--stdin ]
33 34 [ \--quiet ]
34 35 [ \--topo-order ]
4 Documentation/rev-list-options.txt
@@ -139,6 +139,10 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
139 139 is automatically prepended if missing. If pattern lacks '?', '*',
140 140 or '[', '/*' at the end is implied.
141 141
  142 +--ignore-missing::
  143 +
  144 + Upon seeing an invalid object name in the input, pretend as if
  145 + the bad input was not given.
142 146
143 147 ifndef::git-rev-list[]
144 148 --bisect::
64 builtin/notes.c
@@ -29,7 +29,7 @@ static const char * const git_notes_usage[] = {
29 29 "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
30 30 "git notes merge --commit [-v | -q]",
31 31 "git notes merge --abort [-v | -q]",
32   - "git notes [--ref <notes_ref>] remove [<object>]",
  32 + "git notes [--ref <notes_ref>] remove [<object>...]",
33 33 "git notes [--ref <notes_ref>] prune [-n | -v]",
34 34 "git notes [--ref <notes_ref>] get-ref",
35 35 NULL
@@ -953,40 +953,60 @@ static int merge(int argc, const char **argv, const char *prefix)
953 953 return result < 0; /* return non-zero on conflicts */
954 954 }
955 955
  956 +#define IGNORE_MISSING 1
  957 +
  958 +static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
  959 +{
  960 + int status;
  961 + unsigned char sha1[20];
  962 + if (get_sha1(name, sha1))
  963 + return error(_("Failed to resolve '%s' as a valid ref."), name);
  964 + status = remove_note(t, sha1);
  965 + if (status)
  966 + fprintf(stderr, _("Object %s has no note\n"), name);
  967 + else
  968 + fprintf(stderr, _("Removing note for object %s\n"), name);
  969 + return (flag & IGNORE_MISSING) ? 0 : status;
  970 +}
  971 +
956 972 static int remove_cmd(int argc, const char **argv, const char *prefix)
957 973 {
  974 + unsigned flag = 0;
  975 + int from_stdin = 0;
958 976 struct option options[] = {
  977 + OPT_BIT(0, "ignore-missing", &flag,
  978 + "attempt to remove non-existent note is not an error",
  979 + IGNORE_MISSING),
  980 + OPT_BOOLEAN(0, "stdin", &from_stdin,
  981 + "read object names from the standard input"),
959 982 OPT_END()
960 983 };
961   - const char *object_ref;
962 984 struct notes_tree *t;
963   - unsigned char object[20];
964   - int retval;
  985 + int retval = 0;
965 986
966 987 argc = parse_options(argc, argv, prefix, options,
967 988 git_notes_remove_usage, 0);
968 989
969   - if (1 < argc) {
970   - error(_("too many parameters"));
971   - usage_with_options(git_notes_remove_usage, options);
972   - }
973   -
974   - object_ref = argc ? argv[0] : "HEAD";
975   -
976   - if (get_sha1(object_ref, object))
977   - die(_("Failed to resolve '%s' as a valid ref."), object_ref);
978   -
979 990 t = init_notes_check("remove");
980 991
981   - retval = remove_note(t, object);
982   - if (retval)
983   - fprintf(stderr, _("Object %s has no note\n"), sha1_to_hex(object));
984   - else {
985   - fprintf(stderr, _("Removing note for object %s\n"),
986   - sha1_to_hex(object));
987   -
988   - commit_notes(t, "Notes removed by 'git notes remove'");
  992 + if (!argc && !from_stdin) {
  993 + retval = remove_one_note(t, "HEAD", flag);
  994 + } else {
  995 + while (*argv) {
  996 + retval |= remove_one_note(t, *argv, flag);
  997 + argv++;
  998 + }
989 999 }
  1000 + if (from_stdin) {
  1001 + struct strbuf sb = STRBUF_INIT;
  1002 + while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
  1003 + strbuf_rtrim(&sb);
  1004 + retval |= remove_one_note(t, sb.buf, flag);
  1005 + }
  1006 + strbuf_release(&sb);
  1007 + }
  1008 + if (!retval)
  1009 + commit_notes(t, "Notes removed by 'git notes remove'");
990 1010 free_notes(t);
991 1011 return retval;
992 1012 }
1  builtin/rev-parse.c
@@ -44,6 +44,7 @@ static int is_rev_argument(const char *arg)
44 44 "--branches=",
45 45 "--branches",
46 46 "--header",
  47 + "--ignore-missing",
47 48 "--max-age=",
48 49 "--max-count=",
49 50 "--min-age=",
15 revision.c
@@ -133,6 +133,8 @@ void mark_parents_uninteresting(struct commit *commit)
133 133
134 134 static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
135 135 {
  136 + if (!obj)
  137 + return;
136 138 if (revs->no_walk && (obj->flags & UNINTERESTING))
137 139 revs->no_walk = 0;
138 140 if (revs->reflog_info && obj->type == OBJ_COMMIT) {
@@ -174,8 +176,11 @@ static struct object *get_reference(struct rev_info *revs, const char *name, con
174 176 struct object *object;
175 177
176 178 object = parse_object(sha1);
177   - if (!object)
  179 + if (!object) {
  180 + if (revs->ignore_missing)
  181 + return object;
178 182 die("bad object %s", name);
  183 + }
179 184 object->flags |= flags;
180 185 return object;
181 186 }
@@ -906,6 +911,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
906 911 return 0;
907 912 while (1) {
908 913 it = get_reference(revs, arg, sha1, 0);
  914 + if (!it && revs->ignore_missing)
  915 + return 0;
909 916 if (it->type != OBJ_TAG)
910 917 break;
911 918 if (!((struct tag*)it)->tagged)
@@ -1044,6 +1051,8 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
1044 1051 a = lookup_commit_reference(from_sha1);
1045 1052 b = lookup_commit_reference(sha1);
1046 1053 if (!a || !b) {
  1054 + if (revs->ignore_missing)
  1055 + return 0;
1047 1056 die(symmetric ?
1048 1057 "Invalid symmetric difference expression %s...%s" :
1049 1058 "Invalid revision range %s..%s",
@@ -1090,7 +1099,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
1090 1099 arg++;
1091 1100 }
1092 1101 if (get_sha1_with_mode(arg, sha1, &mode))
1093   - return -1;
  1102 + return revs->ignore_missing ? 0 : -1;
1094 1103 if (!cant_be_filename)
1095 1104 verify_non_filename(revs->prefix, arg);
1096 1105 object = get_reference(revs, arg, sha1, flags ^ local_flags);
@@ -1477,6 +1486,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
1477 1486 } else if (!strcmp(arg, "--children")) {
1478 1487 revs->children.name = "children";
1479 1488 revs->limited = 1;
  1489 + } else if (!strcmp(arg, "--ignore-missing")) {
  1490 + revs->ignore_missing = 1;
1480 1491 } else {
1481 1492 int opts = diff_opt_parse(&revs->diffopt, argv, argc);
1482 1493 if (!opts)
3  revision.h
@@ -36,7 +36,8 @@ struct rev_info {
36 36 const char *prefix;
37 37 const char *def;
38 38 struct pathspec prune_data;
39   - unsigned int early_output;
  39 + unsigned int early_output:1,
  40 + ignore_missing:1;
40 41
41 42 /* Traversal flags */
42 43 unsigned int dense:1,
75 t/t3301-notes.sh
@@ -435,6 +435,81 @@ test_expect_success 'removing non-existing note should not create new commit' '
435 435 test_cmp before_commit after_commit
436 436 '
437 437
  438 +test_expect_success 'removing more than one' '
  439 + before=$(git rev-parse --verify refs/notes/commits) &&
  440 + test_when_finished "git update-ref refs/notes/commits $before" &&
  441 +
  442 + # We have only two -- add another and make sure it stays
  443 + git notes add -m "extra" &&
  444 + git notes list HEAD >after-removal-expect &&
  445 + git notes remove HEAD^^ HEAD^^^ &&
  446 + git notes list | sed -e "s/ .*//" >actual &&
  447 + test_cmp after-removal-expect actual
  448 +'
  449 +
  450 +test_expect_success 'removing is atomic' '
  451 + before=$(git rev-parse --verify refs/notes/commits) &&
  452 + test_when_finished "git update-ref refs/notes/commits $before" &&
  453 + test_must_fail git notes remove HEAD^^ HEAD^^^ HEAD^ &&
  454 + after=$(git rev-parse --verify refs/notes/commits) &&
  455 + test "$before" = "$after"
  456 +'
  457 +
  458 +test_expect_success 'removing with --ignore-missing' '
  459 + before=$(git rev-parse --verify refs/notes/commits) &&
  460 + test_when_finished "git update-ref refs/notes/commits $before" &&
  461 +
  462 + # We have only two -- add another and make sure it stays
  463 + git notes add -m "extra" &&
  464 + git notes list HEAD >after-removal-expect &&
  465 + git notes remove --ignore-missing HEAD^^ HEAD^^^ HEAD^ &&
  466 + git notes list | sed -e "s/ .*//" >actual &&
  467 + test_cmp after-removal-expect actual
  468 +'
  469 +
  470 +test_expect_success 'removing with --ignore-missing but bogus ref' '
  471 + before=$(git rev-parse --verify refs/notes/commits) &&
  472 + test_when_finished "git update-ref refs/notes/commits $before" &&
  473 + test_must_fail git notes remove --ignore-missing HEAD^^ HEAD^^^ NO-SUCH-COMMIT &&
  474 + after=$(git rev-parse --verify refs/notes/commits) &&
  475 + test "$before" = "$after"
  476 +'
  477 +
  478 +test_expect_success 'remove reads from --stdin' '
  479 + before=$(git rev-parse --verify refs/notes/commits) &&
  480 + test_when_finished "git update-ref refs/notes/commits $before" &&
  481 +
  482 + # We have only two -- add another and make sure it stays
  483 + git notes add -m "extra" &&
  484 + git notes list HEAD >after-removal-expect &&
  485 + git rev-parse HEAD^^ HEAD^^^ >input &&
  486 + git notes remove --stdin <input &&
  487 + git notes list | sed -e "s/ .*//" >actual &&
  488 + test_cmp after-removal-expect actual
  489 +'
  490 +
  491 +test_expect_success 'remove --stdin is also atomic' '
  492 + before=$(git rev-parse --verify refs/notes/commits) &&
  493 + test_when_finished "git update-ref refs/notes/commits $before" &&
  494 + git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
  495 + test_must_fail git notes remove --stdin <input &&
  496 + after=$(git rev-parse --verify refs/notes/commits) &&
  497 + test "$before" = "$after"
  498 +'
  499 +
  500 +test_expect_success 'removing with --stdin --ignore-missing' '
  501 + before=$(git rev-parse --verify refs/notes/commits) &&
  502 + test_when_finished "git update-ref refs/notes/commits $before" &&
  503 +
  504 + # We have only two -- add another and make sure it stays
  505 + git notes add -m "extra" &&
  506 + git notes list HEAD >after-removal-expect &&
  507 + git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
  508 + git notes remove --ignore-missing --stdin <input &&
  509 + git notes list | sed -e "s/ .*//" >actual &&
  510 + test_cmp after-removal-expect actual
  511 +'
  512 +
438 513 test_expect_success 'list notes with "git notes list"' '
439 514 git notes list > output &&
440 515 test_cmp expect output

0 comments on commit 3d109dd

Please sign in to comment.
Something went wrong with that request. Please try again.