ref-filter: implement '--merged' and '--no-merged' options
In 'branch -l' we have '--merged' option which only lists refs (branches) merged into the named commit and '--no-merged' option which only lists refs (branches) not merged into the named commit. Implement these two options in ref-filter.{c,h} so that other commands can benefit from this. Based-on-patch-by: Jeff King <peff@peff.net> Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Karthik Nayak committed
Jul 7, 2015 at 21:36 UTC
35257aa01203bae74f9fb856fb02c10c4b3836e6
3 files changed
+81
-4
builtin/branch.c
+4
@@ -635,6 +635,10 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
635
cb.pattern = pattern;
636
cb.ret = 0;
637
for_each_rawref(append_ref, &cb);
638
+ /*
639
+ * The following implementation is currently duplicated in ref-filter. It
640
+ * will eventually be removed when we port branch.c to use ref-filter APIs.
641
+ */
642
if (merge_filter != NO_FILTER) {
643
struct commit *filter;
644
filter = lookup_commit_reference_gently(merge_filter_ref, 0);
ref-filter.c
+69
-4
@@ -9,6 +9,7 @@
9
#include "tag.h"
10
#include "quote.h"
11
#include "ref-filter.h"
12
+#include "revision.h"
13
14
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
15
@@ -898,6 +899,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
899
struct ref_filter_cbdata *ref_cbdata = cb_data;
900
struct ref_filter *filter = ref_cbdata->filter;
901
struct ref_array_item *ref;
902
+ struct commit *commit = NULL;
903
904
if (flag & REF_BAD_NAME) {
905
warning("ignoring ref with broken name %s", refname);
@@ -910,12 +912,24 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
912
if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))
913
return 0;
914
915
+ /*
916
+ * A merge filter is applied on refs pointing to commits. Hence
917
+ * obtain the commit using the 'oid' available and discard all
918
+ * non-commits early. The actual filtering is done later.
919
+ */
920
+ if (filter->merge_commit) {
921
+ commit = lookup_commit_reference_gently(oid->hash, 1);
922
+ if (!commit)
923
+ return 0;
924
+ }
925
+
926
/*
927
* We do not open the object yet; sort may only need refname
928
* to do its job and the resulting list may yet to be pruned
929
* by maxcount logic.
930
*/
931
ref = new_ref_array_item(refname, oid->hash, flag);
932
+ ref->commit = commit;
933
934
REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
935
ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
@@ -941,6 +955,50 @@ void ref_array_clear(struct ref_array *array)
955
array->nr = array->alloc = 0;
956
}
957
958
+static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
959
+{
960
+ struct rev_info revs;
961
+ int i, old_nr;
962
+ struct ref_filter *filter = ref_cbdata->filter;
963
+ struct ref_array *array = ref_cbdata->array;
964
+ struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr);
965
+
966
+ init_revisions(&revs, NULL);
967
+
968
+ for (i = 0; i < array->nr; i++) {
969
+ struct ref_array_item *item = array->items[i];
970
+ add_pending_object(&revs, &item->commit->object, item->refname);
971
+ to_clear[i] = item->commit;
972
+ }
973
+
974
+ filter->merge_commit->object.flags |= UNINTERESTING;
975
+ add_pending_object(&revs, &filter->merge_commit->object, "");
976
+
977
+ revs.limited = 1;
978
+ if (prepare_revision_walk(&revs))
979
+ die(_("revision walk setup failed"));
980
+
981
+ old_nr = array->nr;
982
+ array->nr = 0;
983
+
984
+ for (i = 0; i < old_nr; i++) {
985
+ struct ref_array_item *item = array->items[i];
986
+ struct commit *commit = item->commit;
987
+
988
+ int is_merged = !!(commit->object.flags & UNINTERESTING);
989
+
990
+ if (is_merged == (filter->merge == REF_FILTER_MERGED_INCLUDE))
991
+ array->items[array->nr++] = array->items[i];
992
+ else
993
+ free_array_item(item);
994
+ }
995
+
996
+ for (i = 0; i < old_nr; i++)
997
+ clear_commit_marks(to_clear[i], ALL_REV_FLAGS);
998
+ clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);
999
+ free(to_clear);
1000
+}
1001
+
1002
/*
1003
* API for filtering a set of refs. Based on the type of refs the user
1004
* has requested, we iterate through those refs and apply filters
@@ -950,17 +1008,24 @@ void ref_array_clear(struct ref_array *array)
1008
int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type)
1009
{
1010
struct ref_filter_cbdata ref_cbdata;
1011
+ int ret = 0;
1012
1013
ref_cbdata.array = array;
1014
ref_cbdata.filter = filter;
1015
1016
+ /* Simple per-ref filtering */
1017
if (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))
958
- return for_each_rawref(ref_filter_handler, &ref_cbdata);
1018
+ ret = for_each_rawref(ref_filter_handler, &ref_cbdata);
1019
else if (type & FILTER_REFS_ALL)
960
- return for_each_ref(ref_filter_handler, &ref_cbdata);
961
- else
1020
+ ret = for_each_ref(ref_filter_handler, &ref_cbdata);
1021
+ else if (type)
1022
die("filter_refs: invalid type");
963
- return 0;
1023
+
1024
+ /* Filters that need revision walking */
1025
+ if (filter->merge_commit)
1026
+ do_merge_filter(&ref_cbdata);
1027
+
1028
+ return ret;
1029
}
1030
1031
static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
ref-filter.h
+8
@@ -31,6 +31,7 @@ struct ref_array_item {
31
unsigned char objectname[20];
32
int flag;
33
const char *symref;
34
+ struct commit *commit;
35
struct atom_value *value;
36
char refname[FLEX_ARRAY];
37
};
@@ -43,6 +44,13 @@ struct ref_array {
44
struct ref_filter {
45
const char **name_patterns;
46
struct sha1_array points_at;
47
+
48
+ enum {
49
+ REF_FILTER_MERGED_NONE = 0,
50
+ REF_FILTER_MERGED_INCLUDE,
51
+ REF_FILTER_MERGED_OMIT
52
+ } merge;
53
+ struct commit *merge_commit;
54
};
55
56
struct ref_filter_cbdata {