99#include "tag.h"
1010#include "quote.h"
1111#include "ref-filter.h"
12+ #include "revision.h"
1213
1314typedef enum { FIELD_STR , FIELD_ULONG , FIELD_TIME } cmp_type ;
1415
@@ -898,6 +899,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
898899 struct ref_filter_cbdata * ref_cbdata = cb_data ;
899900 struct ref_filter * filter = ref_cbdata -> filter ;
900901 struct ref_array_item * ref ;
902+ struct commit * commit = NULL ;
901903
902904 if (flag & REF_BAD_NAME ) {
903905 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,
910912 if (filter -> points_at .nr && !match_points_at (& filter -> points_at , oid -> hash , refname ))
911913 return 0 ;
912914
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+
913926 /*
914927 * We do not open the object yet; sort may only need refname
915928 * to do its job and the resulting list may yet to be pruned
916929 * by maxcount logic.
917930 */
918931 ref = new_ref_array_item (refname , oid -> hash , flag );
932+ ref -> commit = commit ;
919933
920934 REALLOC_ARRAY (ref_cbdata -> array -> items , ref_cbdata -> array -> nr + 1 );
921935 ref_cbdata -> array -> items [ref_cbdata -> array -> nr ++ ] = ref ;
@@ -941,6 +955,50 @@ void ref_array_clear(struct ref_array *array)
941955 array -> nr = array -> alloc = 0 ;
942956}
943957
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+
9441002/*
9451003 * API for filtering a set of refs. Based on the type of refs the user
9461004 * has requested, we iterate through those refs and apply filters
@@ -950,17 +1008,24 @@ void ref_array_clear(struct ref_array *array)
9501008int filter_refs (struct ref_array * array , struct ref_filter * filter , unsigned int type )
9511009{
9521010 struct ref_filter_cbdata ref_cbdata ;
1011+ int ret = 0 ;
9531012
9541013 ref_cbdata .array = array ;
9551014 ref_cbdata .filter = filter ;
9561015
1016+ /* Simple per-ref filtering */
9571017 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 );
9591019 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 )
9621022 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 ;
9641029}
9651030
9661031static int cmp_ref_sorting (struct ref_sorting * s , struct ref_array_item * a , struct ref_array_item * b )
0 commit comments