1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
package gitattributes
import (
"os"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-git/v5/plumbing/format/config"
gioutil "github.com/go-git/go-git/v5/utils/ioutil"
)
const (
coreSection = "core"
attributesfile = "attributesfile"
gitDir = ".git"
gitattributesFile = ".gitattributes"
gitconfigFile = ".gitconfig"
systemFile = "/etc/gitconfig"
)
func ReadAttributesFile(fs billy.Filesystem, path []string, attributesFile string, allowMacro bool) ([]MatchAttribute, error) {
f, err := fs.Open(fs.Join(append(path, attributesFile)...))
if os.IsNotExist(err) {
return nil, nil
}
if err != nil {
return nil, err
}
defer gioutil.CheckClose(f, &err)
return ReadAttributes(f, path, allowMacro)
}
// ReadPatterns reads gitattributes patterns recursively through the directory
// structure. The result is in ascending order of priority (last higher).
//
// The .gitattribute file in the root directory will allow custom macro
// definitions. Custom macro definitions in other directories .gitattributes
// will return an error.
func ReadPatterns(fs billy.Filesystem, path []string) (attributes []MatchAttribute, err error) {
attributes, err = ReadAttributesFile(fs, path, gitattributesFile, true)
if err != nil {
return
}
attrs, err := walkDirectory(fs, path)
return append(attributes, attrs...), err
}
func walkDirectory(fs billy.Filesystem, root []string) (attributes []MatchAttribute, err error) {
fis, err := fs.ReadDir(fs.Join(root...))
if err != nil {
return attributes, err
}
for _, fi := range fis {
if !fi.IsDir() || fi.Name() == ".git" {
continue
}
path := append(root, fi.Name())
dirAttributes, err := ReadAttributesFile(fs, path, gitattributesFile, false)
if err != nil {
return attributes, err
}
subAttributes, err := walkDirectory(fs, path)
if err != nil {
return attributes, err
}
attributes = append(attributes, append(dirAttributes, subAttributes...)...)
}
return
}
func loadPatterns(fs billy.Filesystem, path string) ([]MatchAttribute, error) {
f, err := fs.Open(path)
if os.IsNotExist(err) {
return nil, nil
}
if err != nil {
return nil, err
}
defer gioutil.CheckClose(f, &err)
raw := config.New()
if err = config.NewDecoder(f).Decode(raw); err != nil {
return nil, nil
}
path = raw.Section(coreSection).Options.Get(attributesfile)
if path == "" {
return nil, nil
}
return ReadAttributesFile(fs, nil, path, true)
}
// LoadGlobalPatterns loads gitattributes patterns and attributes from the
// gitattributes file declared in a user's ~/.gitconfig file. If the
// ~/.gitconfig file does not exist the function will return nil. If the
// core.attributesFile property is not declared, the function will return nil.
// If the file pointed to by the core.attributesfile property does not exist,
// the function will return nil. The function assumes fs is rooted at the root
// filesystem.
func LoadGlobalPatterns(fs billy.Filesystem) (attributes []MatchAttribute, err error) {
home, err := os.UserHomeDir()
if err != nil {
return
}
return loadPatterns(fs, fs.Join(home, gitconfigFile))
}
// LoadSystemPatterns loads gitattributes patterns and attributes from the
// gitattributes file declared in a system's /etc/gitconfig file. If the
// /etc/gitconfig file does not exist the function will return nil. If the
// core.attributesfile property is not declared, the function will return nil.
// If the file pointed to by the core.attributesfile property does not exist,
// the function will return nil. The function assumes fs is rooted at the root
// filesystem.
func LoadSystemPatterns(fs billy.Filesystem) (attributes []MatchAttribute, err error) {
return loadPatterns(fs, systemFile)
}
|