Skip to content

Commit e0ec350

Browse files
authored
Merge pull request #87 from github/ref-group-counts
Expand the use of refgroups
2 parents 9847ad9 + d508da2 commit e0ec350

14 files changed

+1293
-386
lines changed

git-sizer.go

Lines changed: 56 additions & 296 deletions
Large diffs are not rendered by default.

git/gitconfig.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type ConfigEntry struct {
1414
}
1515

1616
type Config struct {
17+
Prefix string
1718
Entries []ConfigEntry
1819
}
1920

@@ -29,7 +30,9 @@ func (repo *Repository) Config(prefix string) (*Config, error) {
2930
return nil, fmt.Errorf("reading git configuration: %w", err)
3031
}
3132

32-
var config Config
33+
config := Config{
34+
Prefix: prefix,
35+
}
3336

3437
for len(out) > 0 {
3538
keyEnd := bytes.IndexByte(out, '\n')
@@ -60,6 +63,15 @@ func (repo *Repository) Config(prefix string) (*Config, error) {
6063
return &config, nil
6164
}
6265

66+
// FullKey returns the full gitconfig key name for the relative key
67+
// name `key`.
68+
func (config *Config) FullKey(key string) string {
69+
if config.Prefix == "" {
70+
return key
71+
}
72+
return fmt.Sprintf("%s.%s", config.Prefix, key)
73+
}
74+
6375
// configKeyMatchesPrefix checks whether `key` starts with `prefix` at
6476
// a component boundary (i.e., at a '.'). If yes, it returns `true`
6577
// and the part of the key after the prefix; e.g.:

git/ref_filter.go

Lines changed: 82 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,68 +5,84 @@ import (
55
"strings"
66
)
77

8-
type ReferenceFilter func(refname string) bool
8+
type ReferenceFilter interface {
9+
Filter(refname string) bool
10+
}
911

10-
func AllReferencesFilter(_ string) bool {
11-
return true
12+
// Combiner combines two `ReferenceFilter`s into one compound one.
13+
// `f1` is allowed to be `nil`.
14+
type Combiner interface {
15+
Combine(f1, f2 ReferenceFilter) ReferenceFilter
16+
Inverted() Combiner
1217
}
1318

14-
type Polarity uint8
19+
type inverse struct {
20+
f ReferenceFilter
21+
}
1522

16-
const (
17-
Include Polarity = iota
18-
Exclude
19-
)
23+
func (f inverse) Filter(refname string) bool {
24+
return !f.f.Filter(refname)
25+
}
2026

21-
func (p Polarity) Inverted() Polarity {
22-
switch p {
23-
case Include:
24-
return Exclude
25-
case Exclude:
26-
return Include
27-
default:
28-
// This shouldn't happen:
29-
return Exclude
30-
}
27+
type intersection struct {
28+
f1, f2 ReferenceFilter
29+
}
30+
31+
func (f intersection) Filter(refname string) bool {
32+
return f.f1.Filter(refname) && f.f2.Filter(refname)
3133
}
3234

33-
// polarizedFilter is a filter that might match, in which case it
34-
// includes or excludes the reference (according to its polarity). If
35-
// it doesn't match, then it doesn't say anything about the reference.
36-
type polarizedFilter struct {
37-
polarity Polarity
38-
filter ReferenceFilter
35+
// Include is a Combiner that includes the references matched by `f2`.
36+
// If `f1` is `nil`, it is treated as including nothing.
37+
type include struct{}
38+
39+
func (_ include) Combine(f1, f2 ReferenceFilter) ReferenceFilter {
40+
if f1 == nil {
41+
return f2
42+
}
43+
return union{f1, f2}
3944
}
4045

41-
// IncludeExcludeFilter is a filter based on a bunch of
42-
// `polarizedFilter`s. The last one that matches a reference wins. If
43-
// none match, then the result is based on the polarity of the first
44-
// polarizedFilter: if it is `Include`, then return `false`; if it is
45-
// `Exclude`, then return `true`.
46-
type IncludeExcludeFilter struct {
47-
filters []polarizedFilter
46+
func (_ include) Inverted() Combiner {
47+
return Exclude
4848
}
4949

50-
func (ief *IncludeExcludeFilter) Include(f ReferenceFilter) {
51-
ief.filters = append(ief.filters, polarizedFilter{Include, f})
50+
var Include include
51+
52+
type union struct {
53+
f1, f2 ReferenceFilter
5254
}
5355

54-
func (ief *IncludeExcludeFilter) Exclude(f ReferenceFilter) {
55-
ief.filters = append(ief.filters, polarizedFilter{Exclude, f})
56+
func (f union) Filter(refname string) bool {
57+
return f.f1.Filter(refname) || f.f2.Filter(refname)
5658
}
5759

58-
func (ief *IncludeExcludeFilter) Filter(refname string) bool {
59-
for i := len(ief.filters); i > 0; i-- {
60-
f := ief.filters[i-1]
61-
if !f.filter(refname) {
62-
continue
63-
}
64-
return f.polarity == Include
60+
// Exclude is a Combiner that excludes the references matched by `f2`.
61+
// If `f1` is `nil`, it is treated as including everything.
62+
type exclude struct{}
63+
64+
func (_ exclude) Combine(f1, f2 ReferenceFilter) ReferenceFilter {
65+
if f1 == nil {
66+
return inverse{f2}
6567
}
68+
return intersection{f1, inverse{f2}}
69+
70+
}
6671

67-
return len(ief.filters) == 0 || ief.filters[0].polarity == Exclude
72+
func (_ exclude) Inverted() Combiner {
73+
return include{}
6874
}
6975

76+
var Exclude exclude
77+
78+
type allReferencesFilter struct{}
79+
80+
func (_ allReferencesFilter) Filter(_ string) bool {
81+
return true
82+
}
83+
84+
var AllReferencesFilter allReferencesFilter
85+
7086
// PrefixFilter returns a `ReferenceFilter` that matches references
7187
// whose names start with the specified `prefix`, which must match at
7288
// a component boundary. For example,
@@ -77,16 +93,23 @@ func (ief *IncludeExcludeFilter) Filter(refname string) bool {
7793
// * Prefix "refs/foo/" matches "refs/foo/bar" but not "refs/foo" or
7894
// "refs/foobar".
7995
func PrefixFilter(prefix string) ReferenceFilter {
80-
if strings.HasSuffix(prefix, "/") {
81-
return func(refname string) bool {
82-
return strings.HasPrefix(refname, prefix)
83-
}
96+
if prefix == "" {
97+
return AllReferencesFilter
8498
}
99+
return prefixFilter{prefix}
100+
}
85101

86-
return func(refname string) bool {
87-
return strings.HasPrefix(refname, prefix) &&
88-
(len(refname) == len(prefix) || refname[len(prefix)] == '/')
102+
type prefixFilter struct {
103+
prefix string
104+
}
105+
106+
func (f prefixFilter) Filter(refname string) bool {
107+
if strings.HasSuffix(f.prefix, "/") {
108+
return strings.HasPrefix(refname, f.prefix)
89109
}
110+
111+
return strings.HasPrefix(refname, f.prefix) &&
112+
(len(refname) == len(f.prefix) || refname[len(f.prefix)] == '/')
90113
}
91114

92115
// RegexpFilter returns a `ReferenceFilter` that matches references
@@ -99,7 +122,13 @@ func RegexpFilter(pattern string) (ReferenceFilter, error) {
99122
return nil, err
100123
}
101124

102-
return func(refname string) bool {
103-
return re.MatchString(refname)
104-
}, nil
125+
return regexpFilter{re}, nil
126+
}
127+
128+
type regexpFilter struct {
129+
re *regexp.Regexp
130+
}
131+
132+
func (f regexpFilter) Filter(refname string) bool {
133+
return f.re.MatchString(refname)
105134
}

git/ref_filter_test.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ func TestPrefixFilter(t *testing.T) {
3838
t.Run(
3939
fmt.Sprintf("prefix '%s', refname '%s'", p.prefix, p.refname),
4040
func(t *testing.T) {
41-
assert.Equal(t, p.expected, git.PrefixFilter(p.prefix)(p.refname))
41+
assert.Equal(
42+
t,
43+
p.expected,
44+
git.PrefixFilter(p.prefix).Filter(p.refname),
45+
)
4246
},
4347
)
4448
}
@@ -73,7 +77,11 @@ func TestRegexpFilter(t *testing.T) {
7377
t.Run(
7478
fmt.Sprintf("pattern '%s', refname '%s'", p.pattern, p.refname),
7579
func(t *testing.T) {
76-
assert.Equal(t, p.expected, regexpFilter(t, p.pattern)(p.refname))
80+
assert.Equal(
81+
t,
82+
p.expected,
83+
regexpFilter(t, p.pattern).Filter(p.refname),
84+
)
7785
},
7886
)
7987
}
@@ -82,11 +90,11 @@ func TestRegexpFilter(t *testing.T) {
8290
func TestIncludeExcludeFilter(t *testing.T) {
8391
t.Parallel()
8492

85-
var filter git.IncludeExcludeFilter
86-
filter.Include(git.PrefixFilter("refs/heads"))
87-
filter.Exclude(regexpFilter(t, "refs/heads/.*foo.*"))
88-
filter.Include(git.PrefixFilter("refs/remotes"))
89-
filter.Exclude(git.PrefixFilter("refs/remotes/foo"))
93+
var filter git.ReferenceFilter
94+
filter = git.Include.Combine(filter, git.PrefixFilter("refs/heads"))
95+
filter = git.Exclude.Combine(filter, regexpFilter(t, "refs/heads/.*foo.*"))
96+
filter = git.Include.Combine(filter, git.PrefixFilter("refs/remotes"))
97+
filter = git.Exclude.Combine(filter, git.PrefixFilter("refs/remotes/foo"))
9098

9199
for _, p := range []struct {
92100
refname string

0 commit comments

Comments
 (0)