mirror of
https://github.com/git/git
synced 2024-08-25 10:45:59 +00:00
ref-filter: add --no-contains option to tag/branch/for-each-ref
Change the tag, branch & for-each-ref commands to have a --no-contains option in addition to their longstanding --contains options. This allows for finding the last-good rollout tag given a known-bad <commit>. Given a hypothetically bad commitcf5c7253e0
, the git version to revert to can be found with this hacky two-liner: (git tag -l 'v[0-9]*'; git tag -l --containscf5c7253e0
'v[0-9]*') | sort | uniq -c | grep -E '^ *1 ' | awk '{print $2}' | tail -n 10 With this new --no-contains option the same can be achieved with: git tag -l --no-containscf5c7253e0
'v[0-9]*' | sort | tail -n 10 As the filtering machinery is shared between the tag, branch & for-each-ref commands, implement this for those commands too. A practical use for this with "branch" is e.g. finding branches which were branched off between v2.8.0 and v2.10.0: git branch --contains v2.8.0 --no-contains v2.10.0 The "describe" command also has a --contains option, but its semantics are unrelated to what tag/branch/for-each-ref use --contains for. A --no-contains option for "describe" wouldn't make any sense, other than being exactly equivalent to not supplying --contains at all, which would be confusing at best. Add a --without option to "tag" as an alias for --no-contains, for consistency with --with and --contains. The --with option is undocumented, and possibly the only user of it is Junio (<xmqqefy71iej.fsf@gitster.mtv.corp.google.com>). But it's trivial to support, so let's do that. The additions to the the test suite are inverse copies of the corresponding --contains tests. With this change --no-contains for tag, branch & for-each-ref is just as well tested as the existing --contains option. In addition to those tests, add a test for "tag" which asserts that --no-contains won't find tree/blob tags, which is slightly unintuitive, but consistent with how --contains works & is documented. Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
1e0c3b680c
commit
ac3f5a3468
|
@ -11,7 +11,8 @@ SYNOPSIS
|
||||||
'git branch' [--color[=<when>] | --no-color] [-r | -a]
|
'git branch' [--color[=<when>] | --no-color] [-r | -a]
|
||||||
[--list] [-v [--abbrev=<length> | --no-abbrev]]
|
[--list] [-v [--abbrev=<length> | --no-abbrev]]
|
||||||
[--column[=<options>] | --no-column]
|
[--column[=<options>] | --no-column]
|
||||||
[(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]
|
[(--merged | --no-merged) [<commit>]]
|
||||||
|
[--contains [<commit]] [--no-contains [<commit>]] [--sort=<key>]
|
||||||
[--points-at <object>] [--format=<format>] [<pattern>...]
|
[--points-at <object>] [--format=<format>] [<pattern>...]
|
||||||
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
|
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
|
||||||
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
|
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
|
||||||
|
@ -35,7 +36,7 @@ as branch creation.
|
||||||
|
|
||||||
With `--contains`, shows only the branches that contain the named commit
|
With `--contains`, shows only the branches that contain the named commit
|
||||||
(in other words, the branches whose tip commits are descendants of the
|
(in other words, the branches whose tip commits are descendants of the
|
||||||
named commit). With `--merged`, only branches merged into the named
|
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 (i.e. the branches whose tip commits are reachable from the named
|
||||||
commit) will be listed. With `--no-merged` only branches not merged into
|
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
|
the named commit will be listed. If the <commit> argument is missing it
|
||||||
|
@ -213,6 +214,10 @@ start-point is either a local or remote-tracking branch.
|
||||||
Only list branches which contain the specified commit (HEAD
|
Only list branches which contain the specified commit (HEAD
|
||||||
if not specified). Implies `--list`.
|
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>]::
|
--merged [<commit>]::
|
||||||
Only list branches whose tips are reachable from the
|
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`,
|
||||||
|
@ -298,13 +303,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
|
easier to use the git checkout command with its `-b` option to create
|
||||||
a branch and check it out with a single command.
|
a branch and check it out with a single command.
|
||||||
|
|
||||||
The options `--contains`, `--merged` and `--no-merged` serve three related
|
The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
|
||||||
but different purposes:
|
serve four related but different purposes:
|
||||||
|
|
||||||
- `--contains <commit>` is used to find all branches which will need
|
- `--contains <commit>` is used to find all branches which will need
|
||||||
special attention if <commit> were to be rebased or amended, since those
|
special attention if <commit> were to be rebased or amended, since those
|
||||||
branches contain the specified <commit>.
|
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,
|
- `--merged` is used to find all branches which can be safely deleted,
|
||||||
since those branches are fully contained by HEAD.
|
since those branches are fully contained by HEAD.
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ SYNOPSIS
|
||||||
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
|
'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
|
||||||
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
|
[(--sort=<key>)...] [--format=<format>] [<pattern>...]
|
||||||
[--points-at <object>] [(--merged | --no-merged) [<object>]]
|
[--points-at <object>] [(--merged | --no-merged) [<object>]]
|
||||||
[--contains [<object>]]
|
[--contains [<object>]] [--no-contains [<object>]]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -81,6 +81,10 @@ OPTIONS
|
||||||
Only list refs which contain the specified commit (HEAD if not
|
Only list refs which contain the specified commit (HEAD if not
|
||||||
specified).
|
specified).
|
||||||
|
|
||||||
|
--no-contains [<object>]::
|
||||||
|
Only list refs which don't contain the specified commit (HEAD
|
||||||
|
if not specified).
|
||||||
|
|
||||||
--ignore-case::
|
--ignore-case::
|
||||||
Sorting and filtering refs are case insensitive.
|
Sorting and filtering refs are case insensitive.
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ SYNOPSIS
|
||||||
'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>]
|
'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>]
|
||||||
<tagname> [<commit> | <object>]
|
<tagname> [<commit> | <object>]
|
||||||
'git tag' -d <tagname>...
|
'git tag' -d <tagname>...
|
||||||
'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
|
'git tag' [-n[<num>]] -l [--contains <commit>] [--contains <commit>] [--points-at <object>]
|
||||||
[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
|
[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
|
||||||
[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]
|
[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]
|
||||||
'git tag' -v [--format=<format>] <tagname>...
|
'git tag' -v [--format=<format>] <tagname>...
|
||||||
|
@ -130,6 +130,10 @@ This option is only applicable when listing tags without annotation lines.
|
||||||
Only list tags which contain the specified commit (HEAD if not
|
Only list tags which contain the specified commit (HEAD if not
|
||||||
specified). Implies `--list`.
|
specified). Implies `--list`.
|
||||||
|
|
||||||
|
--no-contains [<commit>]::
|
||||||
|
Only list tags which don't contain the specified commit (HEAD if
|
||||||
|
not specified). Implies `--list`.
|
||||||
|
|
||||||
--merged [<commit>]::
|
--merged [<commit>]::
|
||||||
Only list tags whose commits are reachable from the specified
|
Only list tags whose commits are reachable from the specified
|
||||||
commit (`HEAD` if not specified), incompatible with `--no-merged`.
|
commit (`HEAD` if not specified), incompatible with `--no-merged`.
|
||||||
|
|
|
@ -548,7 +548,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"),
|
OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"),
|
||||||
FILTER_REFS_REMOTES),
|
FILTER_REFS_REMOTES),
|
||||||
OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
|
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_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__ABBREV(&filter.abbrev),
|
||||||
|
|
||||||
OPT_GROUP(N_("Specific git-branch actions:")),
|
OPT_GROUP(N_("Specific git-branch actions:")),
|
||||||
|
@ -604,7 +606,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
|
||||||
if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
|
if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
|
||||||
list = 1;
|
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;
|
list = 1;
|
||||||
|
|
||||||
if (!!delete + !!rename + !!new_upstream +
|
if (!!delete + !!rename + !!new_upstream +
|
||||||
|
|
|
@ -9,7 +9,7 @@ static char const * const for_each_ref_usage[] = {
|
||||||
N_("git for-each-ref [<options>] [<pattern>]"),
|
N_("git for-each-ref [<options>] [<pattern>]"),
|
||||||
N_("git for-each-ref [--points-at <object>]"),
|
N_("git for-each-ref [--points-at <object>]"),
|
||||||
N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"),
|
N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"),
|
||||||
N_("git for-each-ref [--contains [<commit>]]"),
|
N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
|
||||||
NULL
|
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_MERGED(&filter, N_("print only refs that are merged")),
|
||||||
OPT_NO_MERGED(&filter, N_("print only refs that are not 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_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_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
static const char * const git_tag_usage[] = {
|
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 [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
|
||||||
N_("git tag -d <tagname>..."),
|
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\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
|
||||||
N_("git tag -v [--format=<format>] <tagname>..."),
|
N_("git tag -v [--format=<format>] <tagname>..."),
|
||||||
NULL
|
NULL
|
||||||
|
@ -424,7 +424,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||||
OPT_GROUP(N_("Tag listing options")),
|
OPT_GROUP(N_("Tag listing options")),
|
||||||
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
|
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_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_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_MERGED(&filter, N_("print only tags that are merged")),
|
||||||
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
|
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
|
||||||
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
|
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
|
||||||
|
@ -458,7 +460,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||||
if (!cmdmode) {
|
if (!cmdmode) {
|
||||||
if (argc == 0)
|
if (argc == 0)
|
||||||
cmdmode = 'l';
|
cmdmode = 'l';
|
||||||
else if (filter.with_commit ||
|
else if (filter.with_commit || filter.no_commit ||
|
||||||
filter.points_at.nr || filter.merge_commit ||
|
filter.points_at.nr || filter.merge_commit ||
|
||||||
filter.lines != -1)
|
filter.lines != -1)
|
||||||
cmdmode = 'l';
|
cmdmode = 'l';
|
||||||
|
@ -495,6 +497,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||||
die(_("-n option is only allowed in list mode"));
|
die(_("-n option is only allowed in list mode"));
|
||||||
if (filter.with_commit)
|
if (filter.with_commit)
|
||||||
die(_("--contains option is only allowed in list mode"));
|
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)
|
if (filter.points_at.nr)
|
||||||
die(_("--points-at option is only allowed in list mode"));
|
die(_("--points-at option is only allowed in list mode"));
|
||||||
if (filter.merge_commit)
|
if (filter.merge_commit)
|
||||||
|
|
|
@ -1093,7 +1093,7 @@ _git_branch ()
|
||||||
--*)
|
--*)
|
||||||
__gitcomp "
|
__gitcomp "
|
||||||
--color --no-color --verbose --abbrev= --no-abbrev
|
--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
|
--set-upstream-to= --edit-description --list
|
||||||
--unset-upstream --delete --move --remotes
|
--unset-upstream --delete --move --remotes
|
||||||
--column --no-column --sort= --points-at
|
--column --no-column --sort= --points-at
|
||||||
|
@ -2862,7 +2862,7 @@ _git_tag ()
|
||||||
__gitcomp "
|
__gitcomp "
|
||||||
--list --delete --verify --annotate --message --file
|
--list --delete --verify --annotate --message --file
|
||||||
--sign --cleanup --local-user --force --column --sort=
|
--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
|
esac
|
||||||
|
|
|
@ -259,6 +259,8 @@ extern int parse_opt_passthru_argv(const struct option *, const char *, int);
|
||||||
parse_opt_commits, (intptr_t) "HEAD" \
|
parse_opt_commits, (intptr_t) "HEAD" \
|
||||||
}
|
}
|
||||||
#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG)
|
#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_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
|
#endif
|
||||||
|
|
19
ref-filter.c
19
ref-filter.c
|
@ -1487,6 +1487,7 @@ struct ref_filter_cbdata {
|
||||||
struct ref_array *array;
|
struct ref_array *array;
|
||||||
struct ref_filter *filter;
|
struct ref_filter *filter;
|
||||||
struct contains_cache contains_cache;
|
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,
|
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)
|
if (filter->with_commit_tag_algo)
|
||||||
return contains_tag_algo(commit, filter->with_commit, cache) == CONTAINS_YES;
|
return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
|
||||||
return is_descendant_of(commit, filter->with_commit);
|
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
|
* obtain the commit using the 'oid' available and discard all
|
||||||
* non-commits early. The actual filtering is done later.
|
* 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);
|
commit = lookup_commit_reference_gently(oid->hash, 1);
|
||||||
if (!commit)
|
if (!commit)
|
||||||
return 0;
|
return 0;
|
||||||
/* We perform the filtering for the '--contains' option */
|
/* We perform the filtering for the '--contains' option... */
|
||||||
if (filter->with_commit &&
|
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;
|
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;
|
filter->kind = type & FILTER_REFS_KIND_MASK;
|
||||||
|
|
||||||
init_contains_cache(&ref_cbdata.contains_cache);
|
init_contains_cache(&ref_cbdata.contains_cache);
|
||||||
|
init_contains_cache(&ref_cbdata.no_contains_cache);
|
||||||
|
|
||||||
/* Simple per-ref filtering */
|
/* Simple per-ref filtering */
|
||||||
if (!filter->kind)
|
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.contains_cache);
|
||||||
|
clear_contains_cache(&ref_cbdata.no_contains_cache);
|
||||||
|
|
||||||
/* Filters that need revision walking */
|
/* Filters that need revision walking */
|
||||||
if (filter->merge_commit)
|
if (filter->merge_commit)
|
||||||
|
|
|
@ -53,6 +53,7 @@ struct ref_filter {
|
||||||
const char **name_patterns;
|
const char **name_patterns;
|
||||||
struct sha1_array points_at;
|
struct sha1_array points_at;
|
||||||
struct commit_list *with_commit;
|
struct commit_list *with_commit;
|
||||||
|
struct commit_list *no_commit;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
REF_FILTER_MERGED_NONE = 0,
|
REF_FILTER_MERGED_NONE = 0,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/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
|
. ./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' '
|
test_expect_success 'branch --contains=side' '
|
||||||
|
|
||||||
git branch --contains=side >actual &&
|
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' '
|
test_expect_success 'branch --contains with pattern implies --list' '
|
||||||
|
|
||||||
git branch --contains=master master >actual &&
|
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' '
|
test_expect_success 'side: branch --merged' '
|
||||||
|
|
||||||
git branch --merged >actual &&
|
git branch --merged >actual &&
|
||||||
|
@ -126,7 +160,9 @@ test_expect_success 'branch --no-merged with pattern implies --list' '
|
||||||
test_expect_success 'implicit --list conflicts with modification options' '
|
test_expect_success 'implicit --list conflicts with modification options' '
|
||||||
|
|
||||||
test_must_fail git branch --contains=master -d &&
|
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
|
||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
@ -136,7 +172,8 @@ test_expect_success 'Assert that --contains only works on commits, not trees & b
|
||||||
Some blob
|
Some blob
|
||||||
EOF
|
EOF
|
||||||
) &&
|
) &&
|
||||||
test_must_fail git branch --contains $blob
|
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
|
# We want to set up a case where the walk for the tracking info
|
||||||
|
@ -168,4 +205,15 @@ test_expect_success 'branch --merged with --verbose' '
|
||||||
test_i18ncmp expect actual
|
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
|
test_done
|
||||||
|
|
|
@ -93,6 +93,22 @@ test_expect_success 'filtering with --contains' '
|
||||||
test_cmp expect actual
|
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_expect_success '%(color) must fail' '
|
||||||
test_must_fail git for-each-ref --format="%(color)%(refname)"
|
test_must_fail git for-each-ref --format="%(color)%(refname)"
|
||||||
'
|
'
|
||||||
|
|
130
t/t7004-tag.sh
130
t/t7004-tag.sh
|
@ -1424,6 +1424,23 @@ test_expect_success 'checking that first commit is in all tags (relative)' "
|
||||||
test_cmp expected actual
|
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
|
cat > expected <<EOF
|
||||||
v2.0
|
v2.0
|
||||||
EOF
|
EOF
|
||||||
|
@ -1433,6 +1450,17 @@ test_expect_success 'checking that second commit only has one tag' "
|
||||||
test_cmp expected actual
|
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
|
cat > expected <<EOF
|
||||||
EOF
|
EOF
|
||||||
|
@ -1442,6 +1470,19 @@ test_expect_success 'checking that third commit has no tags' "
|
||||||
test_cmp expected actual
|
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?
|
# how about a simple merge?
|
||||||
|
|
||||||
test_expect_success 'creating simple branch' '
|
test_expect_success 'creating simple branch' '
|
||||||
|
@ -1463,6 +1504,19 @@ test_expect_success 'checking that branch head only has one tag' "
|
||||||
test_cmp expected actual
|
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' '
|
test_expect_success 'merging original branch into this branch' '
|
||||||
git merge --strategy=ours master &&
|
git merge --strategy=ours master &&
|
||||||
git tag v4.0
|
git tag v4.0
|
||||||
|
@ -1477,6 +1531,20 @@ test_expect_success 'checking that original branch head has one tag now' "
|
||||||
test_cmp expected actual
|
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
|
cat > expected <<EOF
|
||||||
v0.2.1
|
v0.2.1
|
||||||
v1.0
|
v1.0
|
||||||
|
@ -1497,6 +1565,12 @@ test_expect_success 'checking that --contains can be used in non-list mode' '
|
||||||
test_cmp expected 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:
|
# mixing modes and options:
|
||||||
|
|
||||||
test_expect_success 'mixing incompatibles modes and options is forbidden' '
|
test_expect_success 'mixing incompatibles modes and options is forbidden' '
|
||||||
|
@ -1522,10 +1596,13 @@ test_expect_success 'mixing incompatibles modes and options is forbidden' '
|
||||||
test_must_fail git tag -l -F some file &&
|
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-tree &&
|
||||||
test_must_fail git tag --contains tag-blob
|
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
|
||||||
'
|
'
|
||||||
|
|
||||||
for option in --contains --merged --no-merged --points-at
|
for option in --contains --no-contains --merged --no-merged --points-at
|
||||||
do
|
do
|
||||||
test_expect_success "mixing incompatible modes with $option is forbidden" "
|
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 &&
|
||||||
|
@ -1792,7 +1869,7 @@ run_with_limited_stack () {
|
||||||
test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
|
test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
|
||||||
|
|
||||||
# we require ulimit, this excludes Windows
|
# 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 &&
|
>expect &&
|
||||||
i=1 &&
|
i=1 &&
|
||||||
while test $i -lt 8000
|
while test $i -lt 8000
|
||||||
|
@ -1808,7 +1885,9 @@ EOF"
|
||||||
git checkout master &&
|
git checkout master &&
|
||||||
git tag far-far-away HEAD^ &&
|
git tag far-far-away HEAD^ &&
|
||||||
run_with_limited_stack git tag --contains HEAD >actual &&
|
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' '
|
test_expect_success '--format should list tags as per format given' '
|
||||||
|
@ -1870,4 +1949,47 @@ test_expect_success 'ambiguous branch/tags not marked' '
|
||||||
test_cmp expect actual
|
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
|
test_done
|
||||||
|
|
Loading…
Reference in a new issue