github.com/psexton/git-lfs@v2.1.1-0.20170517224304-289a18b2bc53+incompatible/lfs/attribute.go (about) 1 package lfs 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/git-lfs/git-lfs/git" 8 ) 9 10 // Attribute wraps the structure and some operations of Git's conception of an 11 // "attribute", as defined here: http://git-scm.com/docs/gitattributes. 12 type Attribute struct { 13 // The Section of an Attribute refers to the location at which all 14 // properties are relative to. For example, for a Section with the value 15 // "core", Git will produce something like: 16 // 17 // [core] 18 // autocrlf = true 19 // ... 20 Section string 21 22 // The Properties of an Attribute refer to all of the keys and values 23 // that define that Attribute. 24 Properties map[string]string 25 // Previous values of these attributes that can be automatically upgraded 26 Upgradeables map[string][]string 27 } 28 29 // InstallOptions serves as an argument to Install(). 30 type InstallOptions struct { 31 Force bool 32 Local bool 33 System bool 34 } 35 36 // Install instructs Git to set all keys and values relative to the root 37 // location of this Attribute. For any particular key/value pair, if a matching 38 // key is already set, it will be overridden if it is either a) empty, or b) the 39 // `force` argument is passed as true. If an attribute is already set to a 40 // different value than what is given, and force is false, an error will be 41 // returned immediately, and the rest of the attributes will not be set. 42 func (a *Attribute) Install(opt InstallOptions) error { 43 for k, v := range a.Properties { 44 var upgradeables []string 45 if a.Upgradeables != nil { 46 // use pre-normalised key since caller will have set up the same 47 upgradeables = a.Upgradeables[k] 48 } 49 key := a.normalizeKey(k) 50 if err := a.set(key, v, upgradeables, opt); err != nil { 51 return err 52 } 53 } 54 55 return nil 56 } 57 58 // normalizeKey makes an absolute path out of a partial relative one. For a 59 // relative path of "foo", and a root Section of "bar", "bar.foo" will be returned. 60 func (a *Attribute) normalizeKey(relative string) string { 61 return strings.Join([]string{a.Section, relative}, ".") 62 } 63 64 // set attempts to set a single key/value pair portion of this Attribute. If a 65 // matching key already exists and the value is not equal to the desired value, 66 // an error will be thrown if force is set to false. If force is true, the value 67 // will be overridden. 68 func (a *Attribute) set(key, value string, upgradeables []string, opt InstallOptions) error { 69 var currentValue string 70 if opt.Local { 71 currentValue = git.Config.FindLocal(key) 72 } else if opt.System { 73 currentValue = git.Config.FindSystem(key) 74 } else { 75 currentValue = git.Config.FindGlobal(key) 76 } 77 78 if opt.Force || shouldReset(currentValue, upgradeables) { 79 var err error 80 if opt.Local { 81 // ignore error for unset, git returns non-zero if missing 82 git.Config.UnsetLocalKey("", key) 83 _, err = git.Config.SetLocal("", key, value) 84 } else if opt.System { 85 // ignore error for unset, git returns non-zero if missing 86 git.Config.UnsetSystem(key) 87 _, err = git.Config.SetSystem(key, value) 88 } else { 89 // ignore error for unset, git returns non-zero if missing 90 git.Config.UnsetGlobal(key) 91 _, err = git.Config.SetGlobal(key, value) 92 } 93 return err 94 } else if currentValue != value { 95 return fmt.Errorf("The %s attribute should be %q but is %q", 96 key, value, currentValue) 97 } 98 99 return nil 100 } 101 102 // Uninstall removes all properties in the path of this property. 103 func (a *Attribute) Uninstall(opt InstallOptions) { 104 if opt.Local { 105 git.Config.UnsetLocalSection(a.Section) 106 } else if opt.System { 107 git.Config.UnsetSystemSection(a.Section) 108 } else { 109 git.Config.UnsetGlobalSection(a.Section) 110 } 111 } 112 113 // shouldReset determines whether or not a value is resettable given its current 114 // value on the system. If the value is empty (length = 0), then it will pass. 115 // It will also pass if it matches any upgradeable value 116 func shouldReset(value string, upgradeables []string) bool { 117 if len(value) == 0 { 118 return true 119 } 120 121 for _, u := range upgradeables { 122 if value == u { 123 return true 124 } 125 } 126 127 return false 128 }