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  }