diff options
author | Ævar Arnfjörð Bjarmason <avarab@gmail.com> | 2017-03-24 21:40:57 +0300 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2017-03-24 22:15:26 +0300 |
commit | ac3f5a346860b824e083c5d305757c3260565475 (patch) | |
tree | b1dfdaaf848507f24dbd84a17936da3482975819 /ref-filter.c | |
parent | 1e0c3b680c6e7b37fc51bd69a410af01897a4f94 (diff) |
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 commit cf5c7253e0, the git
version to revert to can be found with this hacky two-liner:
(git tag -l 'v[0-9]*'; git tag -l --contains cf5c7253e0 '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-contains cf5c7253e0 '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>
Diffstat (limited to 'ref-filter.c')
-rw-r--r-- | ref-filter.c | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/ref-filter.c b/ref-filter.c index 81da37b5b9..71b72e04e8 100644 --- a/ref-filter.c +++ b/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) |