mirror of
https://github.com/git/git
synced 2024-08-25 02:35:46 +00:00
Merge branch 'ab/ref-filter-no-contains'
"git tag/branch/for-each-ref" family of commands long allowed to filter the refs by "--contains X" (show only the refs that are descendants of X), "--merged X" (show only the refs that are ancestors of X), "--no-merged X" (show only the refs that are not ancestors of X). One curious omission, "--no-contains X" (show only the refs that are not descendants of X) has been added to them. * ab/ref-filter-no-contains: tag: add tests for --with and --without ref-filter: reflow recently changed branch/tag/for-each-ref docs ref-filter: add --no-contains option to tag/branch/for-each-ref tag: change --point-at to default to HEAD tag: implicitly supply --list given another list-like option tag: change misleading --list <pattern> documentation parse-options: add OPT_NONEG to the "contains" option tag: add more incompatibles mode tests for-each-ref: partly change <object> to <commit> in help tag tests: fix a typo in a test description tag: remove a TODO item from the test suite ref-filter: add test for --contains on a non-commit ref-filter: make combining --merged & --no-merged an error tag doc: reword --[no-]merged to talk about commits, not tips tag doc: split up the --[no-]merged documentation tag doc: move the description of --[no-]merged earlier
This commit is contained in:
commit
d1d3d46146
|
@ -10,8 +10,9 @@ SYNOPSIS
|
|||
[verse]
|
||||
'git branch' [--color[=<when>] | --no-color] [-r | -a]
|
||||
[--list] [-v [--abbrev=<length> | --no-abbrev]]
|
||||
[--column[=<options>] | --no-column]
|
||||
[(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]
|
||||
[--column[=<options>] | --no-column] [--sort=<key>]
|
||||
[(--merged | --no-merged) [<commit>]]
|
||||
[--contains [<commit]] [--no-contains [<commit>]]
|
||||
[--points-at <object>] [--format=<format>] [<pattern>...]
|
||||
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
|
||||
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
|
||||
|
@ -35,11 +36,12 @@ as branch creation.
|
|||
|
||||
With `--contains`, shows only the branches that contain the named commit
|
||||
(in other words, the branches whose tip commits are descendants of the
|
||||
named commit). With `--merged`, only branches merged into the named
|
||||
commit (i.e. the branches whose tip commits are reachable from the named
|
||||
commit) will be listed. With `--no-merged` only branches not merged into
|
||||
the named commit will be listed. If the <commit> argument is missing it
|
||||
defaults to `HEAD` (i.e. the tip of the current branch).
|
||||
named commit), `--no-contains` inverts it. With `--merged`, only branches
|
||||
merged into the named commit (i.e. the branches whose tip commits are
|
||||
reachable from the named commit) will be listed. With `--no-merged` only
|
||||
branches not merged into the named commit will be listed. If the <commit>
|
||||
argument is missing it defaults to `HEAD` (i.e. the tip of the current
|
||||
branch).
|
||||
|
||||
The command's second form creates a new branch head named <branchname>
|
||||
which points to the current `HEAD`, or <start-point> if given.
|
||||
|
@ -218,13 +220,19 @@ start-point is either a local or remote-tracking branch.
|
|||
Only list branches which contain the specified commit (HEAD
|
||||
if not specified). Implies `--list`.
|
||||
|
||||
--no-contains [<commit>]::
|
||||
Only list branches which don't contain the specified commit
|
||||
(HEAD if not specified). Implies `--list`.
|
||||
|
||||
--merged [<commit>]::
|
||||
Only list branches whose tips are reachable from the
|
||||
specified commit (HEAD if not specified). Implies `--list`.
|
||||
specified commit (HEAD if not specified). Implies `--list`,
|
||||
incompatible with `--no-merged`.
|
||||
|
||||
--no-merged [<commit>]::
|
||||
Only list branches whose tips are not reachable from the
|
||||
specified commit (HEAD if not specified). Implies `--list`.
|
||||
specified commit (HEAD if not specified). Implies `--list`,
|
||||
incompatible with `--merged`.
|
||||
|
||||
<branchname>::
|
||||
The name of the branch to create or delete.
|
||||
|
@ -301,13 +309,16 @@ If you are creating a branch that you want to checkout immediately, it is
|
|||
easier to use the git checkout command with its `-b` option to create
|
||||
a branch and check it out with a single command.
|
||||
|
||||
The options `--contains`, `--merged` and `--no-merged` serve three related
|
||||
but different purposes:
|
||||
The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
|
||||
serve four related but different purposes:
|
||||
|
||||
- `--contains <commit>` is used to find all branches which will need
|
||||
special attention if <commit> were to be rebased or amended, since those
|
||||
branches contain the specified <commit>.
|
||||
|
||||
- `--no-contains <commit>` is the inverse of that, i.e. branches that don't
|
||||
contain the specified <commit>.
|
||||
|
||||
- `--merged` is used to find all branches which can be safely deleted,
|
||||
since those branches are fully contained by HEAD.
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ SYNOPSIS
|
|||
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
|
||||
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
|
||||
[--points-at <object>] [(--merged | --no-merged) [<object>]]
|
||||
[--contains [<object>]]
|
||||
[--contains [<object>]] [--no-contains [<object>]]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
@ -69,16 +69,22 @@ OPTIONS
|
|||
|
||||
--merged [<object>]::
|
||||
Only list refs whose tips are reachable from the
|
||||
specified commit (HEAD if not specified).
|
||||
specified commit (HEAD if not specified),
|
||||
incompatible with `--no-merged`.
|
||||
|
||||
--no-merged [<object>]::
|
||||
Only list refs whose tips are not reachable from the
|
||||
specified commit (HEAD if not specified).
|
||||
specified commit (HEAD if not specified),
|
||||
incompatible with `--merged`.
|
||||
|
||||
--contains [<object>]::
|
||||
Only list refs which contain the specified commit (HEAD if not
|
||||
specified).
|
||||
|
||||
--no-contains [<object>]::
|
||||
Only list refs which don't contain the specified commit (HEAD
|
||||
if not specified).
|
||||
|
||||
--ignore-case::
|
||||
Sorting and filtering refs are case insensitive.
|
||||
|
||||
|
|
|
@ -12,9 +12,10 @@ SYNOPSIS
|
|||
'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>]
|
||||
<tagname> [<commit> | <object>]
|
||||
'git tag' -d <tagname>...
|
||||
'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
|
||||
[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
|
||||
[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]
|
||||
'git tag' [-n[<num>]] -l [--contains <commit>] [--contains <commit>]
|
||||
[--points-at <object>] [--column[=<options>] | --no-column]
|
||||
[--create-reflog] [--sort=<key>] [--format=<format>]
|
||||
[--[no-]merged [<commit>]] [<pattern>...]
|
||||
'git tag' -v [--format=<format>] <tagname>...
|
||||
|
||||
DESCRIPTION
|
||||
|
@ -82,18 +83,24 @@ OPTIONS
|
|||
|
||||
-n<num>::
|
||||
<num> specifies how many lines from the annotation, if any,
|
||||
are printed when using -l.
|
||||
are printed when using -l. Implies `--list`.
|
||||
+
|
||||
The default is not to print any annotation lines.
|
||||
If no number is given to `-n`, only the first line is printed.
|
||||
If the tag is not annotated, the commit message is displayed instead.
|
||||
|
||||
-l <pattern>::
|
||||
--list <pattern>::
|
||||
List tags with names that match the given pattern (or all if no
|
||||
pattern is given). Running "git tag" without arguments also
|
||||
lists all tags. The pattern is a shell wildcard (i.e., matched
|
||||
using fnmatch(3)). Multiple patterns may be given; if any of
|
||||
them matches, the tag is shown.
|
||||
-l::
|
||||
--list::
|
||||
List tags. With optional `<pattern>...`, e.g. `git tag --list
|
||||
'v-*'`, list only the tags that match the pattern(s).
|
||||
+
|
||||
Running "git tag" without arguments also lists all tags. The pattern
|
||||
is a shell wildcard (i.e., matched using fnmatch(3)). Multiple
|
||||
patterns may be given; if any of them matches, the tag is shown.
|
||||
+
|
||||
This option is implicitly supplied if any other list-like option such
|
||||
as `--contains` is provided. See the documentation for each of those
|
||||
options for details.
|
||||
|
||||
--sort=<key>::
|
||||
Sort based on the key given. Prefix `-` to sort in
|
||||
|
@ -122,10 +129,23 @@ This option is only applicable when listing tags without annotation lines.
|
|||
|
||||
--contains [<commit>]::
|
||||
Only list tags which contain the specified commit (HEAD if not
|
||||
specified).
|
||||
specified). Implies `--list`.
|
||||
|
||||
--no-contains [<commit>]::
|
||||
Only list tags which don't contain the specified commit (HEAD if
|
||||
not specified). Implies `--list`.
|
||||
|
||||
--merged [<commit>]::
|
||||
Only list tags whose commits are reachable from the specified
|
||||
commit (`HEAD` if not specified), incompatible with `--no-merged`.
|
||||
|
||||
--no-merged [<commit>]::
|
||||
Only list tags whose commits are not reachable from the specified
|
||||
commit (`HEAD` if not specified), incompatible with `--merged`.
|
||||
|
||||
--points-at <object>::
|
||||
Only list tags of the given object.
|
||||
Only list tags of the given object (HEAD if not
|
||||
specified). Implies `--list`.
|
||||
|
||||
-m <msg>::
|
||||
--message=<msg>::
|
||||
|
@ -173,11 +193,6 @@ This option is only applicable when listing tags without annotation lines.
|
|||
that of linkgit:git-for-each-ref[1]. When unspecified,
|
||||
defaults to `%(refname:strip=2)`.
|
||||
|
||||
--[no-]merged [<commit>]::
|
||||
Only list tags whose tips are reachable, or not reachable
|
||||
if `--no-merged` is used, from the specified commit (`HEAD`
|
||||
if not specified).
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
By default, 'git tag' in sign-with-default mode (-s) will use your
|
||||
|
|
|
@ -562,7 +562,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
|
||||
FILTER_REFS_REMOTES),
|
||||
OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
|
||||
OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
|
||||
OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
|
||||
OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")),
|
||||
OPT__ABBREV(&filter.abbrev),
|
||||
|
||||
OPT_GROUP(N_("Specific git-branch actions:")),
|
||||
|
@ -618,7 +620,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
|||
if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
|
||||
list = 1;
|
||||
|
||||
if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr)
|
||||
if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
|
||||
filter.no_commit)
|
||||
list = 1;
|
||||
|
||||
if (!!delete + !!rename + !!new_upstream +
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
static char const * const for_each_ref_usage[] = {
|
||||
N_("git for-each-ref [<options>] [<pattern>]"),
|
||||
N_("git for-each-ref [--points-at <object>]"),
|
||||
N_("git for-each-ref [(--merged | --no-merged) [<object>]]"),
|
||||
N_("git for-each-ref [--contains [<object>]]"),
|
||||
N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"),
|
||||
N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -43,6 +43,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
|
|||
OPT_MERGED(&filter, N_("print only refs that are merged")),
|
||||
OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
|
||||
OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
|
||||
OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
|
||||
OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
|
||||
OPT_END(),
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
static const char * const git_tag_usage[] = {
|
||||
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
|
||||
N_("git tag -d <tagname>..."),
|
||||
N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]"
|
||||
N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]"
|
||||
"\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
|
||||
N_("git tag -v [--format=<format>] <tagname>..."),
|
||||
NULL
|
||||
|
@ -424,14 +424,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
|||
OPT_GROUP(N_("Tag listing options")),
|
||||
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
|
||||
OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
|
||||
OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")),
|
||||
OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
|
||||
OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
|
||||
OPT_MERGED(&filter, N_("print only tags that are merged")),
|
||||
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
|
||||
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
|
||||
N_("field name to sort on"), &parse_opt_ref_sorting),
|
||||
{
|
||||
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
|
||||
N_("print only tags of the object"), 0, parse_opt_object_name
|
||||
N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
|
||||
parse_opt_object_name, (intptr_t) "HEAD"
|
||||
},
|
||||
OPT_STRING( 0 , "format", &format, N_("format"), N_("format to use for the output")),
|
||||
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
|
||||
|
@ -454,8 +457,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
create_tag_object = (opt.sign || annotate || msg.given || msgfile);
|
||||
|
||||
if (argc == 0 && !cmdmode)
|
||||
if (!cmdmode) {
|
||||
if (argc == 0)
|
||||
cmdmode = 'l';
|
||||
else if (filter.with_commit || filter.no_commit ||
|
||||
filter.points_at.nr || filter.merge_commit ||
|
||||
filter.lines != -1)
|
||||
cmdmode = 'l';
|
||||
}
|
||||
|
||||
if ((create_tag_object || force) && (cmdmode != 0))
|
||||
usage_with_options(git_tag_usage, options);
|
||||
|
@ -485,13 +494,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
|||
return ret;
|
||||
}
|
||||
if (filter.lines != -1)
|
||||
die(_("-n option is only allowed with -l."));
|
||||
die(_("-n option is only allowed in list mode"));
|
||||
if (filter.with_commit)
|
||||
die(_("--contains option is only allowed with -l."));
|
||||
die(_("--contains option is only allowed in list mode"));
|
||||
if (filter.no_commit)
|
||||
die(_("--no-contains option is only allowed in list mode"));
|
||||
if (filter.points_at.nr)
|
||||
die(_("--points-at option is only allowed with -l."));
|
||||
die(_("--points-at option is only allowed in list mode"));
|
||||
if (filter.merge_commit)
|
||||
die(_("--merged and --no-merged option are only allowed with -l"));
|
||||
die(_("--merged and --no-merged options are only allowed in list mode"));
|
||||
if (cmdmode == 'd')
|
||||
return for_each_tag_name(argv, delete_tag, NULL);
|
||||
if (cmdmode == 'v') {
|
||||
|
|
|
@ -1194,7 +1194,7 @@ _git_branch ()
|
|||
--*)
|
||||
__gitcomp "
|
||||
--color --no-color --verbose --abbrev= --no-abbrev
|
||||
--track --no-track --contains --merged --no-merged
|
||||
--track --no-track --contains --no-contains --merged --no-merged
|
||||
--set-upstream-to= --edit-description --list
|
||||
--unset-upstream --delete --move --remotes
|
||||
--column --no-column --sort= --points-at
|
||||
|
@ -3023,7 +3023,7 @@ _git_tag ()
|
|||
__gitcomp "
|
||||
--list --delete --verify --annotate --message --file
|
||||
--sign --cleanup --local-user --force --column --sort=
|
||||
--contains --points-at --merged --no-merged --create-reflog
|
||||
--contains --no-contains --points-at --merged --no-merged --create-reflog
|
||||
"
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -258,7 +258,9 @@ extern int parse_opt_passthru_argv(const struct option *, const char *, int);
|
|||
PARSE_OPT_LASTARG_DEFAULT | flag, \
|
||||
parse_opt_commits, (intptr_t) "HEAD" \
|
||||
}
|
||||
#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, 0)
|
||||
#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN)
|
||||
#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG)
|
||||
#define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
|
||||
#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
|
||||
#define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
|
||||
|
||||
#endif
|
||||
|
|
30
ref-filter.c
30
ref-filter.c
|
@ -1487,6 +1487,7 @@ struct ref_filter_cbdata {
|
|||
struct ref_array *array;
|
||||
struct ref_filter *filter;
|
||||
struct contains_cache contains_cache;
|
||||
struct contains_cache no_contains_cache;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1586,11 +1587,11 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
|
|||
}
|
||||
|
||||
static int commit_contains(struct ref_filter *filter, struct commit *commit,
|
||||
struct contains_cache *cache)
|
||||
struct commit_list *list, struct contains_cache *cache)
|
||||
{
|
||||
if (filter->with_commit_tag_algo)
|
||||
return contains_tag_algo(commit, filter->with_commit, cache) == CONTAINS_YES;
|
||||
return is_descendant_of(commit, filter->with_commit);
|
||||
return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
|
||||
return is_descendant_of(commit, list);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1780,13 +1781,17 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
|
|||
* obtain the commit using the 'oid' available and discard all
|
||||
* non-commits early. The actual filtering is done later.
|
||||
*/
|
||||
if (filter->merge_commit || filter->with_commit || filter->verbose) {
|
||||
if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) {
|
||||
commit = lookup_commit_reference_gently(oid->hash, 1);
|
||||
if (!commit)
|
||||
return 0;
|
||||
/* We perform the filtering for the '--contains' option */
|
||||
/* We perform the filtering for the '--contains' option... */
|
||||
if (filter->with_commit &&
|
||||
!commit_contains(filter, commit, &ref_cbdata->contains_cache))
|
||||
!commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache))
|
||||
return 0;
|
||||
/* ...or for the `--no-contains' option */
|
||||
if (filter->no_commit &&
|
||||
commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache))
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1887,6 +1892,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
|
|||
filter->kind = type & FILTER_REFS_KIND_MASK;
|
||||
|
||||
init_contains_cache(&ref_cbdata.contains_cache);
|
||||
init_contains_cache(&ref_cbdata.no_contains_cache);
|
||||
|
||||
/* Simple per-ref filtering */
|
||||
if (!filter->kind)
|
||||
|
@ -1911,6 +1917,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
|
|||
}
|
||||
|
||||
clear_contains_cache(&ref_cbdata.contains_cache);
|
||||
clear_contains_cache(&ref_cbdata.no_contains_cache);
|
||||
|
||||
/* Filters that need revision walking */
|
||||
if (filter->merge_commit)
|
||||
|
@ -2084,8 +2091,17 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
|
|||
{
|
||||
struct ref_filter *rf = opt->value;
|
||||
unsigned char sha1[20];
|
||||
int no_merged = starts_with(opt->long_name, "no");
|
||||
|
||||
rf->merge = starts_with(opt->long_name, "no")
|
||||
if (rf->merge) {
|
||||
if (no_merged) {
|
||||
return opterror(opt, "is incompatible with --merged", 0);
|
||||
} else {
|
||||
return opterror(opt, "is incompatible with --no-merged", 0);
|
||||
}
|
||||
}
|
||||
|
||||
rf->merge = no_merged
|
||||
? REF_FILTER_MERGED_OMIT
|
||||
: REF_FILTER_MERGED_INCLUDE;
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ struct ref_filter {
|
|||
const char **name_patterns;
|
||||
struct sha1_array points_at;
|
||||
struct commit_list *with_commit;
|
||||
struct commit_list *no_commit;
|
||||
|
||||
enum {
|
||||
REF_FILTER_MERGED_NONE = 0,
|
||||
|
|
|
@ -978,6 +978,10 @@ test_expect_success '--merged catches invalid object names' '
|
|||
test_must_fail git branch --merged 0000000000000000000000000000000000000000
|
||||
'
|
||||
|
||||
test_expect_success '--merged is incompatible with --no-merged' '
|
||||
test_must_fail git branch --merged HEAD --no-merged HEAD
|
||||
'
|
||||
|
||||
test_expect_success 'tracking with unexpected .fetch refspec' '
|
||||
rm -rf a b c d &&
|
||||
git init a &&
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='branch --contains <commit>, --merged, and --no-merged'
|
||||
test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
|
@ -45,6 +45,22 @@ test_expect_success 'branch --contains master' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --no-contains=master' '
|
||||
|
||||
git branch --no-contains=master >actual &&
|
||||
>expect &&
|
||||
test_cmp expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --no-contains master' '
|
||||
|
||||
git branch --no-contains master >actual &&
|
||||
>expect &&
|
||||
test_cmp expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --contains=side' '
|
||||
|
||||
git branch --contains=side >actual &&
|
||||
|
@ -55,6 +71,16 @@ test_expect_success 'branch --contains=side' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --no-contains=side' '
|
||||
|
||||
git branch --no-contains=side >actual &&
|
||||
{
|
||||
echo " master"
|
||||
} >expect &&
|
||||
test_cmp expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --contains with pattern implies --list' '
|
||||
|
||||
git branch --contains=master master >actual &&
|
||||
|
@ -65,6 +91,14 @@ test_expect_success 'branch --contains with pattern implies --list' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'branch --no-contains with pattern implies --list' '
|
||||
|
||||
git branch --no-contains=master master >actual &&
|
||||
>expect &&
|
||||
test_cmp expect actual
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'side: branch --merged' '
|
||||
|
||||
git branch --merged >actual &&
|
||||
|
@ -126,10 +160,22 @@ test_expect_success 'branch --no-merged with pattern implies --list' '
|
|||
test_expect_success 'implicit --list conflicts with modification options' '
|
||||
|
||||
test_must_fail git branch --contains=master -d &&
|
||||
test_must_fail git branch --contains=master -m foo
|
||||
test_must_fail git branch --contains=master -m foo &&
|
||||
test_must_fail git branch --no-contains=master -d &&
|
||||
test_must_fail git branch --no-contains=master -m foo
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'Assert that --contains only works on commits, not trees & blobs' '
|
||||
test_must_fail git branch --contains master^{tree} &&
|
||||
blob=$(git hash-object -w --stdin <<-\EOF
|
||||
Some blob
|
||||
EOF
|
||||
) &&
|
||||
test_must_fail git branch --contains $blob &&
|
||||
test_must_fail git branch --no-contains $blob
|
||||
'
|
||||
|
||||
# We want to set up a case where the walk for the tracking info
|
||||
# of one branch crosses the tip of another branch (and make sure
|
||||
# that the latter walk does not mess up our flag to see if it was
|
||||
|
@ -159,4 +205,15 @@ test_expect_success 'branch --merged with --verbose' '
|
|||
test_i18ncmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'branch --contains combined with --no-contains' '
|
||||
git branch --contains zzz --no-contains topic >actual &&
|
||||
cat >expect <<-\EOF &&
|
||||
master
|
||||
side
|
||||
zzz
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -93,6 +93,22 @@ test_expect_success 'filtering with --contains' '
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'filtering with --no-contains' '
|
||||
cat >expect <<-\EOF &&
|
||||
refs/tags/one
|
||||
EOF
|
||||
git for-each-ref --format="%(refname)" --no-contains=two >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'filtering with --contains and --no-contains' '
|
||||
cat >expect <<-\EOF &&
|
||||
refs/tags/two
|
||||
EOF
|
||||
git for-each-ref --format="%(refname)" --contains=two --no-contains=three >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '%(color) must fail' '
|
||||
test_must_fail git for-each-ref --format="%(color)%(refname)"
|
||||
'
|
||||
|
@ -421,4 +437,8 @@ test_expect_success 'check %(if:notequals=<string>)' '
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--merged is incompatible with --no-merged' '
|
||||
test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
243
t/t7004-tag.sh
243
t/t7004-tag.sh
|
@ -16,7 +16,6 @@ tag_exists () {
|
|||
git show-ref --quiet --verify refs/tags/"$1"
|
||||
}
|
||||
|
||||
# todo: git tag -l now returns always zero, when fixed, change this test
|
||||
test_expect_success 'listing all tags in an empty tree should succeed' '
|
||||
git tag -l &&
|
||||
git tag
|
||||
|
@ -119,6 +118,18 @@ test_expect_success 'listing all tags if one exists should succeed' '
|
|||
git tag
|
||||
'
|
||||
|
||||
cat >expect <<EOF
|
||||
mytag
|
||||
EOF
|
||||
test_expect_success 'Multiple -l or --list options are equivalent to one -l option' '
|
||||
git tag -l -l >actual &&
|
||||
test_cmp expect actual &&
|
||||
git tag --list --list >actual &&
|
||||
test_cmp expect actual &&
|
||||
git tag --list -l --list >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'listing all tags if one exists should output that tag' '
|
||||
test $(git tag -l) = mytag &&
|
||||
test $(git tag) = mytag
|
||||
|
@ -136,9 +147,8 @@ test_expect_success \
|
|||
'listing a tag using a matching pattern should output that tag' \
|
||||
'test $(git tag -l mytag) = mytag'
|
||||
|
||||
# todo: git tag -l now returns always zero, when fixed, change this test
|
||||
test_expect_success \
|
||||
'listing tags using a non-matching pattern should suceed' \
|
||||
'listing tags using a non-matching pattern should succeed' \
|
||||
'git tag -l xxx'
|
||||
|
||||
test_expect_success \
|
||||
|
@ -338,6 +348,19 @@ test_expect_success 'tag -l can accept multiple patterns' '
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
# Between v1.7.7 & v2.13.0 a fair reading of the git-tag documentation
|
||||
# could leave you with the impression that "-l <pattern> -l <pattern>"
|
||||
# was how we wanted to accept multiple patterns.
|
||||
#
|
||||
# This test should not imply that this is a sane thing to support. but
|
||||
# since the documentation was worded like it was let's at least find
|
||||
# out if we're going to break this long-documented form of taking
|
||||
# multiple patterns.
|
||||
test_expect_success 'tag -l <pattern> -l <pattern> works, as our buggy documentation previously suggested' '
|
||||
git tag -l "v1*" -l "v0*" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'listing tags in column' '
|
||||
COLUMNS=40 git tag -l --column=row >actual &&
|
||||
cat >expected <<\EOF &&
|
||||
|
@ -620,6 +643,11 @@ test_expect_success \
|
|||
git tag -n0 -l tag-one-line >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
git tag -n0 | grep "^tag-one-line" >actual &&
|
||||
test_cmp expect actual &&
|
||||
git tag -n0 tag-one-line >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
echo "tag-one-line A msg" >expect &&
|
||||
git tag -n1 -l | grep "^tag-one-line" >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
@ -633,6 +661,17 @@ test_expect_success \
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'The -n 100 invocation means -n --list 100, not -n100' '
|
||||
>expect &&
|
||||
git tag -n 100 >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
git tag -m "A msg" 100 &&
|
||||
echo "100 A msg" >expect &&
|
||||
git tag -n 100 >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'listing the zero-lines message of a non-signed tag should succeed' '
|
||||
git tag -m "" tag-zero-lines &&
|
||||
|
@ -1383,6 +1422,23 @@ test_expect_success 'checking that first commit is in all tags (relative)' "
|
|||
test_cmp expected actual
|
||||
"
|
||||
|
||||
# All the --contains tests above, but with --no-contains
|
||||
test_expect_success 'checking that first commit is not listed in any tag with --no-contains (hash)' "
|
||||
>expected &&
|
||||
git tag -l --no-contains $hash1 v* >actual &&
|
||||
test_cmp expected actual
|
||||
"
|
||||
|
||||
test_expect_success 'checking that first commit is in all tags (tag)' "
|
||||
git tag -l --no-contains v1.0 v* >actual &&
|
||||
test_cmp expected actual
|
||||
"
|
||||
|
||||
test_expect_success 'checking that first commit is in all tags (relative)' "
|
||||
git tag -l --no-contains HEAD~2 v* >actual &&
|
||||
test_cmp expected actual
|
||||
"
|
||||
|
||||
cat > expected <<EOF
|
||||
v2.0
|
||||
EOF
|
||||
|
@ -1392,6 +1448,17 @@ test_expect_success 'checking that second commit only has one tag' "
|
|||
test_cmp expected actual
|
||||
"
|
||||
|
||||
cat > expected <<EOF
|
||||
v0.2.1
|
||||
v1.0
|
||||
v1.0.1
|
||||
v1.1.3
|
||||
EOF
|
||||
|
||||
test_expect_success 'inverse of the last test, with --no-contains' "
|
||||
git tag -l --no-contains $hash2 v* >actual &&
|
||||
test_cmp expected actual
|
||||
"
|
||||
|
||||
cat > expected <<EOF
|
||||
EOF
|
||||
|
@ -1401,6 +1468,19 @@ test_expect_success 'checking that third commit has no tags' "
|
|||
test_cmp expected actual
|
||||
"
|
||||
|
||||
cat > expected <<EOF
|
||||
v0.2.1
|
||||
v1.0
|
||||
v1.0.1
|
||||
v1.1.3
|
||||
v2.0
|
||||
EOF
|
||||
|
||||
test_expect_success 'conversely --no-contains on the third commit lists all tags' "
|
||||
git tag -l --no-contains $hash3 v* >actual &&
|
||||
test_cmp expected actual
|
||||
"
|
||||
|
||||
# how about a simple merge?
|
||||
|
||||
test_expect_success 'creating simple branch' '
|
||||
|
@ -1422,6 +1502,19 @@ test_expect_success 'checking that branch head only has one tag' "
|
|||
test_cmp expected actual
|
||||
"
|
||||
|
||||
cat > expected <<EOF
|
||||
v0.2.1
|
||||
v1.0
|
||||
v1.0.1
|
||||
v1.1.3
|
||||
v2.0
|
||||
EOF
|
||||
|
||||
test_expect_success 'checking that branch head with --no-contains lists all but one tag' "
|
||||
git tag -l --no-contains $hash4 v* >actual &&
|
||||
test_cmp expected actual
|
||||
"
|
||||
|
||||
test_expect_success 'merging original branch into this branch' '
|
||||
git merge --strategy=ours master &&
|
||||
git tag v4.0
|
||||
|
@ -1436,6 +1529,20 @@ test_expect_success 'checking that original branch head has one tag now' "
|
|||
test_cmp expected actual
|
||||
"
|
||||
|
||||
cat > expected <<EOF
|
||||
v0.2.1
|
||||
v1.0
|
||||
v1.0.1
|
||||
v1.1.3
|
||||
v2.0
|
||||
v3.0
|
||||
EOF
|
||||
|
||||
test_expect_success 'checking that original branch head with --no-contains lists all but one tag now' "
|
||||
git tag -l --no-contains $hash3 v* >actual &&
|
||||
test_cmp expected actual
|
||||
"
|
||||
|
||||
cat > expected <<EOF
|
||||
v0.2.1
|
||||
v1.0
|
||||
|
@ -1451,21 +1558,76 @@ test_expect_success 'checking that initial commit is in all tags' "
|
|||
test_cmp expected actual
|
||||
"
|
||||
|
||||
test_expect_success 'checking that --contains can be used in non-list mode' '
|
||||
git tag --contains $hash1 v* >actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_expect_success 'checking that initial commit is in all tags with --no-contains' "
|
||||
>expected &&
|
||||
git tag -l --no-contains $hash1 v* >actual &&
|
||||
test_cmp expected actual
|
||||
"
|
||||
|
||||
# mixing modes and options:
|
||||
|
||||
test_expect_success 'mixing incompatibles modes and options is forbidden' '
|
||||
test_must_fail git tag -a &&
|
||||
test_must_fail git tag -a -l &&
|
||||
test_must_fail git tag -s &&
|
||||
test_must_fail git tag -s -l &&
|
||||
test_must_fail git tag -m &&
|
||||
test_must_fail git tag -m -l &&
|
||||
test_must_fail git tag -m "hlagh" &&
|
||||
test_must_fail git tag -m "hlagh" -l &&
|
||||
test_must_fail git tag -F &&
|
||||
test_must_fail git tag -F -l &&
|
||||
test_must_fail git tag -f &&
|
||||
test_must_fail git tag -f -l &&
|
||||
test_must_fail git tag -a -s -m -F &&
|
||||
test_must_fail git tag -a -s -m -F -l &&
|
||||
test_must_fail git tag -l -v &&
|
||||
test_must_fail git tag -n 100 &&
|
||||
test_must_fail git tag -l -d &&
|
||||
test_must_fail git tag -l -v -d &&
|
||||
test_must_fail git tag -n 100 -v &&
|
||||
test_must_fail git tag -l -m msg &&
|
||||
test_must_fail git tag -l -F some file &&
|
||||
test_must_fail git tag -v -s
|
||||
test_must_fail git tag -v -s &&
|
||||
test_must_fail git tag --contains tag-tree &&
|
||||
test_must_fail git tag --contains tag-blob &&
|
||||
test_must_fail git tag --no-contains tag-tree &&
|
||||
test_must_fail git tag --no-contains tag-blob &&
|
||||
test_must_fail git tag --contains --no-contains &&
|
||||
test_must_fail git tag --no-with HEAD &&
|
||||
test_must_fail git tag --no-without HEAD
|
||||
'
|
||||
|
||||
for option in --contains --with --no-contains --without --merged --no-merged --points-at
|
||||
do
|
||||
test_expect_success "mixing incompatible modes with $option is forbidden" "
|
||||
test_must_fail git tag -d $option HEAD &&
|
||||
test_must_fail git tag -d $option HEAD some-tag &&
|
||||
test_must_fail git tag -v $option HEAD
|
||||
"
|
||||
test_expect_success "Doing 'git tag --list-like $option <commit> <pattern> is permitted" "
|
||||
git tag -n $option HEAD HEAD &&
|
||||
git tag $option HEAD HEAD &&
|
||||
git tag $option
|
||||
"
|
||||
done
|
||||
|
||||
# check points-at
|
||||
|
||||
test_expect_success '--points-at cannot be used in non-list mode' '
|
||||
test_must_fail git tag --points-at=v4.0 foo
|
||||
test_expect_success '--points-at can be used in non-list mode' '
|
||||
echo v4.0 >expect &&
|
||||
git tag --points-at=v4.0 "v*" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--points-at is a synonym for --points-at HEAD' '
|
||||
echo v4.0 >expect &&
|
||||
git tag --points-at >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--points-at finds lightweight tags' '
|
||||
|
@ -1707,7 +1869,7 @@ run_with_limited_stack () {
|
|||
test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
|
||||
|
||||
# we require ulimit, this excludes Windows
|
||||
test_expect_success ULIMIT_STACK_SIZE '--contains works in a deep repo' '
|
||||
test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' '
|
||||
>expect &&
|
||||
i=1 &&
|
||||
while test $i -lt 8000
|
||||
|
@ -1723,7 +1885,9 @@ EOF"
|
|||
git checkout master &&
|
||||
git tag far-far-away HEAD^ &&
|
||||
run_with_limited_stack git tag --contains HEAD >actual &&
|
||||
test_cmp expect actual
|
||||
test_cmp expect actual &&
|
||||
run_with_limited_stack git tag --no-contains HEAD >actual &&
|
||||
test_line_count ">" 10 actual
|
||||
'
|
||||
|
||||
test_expect_success '--format should list tags as per format given' '
|
||||
|
@ -1742,8 +1906,17 @@ test_expect_success 'setup --merged test tags' '
|
|||
git tag mergetest-3 HEAD
|
||||
'
|
||||
|
||||
test_expect_success '--merged cannot be used in non-list mode' '
|
||||
test_must_fail git tag --merged=mergetest-2 foo
|
||||
test_expect_success '--merged can be used in non-list mode' '
|
||||
cat >expect <<-\EOF &&
|
||||
mergetest-1
|
||||
mergetest-2
|
||||
EOF
|
||||
git tag --merged=mergetest-2 "mergetest*" >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--merged is incompatible with --no-merged' '
|
||||
test_must_fail git tag --merged HEAD --no-merged HEAD
|
||||
'
|
||||
|
||||
test_expect_success '--merged shows merged tags' '
|
||||
|
@ -1763,6 +1936,11 @@ test_expect_success '--no-merged show unmerged tags' '
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--no-merged can be used in non-list mode' '
|
||||
git tag --no-merged=mergetest-2 mergetest-* >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'ambiguous branch/tags not marked' '
|
||||
git tag ambiguous &&
|
||||
git branch ambiguous &&
|
||||
|
@ -1771,4 +1949,47 @@ test_expect_success 'ambiguous branch/tags not marked' '
|
|||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--contains combined with --no-contains' '
|
||||
(
|
||||
git init no-contains &&
|
||||
cd no-contains &&
|
||||
test_commit v0.1 &&
|
||||
test_commit v0.2 &&
|
||||
test_commit v0.3 &&
|
||||
test_commit v0.4 &&
|
||||
test_commit v0.5 &&
|
||||
cat >expected <<-\EOF &&
|
||||
v0.2
|
||||
v0.3
|
||||
v0.4
|
||||
EOF
|
||||
git tag --contains v0.2 --no-contains v0.5 >actual &&
|
||||
test_cmp expected actual
|
||||
)
|
||||
'
|
||||
|
||||
# As the docs say, list tags which contain a specified *commit*. We
|
||||
# don't recurse down to tags for trees or blobs pointed to by *those*
|
||||
# commits.
|
||||
test_expect_success 'Does --[no-]contains stop at commits? Yes!' '
|
||||
cd no-contains &&
|
||||
blob=$(git rev-parse v0.3:v0.3.t) &&
|
||||
tree=$(git rev-parse v0.3^{tree}) &&
|
||||
git tag tag-blob $blob &&
|
||||
git tag tag-tree $tree &&
|
||||
git tag --contains v0.3 >actual &&
|
||||
cat >expected <<-\EOF &&
|
||||
v0.3
|
||||
v0.4
|
||||
v0.5
|
||||
EOF
|
||||
test_cmp expected actual &&
|
||||
git tag --no-contains v0.3 >actual &&
|
||||
cat >expected <<-\EOF &&
|
||||
v0.1
|
||||
v0.2
|
||||
EOF
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
Loading…
Reference in a new issue