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 <p...@peff.net>
Mentored-by: Christian Couder <christian.cou...@gmail.com>
Mentored-by: Matthieu Moy <matthieu....@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik....@gmail.com>
---
 ref-filter.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 ref-filter.h |  8 +++++++
 2 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 6502179..06328eb 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -9,6 +9,7 @@
 #include "tag.h"
 #include "quote.h"
 #include "ref-filter.h"
+#include "revision.h"
 
 typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
 
@@ -889,6 +890,7 @@ static int ref_filter_handler(const char *refname, const 
struct object_id *oid,
        struct ref_filter_cbdata *ref_cbdata = cb_data;
        struct ref_filter *filter = ref_cbdata->filter;
        struct ref_array_item *ref;
+       struct commit *commit = NULL;
 
        if (flag & REF_BAD_NAME) {
                  warning("ignoring ref with broken name %s", refname);
@@ -901,12 +903,19 @@ static int ref_filter_handler(const char *refname, const 
struct object_id *oid,
        if (!match_points_at(&filter->points_at, oid->hash, refname))
                return 0;
 
+       if (filter->merge_commit) {
+               commit = lookup_commit_reference_gently(oid->hash, 1);
+               if (!commit)
+                       return 0;
+       }
+
        /*
         * We do not open the object yet; sort may only need refname
         * to do its job and the resulting list may yet to be pruned
         * by maxcount logic.
         */
        ref = new_ref_array_item(refname, oid->hash, flag);
+       ref->commit = commit;
 
        REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
        ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
@@ -932,6 +941,50 @@ void ref_array_clear(struct ref_array *array)
        array->nr = array->alloc = 0;
 }
 
+static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
+{
+       struct rev_info revs;
+       int i, old_nr;
+       struct ref_filter *filter = ref_cbdata->filter;
+       struct ref_array *array = ref_cbdata->array;
+       struct commit_list *p, *to_clear = NULL;
+
+       init_revisions(&revs, NULL);
+
+       for (i = 0; i < array->nr; i++) {
+               struct ref_array_item *item = array->items[i];
+               add_pending_object(&revs, &item->commit->object, item->refname);
+               commit_list_insert(item->commit, &to_clear);
+       }
+
+       filter->merge_commit->object.flags |= UNINTERESTING;
+       add_pending_object(&revs, &filter->merge_commit->object, "");
+
+       revs.limited = 1;
+       if (prepare_revision_walk(&revs))
+               die(_("revision walk setup failed"));
+
+       old_nr = array->nr;
+       array->nr = 0;
+
+       for (i = 0; i < old_nr; i++) {
+               struct ref_array_item *item = array->items[i];
+               struct commit *commit = item->commit;
+
+               int is_merged = !!(commit->object.flags & UNINTERESTING);
+
+               if (is_merged == (filter->merge == REF_FILTER_MERGED_INCLUDE))
+                       array->items[array->nr++] = array->items[i];
+               else
+                       free_array_item(item);
+       }
+
+       for (p = to_clear; p; p = p->next)
+               clear_commit_marks(p->item, ALL_REV_FLAGS);
+       clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);
+       free_commit_list(to_clear);
+}
+
 /*
  * API for filtering a set of refs. Based on the type of refs the user
  * has requested, we iterate through those refs and apply filters
@@ -941,17 +994,24 @@ void ref_array_clear(struct ref_array *array)
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned 
int type)
 {
        struct ref_filter_cbdata ref_cbdata;
+       int ret = 0;
 
        ref_cbdata.array = array;
        ref_cbdata.filter = filter;
 
+       /*  Simple per-ref filtering */
        if (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))
-               return for_each_rawref(ref_filter_handler, &ref_cbdata);
+               ret = for_each_rawref(ref_filter_handler, &ref_cbdata);
        else if (type & FILTER_REFS_ALL)
-               return for_each_ref(ref_filter_handler, &ref_cbdata);
-       else
+               ret = for_each_ref(ref_filter_handler, &ref_cbdata);
+       else if (type)
                die("filter_refs: invalid type");
-       return 0;
+
+       /*  Filters that need revision walking */
+       if (filter->merge_commit)
+               do_merge_filter(&ref_cbdata);
+
+       return ret;
 }
 
 static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, 
struct ref_array_item *b)
diff --git a/ref-filter.h b/ref-filter.h
index 799e118..39d66b0 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -31,6 +31,7 @@ struct ref_array_item {
        unsigned char objectname[20];
        int flag;
        const char *symref;
+       struct commit *commit;
        struct atom_value *value;
        char refname[FLEX_ARRAY];
 };
@@ -43,6 +44,13 @@ struct ref_array {
 struct ref_filter {
        const char **name_patterns;
        struct sha1_array points_at;
+
+       enum {
+               REF_FILTER_MERGED_NONE = 0,
+               REF_FILTER_MERGED_INCLUDE,
+               REF_FILTER_MERGED_OMIT
+       } merge;
+       struct commit *merge_commit;
 };
 
 struct ref_filter_cbdata {
-- 
2.4.3.435.g2403634.dirty

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to