2015-08-31 00:09:28 +00:00
|
|
|
package lfs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
2016-11-15 17:01:18 +00:00
|
|
|
"github.com/git-lfs/git-lfs/git"
|
2015-08-31 00:09:28 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Attribute wraps the structure and some operations of Git's conception of an
|
|
|
|
// "attribute", as defined here: http://git-scm.com/docs/gitattributes.
|
|
|
|
type Attribute struct {
|
2015-09-02 18:57:09 +00:00
|
|
|
// The Section of an Attribute refers to the location at which all
|
|
|
|
// properties are relative to. For example, for a Section with the value
|
|
|
|
// "core", Git will produce something like:
|
2015-08-31 00:09:28 +00:00
|
|
|
//
|
|
|
|
// [core]
|
|
|
|
// autocrlf = true
|
|
|
|
// ...
|
2015-09-02 18:57:09 +00:00
|
|
|
Section string
|
2015-08-31 00:09:28 +00:00
|
|
|
|
|
|
|
// The Properties of an Attribute refer to all of the keys and values
|
|
|
|
// that define that Attribute.
|
|
|
|
Properties map[string]string
|
2016-09-01 11:29:49 +00:00
|
|
|
// Previous values of these attributes that can be automatically upgraded
|
|
|
|
Upgradeables map[string][]string
|
2015-08-31 00:09:28 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 21:19:21 +00:00
|
|
|
// FilterOptions serves as an argument to Install().
|
|
|
|
type FilterOptions struct {
|
2017-10-25 22:12:29 +00:00
|
|
|
GitConfig *git.Configuration
|
2017-10-18 21:19:21 +00:00
|
|
|
Force bool
|
|
|
|
Local bool
|
|
|
|
System bool
|
|
|
|
SkipSmudge bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *FilterOptions) Install() error {
|
|
|
|
if o.SkipSmudge {
|
|
|
|
return skipSmudgeFilterAttribute().Install(o)
|
|
|
|
}
|
|
|
|
return filterAttribute().Install(o)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *FilterOptions) Uninstall() error {
|
|
|
|
filterAttribute().Uninstall(o)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func filterAttribute() *Attribute {
|
|
|
|
return &Attribute{
|
|
|
|
Section: "filter.lfs",
|
|
|
|
Properties: map[string]string{
|
|
|
|
"clean": "git-lfs clean -- %f",
|
|
|
|
"smudge": "git-lfs smudge -- %f",
|
|
|
|
"process": "git-lfs filter-process",
|
|
|
|
"required": "true",
|
|
|
|
},
|
|
|
|
Upgradeables: upgradeables(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func skipSmudgeFilterAttribute() *Attribute {
|
|
|
|
return &Attribute{
|
|
|
|
Section: "filter.lfs",
|
|
|
|
Properties: map[string]string{
|
|
|
|
"clean": "git-lfs clean -- %f",
|
|
|
|
"smudge": "git-lfs smudge --skip -- %f",
|
|
|
|
"process": "git-lfs filter-process --skip",
|
|
|
|
"required": "true",
|
|
|
|
},
|
|
|
|
Upgradeables: upgradeables(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func upgradeables() map[string][]string {
|
|
|
|
return map[string][]string{
|
|
|
|
"clean": []string{"git-lfs clean %f"},
|
|
|
|
"smudge": []string{
|
|
|
|
"git-lfs smudge %f",
|
|
|
|
"git-lfs smudge --skip %f",
|
|
|
|
"git-lfs smudge -- %f",
|
|
|
|
"git-lfs smudge --skip -- %f",
|
|
|
|
},
|
|
|
|
"process": []string{
|
|
|
|
"git-lfs filter",
|
|
|
|
"git-lfs filter --skip",
|
|
|
|
"git-lfs filter-process",
|
|
|
|
"git-lfs filter-process --skip",
|
|
|
|
},
|
|
|
|
}
|
2015-09-23 17:58:16 +00:00
|
|
|
}
|
|
|
|
|
2015-09-02 18:57:09 +00:00
|
|
|
// Install instructs Git to set all keys and values relative to the root
|
|
|
|
// location of this Attribute. For any particular key/value pair, if a matching
|
|
|
|
// key is already set, it will be overridden if it is either a) empty, or b) the
|
2015-08-31 00:09:28 +00:00
|
|
|
// `force` argument is passed as true. If an attribute is already set to a
|
|
|
|
// different value than what is given, and force is false, an error will be
|
|
|
|
// returned immediately, and the rest of the attributes will not be set.
|
2017-10-18 21:19:21 +00:00
|
|
|
func (a *Attribute) Install(opt *FilterOptions) error {
|
2015-08-31 00:09:28 +00:00
|
|
|
for k, v := range a.Properties {
|
2016-09-01 11:29:49 +00:00
|
|
|
var upgradeables []string
|
|
|
|
if a.Upgradeables != nil {
|
|
|
|
// use pre-normalised key since caller will have set up the same
|
|
|
|
upgradeables = a.Upgradeables[k]
|
|
|
|
}
|
2015-09-02 18:57:09 +00:00
|
|
|
key := a.normalizeKey(k)
|
2017-10-25 22:12:29 +00:00
|
|
|
if err := a.set(opt.GitConfig, key, v, upgradeables, opt); err != nil {
|
2015-08-31 00:09:28 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-02 18:57:09 +00:00
|
|
|
// normalizeKey makes an absolute path out of a partial relative one. For a
|
|
|
|
// relative path of "foo", and a root Section of "bar", "bar.foo" will be returned.
|
|
|
|
func (a *Attribute) normalizeKey(relative string) string {
|
|
|
|
return strings.Join([]string{a.Section, relative}, ".")
|
2015-08-31 00:09:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// set attempts to set a single key/value pair portion of this Attribute. If a
|
|
|
|
// matching key already exists and the value is not equal to the desired value,
|
|
|
|
// an error will be thrown if force is set to false. If force is true, the value
|
|
|
|
// will be overridden.
|
2017-10-25 22:12:29 +00:00
|
|
|
func (a *Attribute) set(gitConfig *git.Configuration, key, value string, upgradeables []string, opt *FilterOptions) error {
|
2015-09-23 17:58:16 +00:00
|
|
|
var currentValue string
|
|
|
|
if opt.Local {
|
2017-10-25 22:12:29 +00:00
|
|
|
currentValue = gitConfig.FindLocal(key)
|
2016-04-26 06:26:02 +00:00
|
|
|
} else if opt.System {
|
2017-10-25 22:12:29 +00:00
|
|
|
currentValue = gitConfig.FindSystem(key)
|
2015-09-23 17:58:16 +00:00
|
|
|
} else {
|
2017-10-25 22:12:29 +00:00
|
|
|
currentValue = gitConfig.FindGlobal(key)
|
2015-09-23 17:58:16 +00:00
|
|
|
}
|
|
|
|
|
2016-09-01 11:29:49 +00:00
|
|
|
if opt.Force || shouldReset(currentValue, upgradeables) {
|
2016-04-26 23:24:00 +00:00
|
|
|
var err error
|
2015-09-23 17:58:16 +00:00
|
|
|
if opt.Local {
|
2017-10-26 01:20:35 +00:00
|
|
|
_, err = gitConfig.SetLocal(key, value)
|
2016-04-26 06:26:02 +00:00
|
|
|
} else if opt.System {
|
2017-10-25 22:12:29 +00:00
|
|
|
_, err = gitConfig.SetSystem(key, value)
|
2015-09-23 17:58:16 +00:00
|
|
|
} else {
|
2017-10-25 22:12:29 +00:00
|
|
|
_, err = gitConfig.SetGlobal(key, value)
|
2015-09-23 17:58:16 +00:00
|
|
|
}
|
2016-04-26 23:24:00 +00:00
|
|
|
return err
|
2015-08-31 00:09:28 +00:00
|
|
|
} else if currentValue != value {
|
2017-10-17 16:24:29 +00:00
|
|
|
return fmt.Errorf("The %q attribute should be %q but is %q",
|
2015-08-31 00:09:28 +00:00
|
|
|
key, value, currentValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Uninstall removes all properties in the path of this property.
|
2017-10-18 21:19:21 +00:00
|
|
|
func (a *Attribute) Uninstall(opt *FilterOptions) {
|
2017-01-24 23:33:29 +00:00
|
|
|
if opt.Local {
|
2017-10-25 22:12:29 +00:00
|
|
|
opt.GitConfig.UnsetLocalSection(a.Section)
|
2017-01-24 23:33:29 +00:00
|
|
|
} else if opt.System {
|
2017-10-25 22:12:29 +00:00
|
|
|
opt.GitConfig.UnsetSystemSection(a.Section)
|
2017-01-24 23:33:29 +00:00
|
|
|
} else {
|
2017-10-25 22:12:29 +00:00
|
|
|
opt.GitConfig.UnsetGlobalSection(a.Section)
|
2017-01-24 23:33:29 +00:00
|
|
|
}
|
2015-08-31 00:09:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// shouldReset determines whether or not a value is resettable given its current
|
|
|
|
// value on the system. If the value is empty (length = 0), then it will pass.
|
2016-09-01 11:29:49 +00:00
|
|
|
// It will also pass if it matches any upgradeable value
|
|
|
|
func shouldReset(value string, upgradeables []string) bool {
|
2015-08-31 00:09:28 +00:00
|
|
|
if len(value) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2016-09-01 11:29:49 +00:00
|
|
|
for _, u := range upgradeables {
|
|
|
|
if value == u {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
2015-08-31 00:09:28 +00:00
|
|
|
}
|