aboutsummaryrefslogtreecommitdiffstats
path: root/ref-filter.c
diff options
context:
space:
mode:
authorAaron Lipman <alipman88@gmail.com>2020-09-15 22:08:40 -0400
committerJunio C Hamano <gitster@pobox.com>2020-09-16 12:38:10 -0700
commit21bf933928c02372633b88aa6c4d9d71271d42b3 (patch)
treee2ad37586b8eef4cb8bee34ff495ed4d7aed0c46 /ref-filter.c
parent415af72b174802a45300039ef2bc1127d48dc0c0 (diff)
downloadgit-21bf933928c02372633b88aa6c4d9d71271d42b3.tar.gz
ref-filter: allow merged and no-merged filters
Enable ref-filter to process multiple merged and no-merged filters, and extend functionality to git branch, git tag and git for-each-ref. This provides an easy way to check for branches that are "graduation candidates:" $ git branch --no-merged master --merged next If passed more than one merged (or more than one no-merged) filter, refs must be reachable from any one of the merged commits, and reachable from none of the no-merged commits. Signed-off-by: Aaron Lipman <alipman88@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'ref-filter.c')
-rw-r--r--ref-filter.c64
1 files changed, 35 insertions, 29 deletions
diff --git a/ref-filter.c b/ref-filter.c
index 110bcd741a..785785a757 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2167,9 +2167,9 @@ 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->no_commit || filter->verbose) {
- commit = lookup_commit_reference_gently(the_repository, oid,
- 1);
+ if (filter->reachable_from || filter->unreachable_from ||
+ filter->with_commit || filter->no_commit || filter->verbose) {
+ commit = lookup_commit_reference_gently(the_repository, oid, 1);
if (!commit)
return 0;
/* We perform the filtering for the '--contains' option... */
@@ -2231,13 +2231,20 @@ void ref_array_clear(struct ref_array *array)
}
}
-static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
+static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata, int reachable)
{
struct rev_info revs;
int i, old_nr;
- struct ref_filter *filter = ref_cbdata->filter;
struct ref_array *array = ref_cbdata->array;
struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr);
+ struct commit_list *rl;
+
+ struct commit_list *check_reachable_list = reachable ?
+ ref_cbdata->filter->reachable_from :
+ ref_cbdata->filter->unreachable_from;
+
+ if (!check_reachable_list)
+ return;
repo_init_revisions(the_repository, &revs, NULL);
@@ -2247,8 +2254,11 @@ static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
to_clear[i] = item->commit;
}
- filter->merge_commit->object.flags |= UNINTERESTING;
- add_pending_object(&revs, &filter->merge_commit->object, "");
+ for (rl = check_reachable_list; rl; rl = rl->next) {
+ struct commit *merge_commit = rl->item;
+ merge_commit->object.flags |= UNINTERESTING;
+ add_pending_object(&revs, &merge_commit->object, "");
+ }
revs.limited = 1;
if (prepare_revision_walk(&revs))
@@ -2263,14 +2273,19 @@ static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
int is_merged = !!(commit->object.flags & UNINTERESTING);
- if (is_merged == (filter->merge == REF_FILTER_MERGED_INCLUDE))
+ if (is_merged == reachable)
array->items[array->nr++] = array->items[i];
else
free_array_item(item);
}
clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
- clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);
+
+ while (check_reachable_list) {
+ struct commit *merge_commit = pop_commit(&check_reachable_list);
+ clear_commit_marks(merge_commit, ALL_REV_FLAGS);
+ }
+
free(to_clear);
}
@@ -2322,8 +2337,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
clear_contains_cache(&ref_cbdata.no_contains_cache);
/* Filters that need revision walking */
- if (filter->merge_commit)
- do_merge_filter(&ref_cbdata);
+ do_merge_filter(&ref_cbdata, DO_MERGE_FILTER_REACHABLE);
+ do_merge_filter(&ref_cbdata, DO_MERGE_FILTER_UNREACHABLE);
return ret;
}
@@ -2541,31 +2556,22 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
{
struct ref_filter *rf = opt->value;
struct object_id oid;
- int no_merged = starts_with(opt->long_name, "no");
+ struct commit *merge_commit;
BUG_ON_OPT_NEG(unset);
- if (rf->merge) {
- if (no_merged) {
- return error(_("option `%s' is incompatible with --merged"),
- opt->long_name);
- } else {
- return error(_("option `%s' is incompatible with --no-merged"),
- opt->long_name);
- }
- }
-
- rf->merge = no_merged
- ? REF_FILTER_MERGED_OMIT
- : REF_FILTER_MERGED_INCLUDE;
-
if (get_oid(arg, &oid))
die(_("malformed object name %s"), arg);
- rf->merge_commit = lookup_commit_reference_gently(the_repository,
- &oid, 0);
- if (!rf->merge_commit)
+ merge_commit = lookup_commit_reference_gently(the_repository, &oid, 0);
+
+ if (!merge_commit)
return error(_("option `%s' must point to a commit"), opt->long_name);
+ if (starts_with(opt->long_name, "no"))
+ commit_list_insert(merge_commit, &rf->unreachable_from);
+ else
+ commit_list_insert(merge_commit, &rf->reachable_from);
+
return 0;
}