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  }