Skip to content

Commit 35257aa

Browse files
KarthikNayakgitster
authored andcommitted
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 <[email protected]> Mentored-by: Christian Couder <[email protected]> Mentored-by: Matthieu Moy <[email protected]> Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5afcb90 commit 35257aa

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

builtin/branch.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,10 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru
635635
cb.pattern = pattern;
636636
cb.ret = 0;
637637
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+
*/
638642
if (merge_filter != NO_FILTER) {
639643
struct commit *filter;
640644
filter = lookup_commit_reference_gently(merge_filter_ref, 0);

ref-filter.c

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "tag.h"
1010
#include "quote.h"
1111
#include "ref-filter.h"
12+
#include "revision.h"
1213

1314
typedef 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)
9501008
int 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

9661031
static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)

ref-filter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct ref_array_item {
3131
unsigned char objectname[20];
3232
int flag;
3333
const char *symref;
34+
struct commit *commit;
3435
struct atom_value *value;
3536
char refname[FLEX_ARRAY];
3637
};
@@ -43,6 +44,13 @@ struct ref_array {
4344
struct ref_filter {
4445
const char **name_patterns;
4546
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;
4654
};
4755

4856
struct ref_filter_cbdata {

0 commit comments

Comments
 (0)