Skip to content

Commit dc63571

Browse files
committed
filterGroupValue: handle nested groups correctly
If the user specifies a refgroup as part of what should be walked, we only want to include the references that would appear in that refgroup. This means that we have to consider its parents and maybe its children. Use a new `refGroupFilter` to implement this logic.
1 parent 91d9c08 commit dc63571

1 file changed

Lines changed: 57 additions & 1 deletion

File tree

internal/refopts/filter_group_value.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ import (
77
"github.com/github/git-sizer/sizes"
88
)
99

10+
// filterGroupValue handles `--refgroup=REFGROUP` options, which
11+
// affect the top-level filter. These are a little bit tricky, because
12+
// the references matched by a refgroup depend on its parents (because
13+
// if the parents don't allow the reference, it won't even get tested
14+
// by the regroup's own filter) and also its children (because if the
15+
// refgroup doesn't have its own filter, then it is defined to be the
16+
// union of its children). Meanwhile, when testing parents, we
17+
// shouldn't test the top-level group, because that's what we are
18+
// trying to affect.
19+
//
20+
// The filtering itself is implemented using a `refGroupFilter`, which
21+
// contains a pointer to a `refGroup` and uses it (including its
22+
// `parent` and `subgroups` to figure out what should be allowed.
1023
type filterGroupValue struct {
1124
filter *git.ReferenceFilter
1225
groups map[sizes.RefGroupSymbol]*refGroup
@@ -21,7 +34,7 @@ func (v *filterGroupValue) Set(symbolString string) error {
2134
return fmt.Errorf("refgroup '%s' is not defined", symbol)
2235
}
2336

24-
*v.filter = git.Include.Combine(*v.filter, refGroup.filter)
37+
*v.filter = git.Include.Combine(*v.filter, refGroupFilter{refGroup})
2538

2639
return nil
2740
}
@@ -37,3 +50,46 @@ func (v *filterGroupValue) String() string {
3750
func (v *filterGroupValue) Type() string {
3851
return "name"
3952
}
53+
54+
// refGroupFilter is a filter based on what would be allowed through
55+
// by a particular refGroup. This is used as part of a top-level
56+
// filter, so it ignores what the top-level filter would say.
57+
type refGroupFilter struct {
58+
refGroup *refGroup
59+
}
60+
61+
func (f refGroupFilter) Filter(refname string) bool {
62+
return refGroupPasses(f.refGroup.parent, refname) &&
63+
refGroupMatches(f.refGroup, refname)
64+
}
65+
66+
// refGroupMatches retruns true iff `rg` would allow `refname`
67+
// through, not considering its parents. If `rg` doesn't have its own
68+
// filter, this consults its children.
69+
func refGroupMatches(rg *refGroup, refname string) bool {
70+
if rg.filter != nil {
71+
return rg.filter.Filter(refname)
72+
}
73+
74+
for _, sg := range rg.subgroups {
75+
if refGroupMatches(sg, refname) {
76+
return true
77+
}
78+
}
79+
80+
return false
81+
}
82+
83+
// refGroupPasses returns true iff `rg` and the parents of `rg` (not
84+
// including the top-level group) would allow `refname` through. This
85+
// does not consider children of `rg`, which we would still need to
86+
// consult if `rg` doesn't have a filter of its own.
87+
func refGroupPasses(rg *refGroup, refname string) bool {
88+
if rg.Symbol == "" {
89+
return true
90+
}
91+
if !refGroupPasses(rg.parent, refname) {
92+
return false
93+
}
94+
return rg.filter == nil || rg.filter.Filter(refname)
95+
}

0 commit comments

Comments
 (0)