github.com/git-lfs/git-lfs@v2.5.2+incompatible/config/git_fetcher.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "os" 6 "strconv" 7 "strings" 8 "sync" 9 10 "github.com/git-lfs/git-lfs/git" 11 ) 12 13 type GitFetcher struct { 14 vmu sync.RWMutex 15 vals map[string][]string 16 } 17 18 func readGitConfig(configs ...*git.ConfigurationSource) (gf *GitFetcher, extensions map[string]Extension, uniqRemotes map[string]bool) { 19 vals := make(map[string][]string) 20 ignored := make([]string, 0) 21 22 extensions = make(map[string]Extension) 23 uniqRemotes = make(map[string]bool) 24 25 for _, gc := range configs { 26 uniqKeys := make(map[string]string) 27 28 for _, line := range gc.Lines { 29 pieces := strings.SplitN(line, "=", 2) 30 if len(pieces) < 2 { 31 continue 32 } 33 34 allowed := !gc.OnlySafeKeys 35 key, val := strings.ToLower(pieces[0]), pieces[1] 36 37 if origKey, ok := uniqKeys[key]; ok { 38 if ShowConfigWarnings && len(vals[key]) > 0 && vals[key][len(vals[key])-1] != val && strings.HasPrefix(key, gitConfigWarningPrefix) { 39 fmt.Fprintf(os.Stderr, "WARNING: These git config values clash:\n") 40 fmt.Fprintf(os.Stderr, " git config %q = %q\n", origKey, vals[key]) 41 fmt.Fprintf(os.Stderr, " git config %q = %q\n", pieces[0], val) 42 } 43 } else { 44 uniqKeys[key] = pieces[0] 45 } 46 47 parts := strings.Split(key, ".") 48 if len(parts) == 4 && parts[0] == "lfs" && parts[1] == "extension" { 49 // prop: lfs.extension.<name>.<prop> 50 name := parts[2] 51 prop := parts[3] 52 53 ext := extensions[name] 54 ext.Name = name 55 56 switch prop { 57 case "clean": 58 if gc.OnlySafeKeys { 59 ignored = append(ignored, key) 60 continue 61 } 62 ext.Clean = val 63 case "smudge": 64 if gc.OnlySafeKeys { 65 ignored = append(ignored, key) 66 continue 67 } 68 ext.Smudge = val 69 case "priority": 70 allowed = true 71 p, err := strconv.Atoi(val) 72 if err == nil && p >= 0 { 73 ext.Priority = p 74 } 75 } 76 77 extensions[name] = ext 78 } else if len(parts) > 1 && parts[0] == "remote" { 79 if gc.OnlySafeKeys && (len(parts) == 3 && parts[2] != "lfsurl") { 80 ignored = append(ignored, key) 81 continue 82 } 83 84 allowed = true 85 remote := parts[1] 86 uniqRemotes[remote] = remote == "origin" 87 } else if len(parts) > 2 && parts[len(parts)-1] == "access" { 88 allowed = true 89 } 90 91 if !allowed && keyIsUnsafe(key) { 92 ignored = append(ignored, key) 93 continue 94 } 95 96 vals[key] = append(vals[key], val) 97 } 98 } 99 100 if len(ignored) > 0 { 101 fmt.Fprintf(os.Stderr, "WARNING: These unsafe lfsconfig keys were ignored:\n\n") 102 for _, key := range ignored { 103 fmt.Fprintf(os.Stderr, " %s\n", key) 104 } 105 } 106 107 gf = &GitFetcher{vals: vals} 108 109 return 110 } 111 112 // Get implements the Fetcher interface, and returns the value associated with 113 // a given key and true, signaling that the value was present. Otherwise, an 114 // empty string and false will be returned, signaling that the value was 115 // absent. 116 // 117 // Map lookup by key is case-insensitive, as per the .gitconfig specification. 118 // 119 // Get is safe to call across multiple goroutines. 120 func (g *GitFetcher) Get(key string) (val string, ok bool) { 121 all := g.GetAll(key) 122 123 if len(all) == 0 { 124 return "", false 125 } 126 return all[len(all)-1], true 127 } 128 129 func (g *GitFetcher) GetAll(key string) []string { 130 g.vmu.RLock() 131 defer g.vmu.RUnlock() 132 133 return g.vals[strings.ToLower(key)] 134 } 135 136 func (g *GitFetcher) All() map[string][]string { 137 newmap := make(map[string][]string) 138 139 g.vmu.RLock() 140 defer g.vmu.RUnlock() 141 142 for key, values := range g.vals { 143 for _, value := range values { 144 newmap[key] = append(newmap[key], value) 145 } 146 } 147 148 return newmap 149 } 150 151 func keyIsUnsafe(key string) bool { 152 for _, safe := range safeKeys { 153 if safe == key { 154 return false 155 } 156 } 157 return true 158 } 159 160 var safeKeys = []string{ 161 "lfs.allowincompletepush", 162 "lfs.fetchexclude", 163 "lfs.fetchinclude", 164 "lfs.gitprotocol", 165 "lfs.locksverify", 166 "lfs.pushurl", 167 "lfs.url", 168 }