mirror of
https://github.com/git/git.git
synced 2024-06-02 10:36:15 +02:00
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
This commit is contained in:
commit
3d109dd8ef
|
@ -17,7 +17,7 @@ SYNOPSIS
|
||||||
'git notes' merge [-v | -q] [-s <strategy> ] <notes_ref>
|
'git notes' merge [-v | -q] [-s <strategy> ] <notes_ref>
|
||||||
'git notes' merge --commit [-v | -q]
|
'git notes' merge --commit [-v | -q]
|
||||||
'git notes' merge --abort [-v | -q]
|
'git notes' merge --abort [-v | -q]
|
||||||
'git notes' remove [<object>]
|
'git notes' remove [--ignore-missing] [--stdin] [<object>...]
|
||||||
'git notes' prune [-n | -v]
|
'git notes' prune [-n | -v]
|
||||||
'git notes' get-ref
|
'git notes' get-ref
|
||||||
|
|
||||||
|
@ -106,8 +106,9 @@ When done, the user can either finalize the merge with
|
||||||
'git notes merge --abort'.
|
'git notes merge --abort'.
|
||||||
|
|
||||||
remove::
|
remove::
|
||||||
Remove the notes for a given object (defaults to HEAD).
|
Remove the notes for given objects (defaults to HEAD). When
|
||||||
This is equivalent to specifying an empty note message to
|
giving zero or one object from the command line, this is
|
||||||
|
equivalent to specifying an empty note message to
|
||||||
the `edit` subcommand.
|
the `edit` subcommand.
|
||||||
|
|
||||||
prune::
|
prune::
|
||||||
|
@ -154,6 +155,15 @@ OPTIONS
|
||||||
'GIT_NOTES_REF' and the "core.notesRef" configuration. The ref
|
'GIT_NOTES_REF' and the "core.notesRef" configuration. The ref
|
||||||
is taken to be in `refs/notes/` if it is not qualified.
|
is taken to be in `refs/notes/` if it is not qualified.
|
||||||
|
|
||||||
|
--ignore-missing::
|
||||||
|
Do not consider it an error to request removing notes from an
|
||||||
|
object that does not have notes attached to it.
|
||||||
|
|
||||||
|
--stdin::
|
||||||
|
Also read the object names to remove notes from from the standard
|
||||||
|
input (there is no reason you cannot combine this with object
|
||||||
|
names from the command line).
|
||||||
|
|
||||||
-n::
|
-n::
|
||||||
--dry-run::
|
--dry-run::
|
||||||
Do not remove anything; just report the object names whose notes
|
Do not remove anything; just report the object names whose notes
|
||||||
|
|
|
@ -29,6 +29,7 @@ SYNOPSIS
|
||||||
[ \--tags[=<pattern>] ]
|
[ \--tags[=<pattern>] ]
|
||||||
[ \--remotes[=<pattern>] ]
|
[ \--remotes[=<pattern>] ]
|
||||||
[ \--glob=<glob-pattern> ]
|
[ \--glob=<glob-pattern> ]
|
||||||
|
[ \--ignore-missing ]
|
||||||
[ \--stdin ]
|
[ \--stdin ]
|
||||||
[ \--quiet ]
|
[ \--quiet ]
|
||||||
[ \--topo-order ]
|
[ \--topo-order ]
|
||||||
|
|
|
@ -139,6 +139,10 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
|
||||||
is automatically prepended if missing. If pattern lacks '?', '*',
|
is automatically prepended if missing. If pattern lacks '?', '*',
|
||||||
or '[', '/*' at the end is implied.
|
or '[', '/*' at the end is implied.
|
||||||
|
|
||||||
|
--ignore-missing::
|
||||||
|
|
||||||
|
Upon seeing an invalid object name in the input, pretend as if
|
||||||
|
the bad input was not given.
|
||||||
|
|
||||||
ifndef::git-rev-list[]
|
ifndef::git-rev-list[]
|
||||||
--bisect::
|
--bisect::
|
||||||
|
|
|
@ -29,7 +29,7 @@ static const char * const git_notes_usage[] = {
|
||||||
"git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
|
"git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
|
||||||
"git notes merge --commit [-v | -q]",
|
"git notes merge --commit [-v | -q]",
|
||||||
"git notes merge --abort [-v | -q]",
|
"git notes merge --abort [-v | -q]",
|
||||||
"git notes [--ref <notes_ref>] remove [<object>]",
|
"git notes [--ref <notes_ref>] remove [<object>...]",
|
||||||
"git notes [--ref <notes_ref>] prune [-n | -v]",
|
"git notes [--ref <notes_ref>] prune [-n | -v]",
|
||||||
"git notes [--ref <notes_ref>] get-ref",
|
"git notes [--ref <notes_ref>] get-ref",
|
||||||
NULL
|
NULL
|
||||||
|
@ -953,40 +953,60 @@ static int merge(int argc, const char **argv, const char *prefix)
|
||||||
return result < 0; /* return non-zero on conflicts */
|
return result < 0; /* return non-zero on conflicts */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define IGNORE_MISSING 1
|
||||||
|
|
||||||
|
static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
if (get_sha1(name, sha1))
|
||||||
|
return error(_("Failed to resolve '%s' as a valid ref."), name);
|
||||||
|
status = remove_note(t, sha1);
|
||||||
|
if (status)
|
||||||
|
fprintf(stderr, _("Object %s has no note\n"), name);
|
||||||
|
else
|
||||||
|
fprintf(stderr, _("Removing note for object %s\n"), name);
|
||||||
|
return (flag & IGNORE_MISSING) ? 0 : status;
|
||||||
|
}
|
||||||
|
|
||||||
static int remove_cmd(int argc, const char **argv, const char *prefix)
|
static int remove_cmd(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
|
unsigned flag = 0;
|
||||||
|
int from_stdin = 0;
|
||||||
struct option options[] = {
|
struct option options[] = {
|
||||||
|
OPT_BIT(0, "ignore-missing", &flag,
|
||||||
|
"attempt to remove non-existent note is not an error",
|
||||||
|
IGNORE_MISSING),
|
||||||
|
OPT_BOOLEAN(0, "stdin", &from_stdin,
|
||||||
|
"read object names from the standard input"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
const char *object_ref;
|
|
||||||
struct notes_tree *t;
|
struct notes_tree *t;
|
||||||
unsigned char object[20];
|
int retval = 0;
|
||||||
int retval;
|
|
||||||
|
|
||||||
argc = parse_options(argc, argv, prefix, options,
|
argc = parse_options(argc, argv, prefix, options,
|
||||||
git_notes_remove_usage, 0);
|
git_notes_remove_usage, 0);
|
||||||
|
|
||||||
if (1 < argc) {
|
|
||||||
error(_("too many parameters"));
|
|
||||||
usage_with_options(git_notes_remove_usage, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
object_ref = argc ? argv[0] : "HEAD";
|
|
||||||
|
|
||||||
if (get_sha1(object_ref, object))
|
|
||||||
die(_("Failed to resolve '%s' as a valid ref."), object_ref);
|
|
||||||
|
|
||||||
t = init_notes_check("remove");
|
t = init_notes_check("remove");
|
||||||
|
|
||||||
retval = remove_note(t, object);
|
if (!argc && !from_stdin) {
|
||||||
if (retval)
|
retval = remove_one_note(t, "HEAD", flag);
|
||||||
fprintf(stderr, _("Object %s has no note\n"), sha1_to_hex(object));
|
} else {
|
||||||
else {
|
while (*argv) {
|
||||||
fprintf(stderr, _("Removing note for object %s\n"),
|
retval |= remove_one_note(t, *argv, flag);
|
||||||
sha1_to_hex(object));
|
argv++;
|
||||||
|
}
|
||||||
commit_notes(t, "Notes removed by 'git notes remove'");
|
|
||||||
}
|
}
|
||||||
|
if (from_stdin) {
|
||||||
|
struct strbuf sb = STRBUF_INIT;
|
||||||
|
while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
|
||||||
|
strbuf_rtrim(&sb);
|
||||||
|
retval |= remove_one_note(t, sb.buf, flag);
|
||||||
|
}
|
||||||
|
strbuf_release(&sb);
|
||||||
|
}
|
||||||
|
if (!retval)
|
||||||
|
commit_notes(t, "Notes removed by 'git notes remove'");
|
||||||
free_notes(t);
|
free_notes(t);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ static int is_rev_argument(const char *arg)
|
||||||
"--branches=",
|
"--branches=",
|
||||||
"--branches",
|
"--branches",
|
||||||
"--header",
|
"--header",
|
||||||
|
"--ignore-missing",
|
||||||
"--max-age=",
|
"--max-age=",
|
||||||
"--max-count=",
|
"--max-count=",
|
||||||
"--min-age=",
|
"--min-age=",
|
||||||
|
|
15
revision.c
15
revision.c
|
@ -133,6 +133,8 @@ void mark_parents_uninteresting(struct commit *commit)
|
||||||
|
|
||||||
static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
|
static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
|
||||||
{
|
{
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
if (revs->no_walk && (obj->flags & UNINTERESTING))
|
if (revs->no_walk && (obj->flags & UNINTERESTING))
|
||||||
revs->no_walk = 0;
|
revs->no_walk = 0;
|
||||||
if (revs->reflog_info && obj->type == OBJ_COMMIT) {
|
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
|
||||||
struct object *object;
|
struct object *object;
|
||||||
|
|
||||||
object = parse_object(sha1);
|
object = parse_object(sha1);
|
||||||
if (!object)
|
if (!object) {
|
||||||
|
if (revs->ignore_missing)
|
||||||
|
return object;
|
||||||
die("bad object %s", name);
|
die("bad object %s", name);
|
||||||
|
}
|
||||||
object->flags |= flags;
|
object->flags |= flags;
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
@ -906,6 +911,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
|
||||||
return 0;
|
return 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
it = get_reference(revs, arg, sha1, 0);
|
it = get_reference(revs, arg, sha1, 0);
|
||||||
|
if (!it && revs->ignore_missing)
|
||||||
|
return 0;
|
||||||
if (it->type != OBJ_TAG)
|
if (it->type != OBJ_TAG)
|
||||||
break;
|
break;
|
||||||
if (!((struct tag*)it)->tagged)
|
if (!((struct tag*)it)->tagged)
|
||||||
|
@ -1044,6 +1051,8 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
|
||||||
a = lookup_commit_reference(from_sha1);
|
a = lookup_commit_reference(from_sha1);
|
||||||
b = lookup_commit_reference(sha1);
|
b = lookup_commit_reference(sha1);
|
||||||
if (!a || !b) {
|
if (!a || !b) {
|
||||||
|
if (revs->ignore_missing)
|
||||||
|
return 0;
|
||||||
die(symmetric ?
|
die(symmetric ?
|
||||||
"Invalid symmetric difference expression %s...%s" :
|
"Invalid symmetric difference expression %s...%s" :
|
||||||
"Invalid revision range %s..%s",
|
"Invalid revision range %s..%s",
|
||||||
|
@ -1090,7 +1099,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
|
||||||
arg++;
|
arg++;
|
||||||
}
|
}
|
||||||
if (get_sha1_with_mode(arg, sha1, &mode))
|
if (get_sha1_with_mode(arg, sha1, &mode))
|
||||||
return -1;
|
return revs->ignore_missing ? 0 : -1;
|
||||||
if (!cant_be_filename)
|
if (!cant_be_filename)
|
||||||
verify_non_filename(revs->prefix, arg);
|
verify_non_filename(revs->prefix, arg);
|
||||||
object = get_reference(revs, arg, sha1, flags ^ local_flags);
|
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
|
||||||
} else if (!strcmp(arg, "--children")) {
|
} else if (!strcmp(arg, "--children")) {
|
||||||
revs->children.name = "children";
|
revs->children.name = "children";
|
||||||
revs->limited = 1;
|
revs->limited = 1;
|
||||||
|
} else if (!strcmp(arg, "--ignore-missing")) {
|
||||||
|
revs->ignore_missing = 1;
|
||||||
} else {
|
} else {
|
||||||
int opts = diff_opt_parse(&revs->diffopt, argv, argc);
|
int opts = diff_opt_parse(&revs->diffopt, argv, argc);
|
||||||
if (!opts)
|
if (!opts)
|
||||||
|
|
|
@ -36,7 +36,8 @@ struct rev_info {
|
||||||
const char *prefix;
|
const char *prefix;
|
||||||
const char *def;
|
const char *def;
|
||||||
struct pathspec prune_data;
|
struct pathspec prune_data;
|
||||||
unsigned int early_output;
|
unsigned int early_output:1,
|
||||||
|
ignore_missing:1;
|
||||||
|
|
||||||
/* Traversal flags */
|
/* Traversal flags */
|
||||||
unsigned int dense:1,
|
unsigned int dense:1,
|
||||||
|
|
|
@ -435,6 +435,81 @@ test_expect_success 'removing non-existing note should not create new commit' '
|
||||||
test_cmp before_commit after_commit
|
test_cmp before_commit after_commit
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'removing more than one' '
|
||||||
|
before=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test_when_finished "git update-ref refs/notes/commits $before" &&
|
||||||
|
|
||||||
|
# We have only two -- add another and make sure it stays
|
||||||
|
git notes add -m "extra" &&
|
||||||
|
git notes list HEAD >after-removal-expect &&
|
||||||
|
git notes remove HEAD^^ HEAD^^^ &&
|
||||||
|
git notes list | sed -e "s/ .*//" >actual &&
|
||||||
|
test_cmp after-removal-expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'removing is atomic' '
|
||||||
|
before=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test_when_finished "git update-ref refs/notes/commits $before" &&
|
||||||
|
test_must_fail git notes remove HEAD^^ HEAD^^^ HEAD^ &&
|
||||||
|
after=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test "$before" = "$after"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'removing with --ignore-missing' '
|
||||||
|
before=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test_when_finished "git update-ref refs/notes/commits $before" &&
|
||||||
|
|
||||||
|
# We have only two -- add another and make sure it stays
|
||||||
|
git notes add -m "extra" &&
|
||||||
|
git notes list HEAD >after-removal-expect &&
|
||||||
|
git notes remove --ignore-missing HEAD^^ HEAD^^^ HEAD^ &&
|
||||||
|
git notes list | sed -e "s/ .*//" >actual &&
|
||||||
|
test_cmp after-removal-expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'removing with --ignore-missing but bogus ref' '
|
||||||
|
before=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test_when_finished "git update-ref refs/notes/commits $before" &&
|
||||||
|
test_must_fail git notes remove --ignore-missing HEAD^^ HEAD^^^ NO-SUCH-COMMIT &&
|
||||||
|
after=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test "$before" = "$after"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'remove reads from --stdin' '
|
||||||
|
before=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test_when_finished "git update-ref refs/notes/commits $before" &&
|
||||||
|
|
||||||
|
# We have only two -- add another and make sure it stays
|
||||||
|
git notes add -m "extra" &&
|
||||||
|
git notes list HEAD >after-removal-expect &&
|
||||||
|
git rev-parse HEAD^^ HEAD^^^ >input &&
|
||||||
|
git notes remove --stdin <input &&
|
||||||
|
git notes list | sed -e "s/ .*//" >actual &&
|
||||||
|
test_cmp after-removal-expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'remove --stdin is also atomic' '
|
||||||
|
before=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test_when_finished "git update-ref refs/notes/commits $before" &&
|
||||||
|
git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
|
||||||
|
test_must_fail git notes remove --stdin <input &&
|
||||||
|
after=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test "$before" = "$after"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'removing with --stdin --ignore-missing' '
|
||||||
|
before=$(git rev-parse --verify refs/notes/commits) &&
|
||||||
|
test_when_finished "git update-ref refs/notes/commits $before" &&
|
||||||
|
|
||||||
|
# We have only two -- add another and make sure it stays
|
||||||
|
git notes add -m "extra" &&
|
||||||
|
git notes list HEAD >after-removal-expect &&
|
||||||
|
git rev-parse HEAD^^ HEAD^^^ HEAD^ >input &&
|
||||||
|
git notes remove --ignore-missing --stdin <input &&
|
||||||
|
git notes list | sed -e "s/ .*//" >actual &&
|
||||||
|
test_cmp after-removal-expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'list notes with "git notes list"' '
|
test_expect_success 'list notes with "git notes list"' '
|
||||||
git notes list > output &&
|
git notes list > output &&
|
||||||
test_cmp expect output
|
test_cmp expect output
|
||||||
|
|
Loading…
Reference in New Issue