2016-05-18 10:43:42 +00:00
|
|
|
// Package config collects together all configuration settings
|
|
|
|
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
|
2016-05-13 16:38:06 +00:00
|
|
|
package config
|
2013-11-05 17:07:03 +00:00
|
|
|
|
2013-11-05 17:45:01 +00:00
|
|
|
import (
|
2016-08-08 22:48:21 +00:00
|
|
|
"errors"
|
2014-05-22 23:02:56 +00:00
|
|
|
"fmt"
|
2016-08-08 22:48:21 +00:00
|
|
|
"reflect"
|
2015-04-23 14:48:44 +00:00
|
|
|
"strconv"
|
2015-04-23 01:07:52 +00:00
|
|
|
"sync"
|
2015-05-13 19:43:41 +00:00
|
|
|
|
2016-12-19 18:45:22 +00:00
|
|
|
"github.com/git-lfs/git-lfs/lfsapi"
|
2016-11-15 17:01:18 +00:00
|
|
|
"github.com/git-lfs/git-lfs/tools"
|
2013-11-05 17:45:01 +00:00
|
|
|
)
|
|
|
|
|
2015-06-17 20:38:09 +00:00
|
|
|
var (
|
2016-07-21 23:38:44 +00:00
|
|
|
Config = New()
|
2015-11-24 18:58:52 +00:00
|
|
|
ShowConfigWarnings = false
|
|
|
|
defaultRemote = "origin"
|
|
|
|
gitConfigWarningPrefix = "lfs."
|
2015-06-17 20:38:09 +00:00
|
|
|
)
|
|
|
|
|
2015-08-04 16:46:51 +00:00
|
|
|
// FetchPruneConfig collects together the config options that control fetching and pruning
|
|
|
|
type FetchPruneConfig struct {
|
|
|
|
// The number of days prior to current date for which (local) refs other than HEAD
|
|
|
|
// will be fetched with --recent (default 7, 0 = only fetch HEAD)
|
2016-08-09 20:27:11 +00:00
|
|
|
FetchRecentRefsDays int `git:"lfs.fetchrecentrefsdays"`
|
2015-09-01 11:06:30 +00:00
|
|
|
// Makes the FetchRecentRefsDays option apply to remote refs from fetch source as well (default true)
|
2016-08-09 20:27:11 +00:00
|
|
|
FetchRecentRefsIncludeRemotes bool `git:"lfs.fetchrecentremoterefs"`
|
2015-08-04 16:46:51 +00:00
|
|
|
// number of days prior to latest commit on a ref that we'll fetch previous
|
2015-08-17 11:04:43 +00:00
|
|
|
// LFS changes too (default 0 = only fetch at ref)
|
2016-08-09 20:27:11 +00:00
|
|
|
FetchRecentCommitsDays int `git:"lfs.fetchrecentcommitsdays"`
|
2015-08-17 11:03:13 +00:00
|
|
|
// Whether to always fetch recent even without --recent
|
2016-08-09 20:27:11 +00:00
|
|
|
FetchRecentAlways bool `git:"lfs.fetchrecentalways"`
|
2015-08-04 16:46:51 +00:00
|
|
|
// Number of days added to FetchRecent*; data outside combined window will be
|
|
|
|
// deleted when prune is run. (default 3)
|
2016-08-09 20:27:11 +00:00
|
|
|
PruneOffsetDays int `git:"lfs.pruneoffsetdays"`
|
2015-09-08 14:00:28 +00:00
|
|
|
// Always verify with remote before pruning
|
2016-08-09 20:27:11 +00:00
|
|
|
PruneVerifyRemoteAlways bool `git:"lfs.pruneverifyremotealways"`
|
2015-09-08 14:00:28 +00:00
|
|
|
// Name of remote to check for unpushed and verify checks
|
2016-08-09 20:27:11 +00:00
|
|
|
PruneRemoteName string `git:"lfs.pruneremotetocheck"`
|
2015-08-04 16:46:51 +00:00
|
|
|
}
|
|
|
|
|
2013-11-05 17:07:03 +00:00
|
|
|
type Configuration struct {
|
2016-08-04 20:41:04 +00:00
|
|
|
// Os provides a `*Environment` used to access to the system's
|
2016-08-03 22:07:05 +00:00
|
|
|
// environment through os.Getenv. It is the point of entry for all
|
|
|
|
// system environment configuration.
|
2016-08-15 19:17:11 +00:00
|
|
|
Os Environment
|
2016-08-03 22:07:05 +00:00
|
|
|
|
2016-08-05 20:16:29 +00:00
|
|
|
// Git provides a `*Environment` used to access to the various levels of
|
|
|
|
// `.gitconfig`'s. It is the point of entry for all Git environment
|
|
|
|
// configuration.
|
2016-08-15 19:17:11 +00:00
|
|
|
Git Environment
|
2016-08-05 23:23:56 +00:00
|
|
|
|
2017-01-09 23:31:56 +00:00
|
|
|
CurrentRemote string
|
2015-06-13 23:00:36 +00:00
|
|
|
|
2016-08-05 23:23:56 +00:00
|
|
|
loading sync.Mutex // guards initialization of gitConfig and remotes
|
|
|
|
remotes []string
|
|
|
|
extensions map[string]Extension
|
2016-12-19 18:45:22 +00:00
|
|
|
manualEndpoint *lfsapi.Endpoint
|
|
|
|
endpointFinder lfsapi.EndpointFinder
|
2016-12-14 03:15:36 +00:00
|
|
|
endpointMu sync.Mutex
|
2013-11-05 17:07:03 +00:00
|
|
|
}
|
|
|
|
|
2016-07-21 23:38:44 +00:00
|
|
|
func New() *Configuration {
|
2016-12-14 03:15:36 +00:00
|
|
|
c := &Configuration{Os: EnvironmentOf(NewOsFetcher())}
|
2016-08-15 20:48:43 +00:00
|
|
|
c.Git = &gitEnvironment{config: c}
|
2016-12-14 03:15:36 +00:00
|
|
|
initConfig(c)
|
2015-03-05 19:59:52 +00:00
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2016-08-03 23:42:21 +00:00
|
|
|
// Values is a convenience type used to call the NewFromValues function. It
|
|
|
|
// specifies `Git` and `Env` maps to use as mock values, instead of calling out
|
|
|
|
// to real `.gitconfig`s and the `os.Getenv` function.
|
|
|
|
type Values struct {
|
2016-08-04 20:41:04 +00:00
|
|
|
// Git and Os are the stand-in maps used to provide values for their
|
2016-08-03 23:42:21 +00:00
|
|
|
// respective environments.
|
2017-04-12 21:29:11 +00:00
|
|
|
Git, Os map[string][]string
|
2016-08-03 23:42:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewFrom returns a new `*config.Configuration` that reads both its Git
|
|
|
|
// and Enviornment-level values from the ones provided instead of the actual
|
|
|
|
// `.gitconfig` file or `os.Getenv`, respectively.
|
2016-05-31 16:38:02 +00:00
|
|
|
//
|
2016-08-03 23:42:21 +00:00
|
|
|
// This method should only be used during testing.
|
|
|
|
func NewFrom(v Values) *Configuration {
|
2016-12-14 03:15:36 +00:00
|
|
|
c := &Configuration{
|
2016-11-10 00:46:52 +00:00
|
|
|
Os: EnvironmentOf(mapFetcher(v.Os)),
|
|
|
|
Git: EnvironmentOf(mapFetcher(v.Git)),
|
2016-05-31 16:38:02 +00:00
|
|
|
}
|
2016-12-14 03:15:36 +00:00
|
|
|
initConfig(c)
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func initConfig(c *Configuration) {
|
|
|
|
c.CurrentRemote = defaultRemote
|
2016-05-31 16:38:02 +00:00
|
|
|
}
|
|
|
|
|
2016-08-08 22:48:21 +00:00
|
|
|
// Unmarshal unmarshals the *Configuration in context into all of `v`'s fields,
|
|
|
|
// according to the following rules:
|
|
|
|
//
|
|
|
|
// Values are marshaled according to the given key and environment, as follows:
|
|
|
|
// type T struct {
|
|
|
|
// Field string `git:"key"`
|
|
|
|
// Other string `os:"key"`
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// If an unknown environment is given, an error will be returned. If there is no
|
|
|
|
// method supporting conversion into a field's type, an error will be returned.
|
|
|
|
// If no value is associated with the given key and environment, the field will
|
2016-08-09 16:08:56 +00:00
|
|
|
// // only be modified if there is a config value present matching the given
|
|
|
|
// key. If the field is already set to a non-zero value of that field's type,
|
|
|
|
// then it will be left alone.
|
2016-08-08 22:48:21 +00:00
|
|
|
//
|
|
|
|
// Otherwise, the field will be set to the value of calling the
|
|
|
|
// appropriately-typed method on the specified environment.
|
|
|
|
func (c *Configuration) Unmarshal(v interface{}) error {
|
|
|
|
into := reflect.ValueOf(v)
|
|
|
|
if into.Kind() != reflect.Ptr {
|
|
|
|
return fmt.Errorf("lfs/config: unable to parse non-pointer type of %T", v)
|
|
|
|
}
|
|
|
|
into = into.Elem()
|
|
|
|
|
|
|
|
for i := 0; i < into.Type().NumField(); i++ {
|
|
|
|
field := into.Field(i)
|
|
|
|
sfield := into.Type().Field(i)
|
|
|
|
|
|
|
|
key, env, err := c.parseTag(sfield.Tag)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if env == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var val interface{}
|
|
|
|
switch sfield.Type.Kind() {
|
|
|
|
case reflect.String:
|
2016-08-09 16:08:56 +00:00
|
|
|
var ok bool
|
|
|
|
|
|
|
|
val, ok = env.Get(key)
|
|
|
|
if !ok {
|
|
|
|
val = field.String()
|
|
|
|
}
|
2016-08-08 22:48:21 +00:00
|
|
|
case reflect.Int:
|
2016-08-09 16:08:56 +00:00
|
|
|
val = env.Int(key, int(field.Int()))
|
2016-08-08 22:48:21 +00:00
|
|
|
case reflect.Bool:
|
2016-08-09 16:08:56 +00:00
|
|
|
val = env.Bool(key, field.Bool())
|
2016-08-08 22:48:21 +00:00
|
|
|
default:
|
|
|
|
return fmt.Errorf(
|
|
|
|
"lfs/config: unsupported target type for field %q: %v",
|
|
|
|
sfield.Name, sfield.Type.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
if val != nil {
|
|
|
|
into.Field(i).Set(reflect.ValueOf(val))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseTag returns the key, environment, and optional error assosciated with a
|
|
|
|
// given tag. It will return the XOR of either the `git` or `os` tag. That is to
|
|
|
|
// say, a field tagged with EITHER `git` OR `os` is valid, but pone tagged with
|
|
|
|
// both is not.
|
|
|
|
//
|
|
|
|
// If neither field was found, then a nil environment will be returned.
|
2016-08-15 19:17:11 +00:00
|
|
|
func (c *Configuration) parseTag(tag reflect.StructTag) (key string, env Environment, err error) {
|
2016-08-08 22:48:21 +00:00
|
|
|
git, os := tag.Get("git"), tag.Get("os")
|
|
|
|
|
|
|
|
if len(git) != 0 && len(os) != 0 {
|
|
|
|
return "", nil, errors.New("lfs/config: ambiguous tags")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(git) != 0 {
|
|
|
|
return git, c.Git, nil
|
|
|
|
}
|
|
|
|
if len(os) != 0 {
|
|
|
|
return os, c.Os, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-01-25 15:09:55 +00:00
|
|
|
// GitRemoteUrl returns the git clone/push url for a given remote (blank if not found)
|
|
|
|
// the forpush argument is to cater for separate remote.name.pushurl settings
|
|
|
|
func (c *Configuration) GitRemoteUrl(remote string, forpush bool) string {
|
2016-12-14 04:08:05 +00:00
|
|
|
return c.endpointConfig().GitRemoteURL(remote, forpush)
|
2016-01-25 15:09:55 +00:00
|
|
|
}
|
|
|
|
|
2015-11-24 13:05:47 +00:00
|
|
|
// Manually set an Endpoint to use instead of deriving from Git config
|
2016-12-19 18:45:22 +00:00
|
|
|
func (c *Configuration) SetManualEndpoint(e lfsapi.Endpoint) {
|
2015-11-24 13:05:47 +00:00
|
|
|
c.manualEndpoint = &e
|
|
|
|
}
|
|
|
|
|
2016-12-19 18:45:22 +00:00
|
|
|
func (c *Configuration) Endpoint(operation string) lfsapi.Endpoint {
|
2015-11-24 13:05:47 +00:00
|
|
|
if c.manualEndpoint != nil {
|
|
|
|
return *c.manualEndpoint
|
|
|
|
}
|
2016-12-14 04:08:05 +00:00
|
|
|
return c.endpointConfig().Endpoint(operation, c.CurrentRemote)
|
2013-11-05 17:07:03 +00:00
|
|
|
}
|
2013-11-05 17:45:01 +00:00
|
|
|
|
2015-05-13 15:27:06 +00:00
|
|
|
func (c *Configuration) ConcurrentTransfers() int {
|
2016-01-25 15:09:55 +00:00
|
|
|
if c.NtlmAccess("download") {
|
2015-11-06 18:08:44 +00:00
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2015-04-23 14:48:44 +00:00
|
|
|
uploads := 3
|
|
|
|
|
2016-08-15 21:43:38 +00:00
|
|
|
if v, ok := c.Git.Get("lfs.concurrenttransfers"); ok {
|
2015-04-23 14:48:44 +00:00
|
|
|
n, err := strconv.Atoi(v)
|
2015-05-26 19:42:37 +00:00
|
|
|
if err == nil && n > 0 {
|
2015-04-23 14:48:44 +00:00
|
|
|
uploads = n
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return uploads
|
|
|
|
}
|
|
|
|
|
2016-07-25 18:50:58 +00:00
|
|
|
// BasicTransfersOnly returns whether to only allow "basic" HTTP transfers.
|
2016-06-10 07:43:32 +00:00
|
|
|
// Default is false, including if the lfs.basictransfersonly is invalid
|
2016-06-09 10:45:24 +00:00
|
|
|
func (c *Configuration) BasicTransfersOnly() bool {
|
2016-08-15 21:43:38 +00:00
|
|
|
return c.Git.Bool("lfs.basictransfersonly", false)
|
2016-06-09 10:45:24 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 18:50:58 +00:00
|
|
|
// TusTransfersAllowed returns whether to only use "tus.io" HTTP transfers.
|
|
|
|
// Default is false, including if the lfs.tustransfers is invalid
|
|
|
|
func (c *Configuration) TusTransfersAllowed() bool {
|
2016-08-15 21:43:38 +00:00
|
|
|
return c.Git.Bool("lfs.tustransfers", false)
|
2016-06-09 10:45:24 +00:00
|
|
|
}
|
|
|
|
|
2016-01-25 15:09:55 +00:00
|
|
|
func (c *Configuration) NtlmAccess(operation string) bool {
|
2016-12-19 18:45:22 +00:00
|
|
|
return c.Access(operation) == lfsapi.NTLMAccess
|
2015-09-01 17:24:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Access returns the access auth type.
|
2016-12-19 18:45:22 +00:00
|
|
|
func (c *Configuration) Access(operation string) lfsapi.Access {
|
2016-01-25 15:09:55 +00:00
|
|
|
return c.EndpointAccess(c.Endpoint(operation))
|
2015-09-01 21:47:47 +00:00
|
|
|
}
|
|
|
|
|
2016-12-19 18:45:22 +00:00
|
|
|
func (c *Configuration) EndpointAccess(e lfsapi.Endpoint) lfsapi.Access {
|
2016-12-19 18:55:47 +00:00
|
|
|
return c.endpointConfig().AccessFor(e.Url)
|
2015-06-23 21:27:04 +00:00
|
|
|
}
|
|
|
|
|
2016-05-31 15:48:09 +00:00
|
|
|
func (c *Configuration) FetchIncludePaths() []string {
|
2016-08-05 21:59:57 +00:00
|
|
|
patterns, _ := c.Git.Get("lfs.fetchinclude")
|
|
|
|
return tools.CleanPaths(patterns, ",")
|
2015-08-06 13:56:57 +00:00
|
|
|
}
|
2016-05-27 19:11:24 +00:00
|
|
|
|
2016-05-31 15:48:09 +00:00
|
|
|
func (c *Configuration) FetchExcludePaths() []string {
|
2016-08-05 22:30:25 +00:00
|
|
|
patterns, _ := c.Git.Get("lfs.fetchexclude")
|
2016-08-05 21:59:57 +00:00
|
|
|
return tools.CleanPaths(patterns, ",")
|
2015-08-06 13:56:57 +00:00
|
|
|
}
|
|
|
|
|
2016-12-19 18:45:22 +00:00
|
|
|
func (c *Configuration) RemoteEndpoint(remote, operation string) lfsapi.Endpoint {
|
2016-12-14 04:08:05 +00:00
|
|
|
return c.endpointConfig().RemoteEndpoint(operation, remote)
|
2015-06-13 22:55:23 +00:00
|
|
|
}
|
2014-03-19 21:21:52 +00:00
|
|
|
|
2014-02-01 20:38:29 +00:00
|
|
|
func (c *Configuration) Remotes() []string {
|
2015-03-05 19:49:15 +00:00
|
|
|
c.loadGitConfig()
|
2016-08-15 20:48:43 +00:00
|
|
|
|
2014-02-01 20:38:29 +00:00
|
|
|
return c.remotes
|
|
|
|
}
|
|
|
|
|
2016-02-02 17:42:20 +00:00
|
|
|
func (c *Configuration) GitProtocol() string {
|
2016-12-14 03:23:24 +00:00
|
|
|
return c.endpointConfig().GitProtocol()
|
2016-02-02 17:42:20 +00:00
|
|
|
}
|
|
|
|
|
2016-12-19 18:45:22 +00:00
|
|
|
func (c *Configuration) endpointConfig() lfsapi.EndpointFinder {
|
2016-12-14 03:15:36 +00:00
|
|
|
c.endpointMu.Lock()
|
|
|
|
defer c.endpointMu.Unlock()
|
2016-08-12 21:33:54 +00:00
|
|
|
|
2016-12-19 18:45:22 +00:00
|
|
|
if c.endpointFinder == nil {
|
|
|
|
c.endpointFinder = lfsapi.NewEndpointFinder(c.Git)
|
2016-02-02 17:42:20 +00:00
|
|
|
}
|
2016-08-12 21:33:54 +00:00
|
|
|
|
2016-12-19 18:45:22 +00:00
|
|
|
return c.endpointFinder
|
2016-02-02 17:42:20 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 20:54:06 +00:00
|
|
|
func (c *Configuration) Extensions() map[string]Extension {
|
|
|
|
c.loadGitConfig()
|
|
|
|
return c.extensions
|
|
|
|
}
|
|
|
|
|
2016-05-13 16:38:06 +00:00
|
|
|
// SortedExtensions gets the list of extensions ordered by Priority
|
|
|
|
func (c *Configuration) SortedExtensions() ([]Extension, error) {
|
|
|
|
return SortExtensions(c.Extensions())
|
|
|
|
}
|
|
|
|
|
2016-08-09 20:27:11 +00:00
|
|
|
func (c *Configuration) FetchPruneConfig() FetchPruneConfig {
|
|
|
|
f := &FetchPruneConfig{
|
|
|
|
FetchRecentRefsDays: 7,
|
|
|
|
FetchRecentRefsIncludeRemotes: true,
|
|
|
|
PruneOffsetDays: 3,
|
|
|
|
PruneRemoteName: "origin",
|
2015-08-04 16:46:51 +00:00
|
|
|
}
|
2016-08-05 23:23:56 +00:00
|
|
|
|
2016-08-09 20:27:11 +00:00
|
|
|
if err := c.Unmarshal(f); err != nil {
|
|
|
|
panic(err.Error())
|
|
|
|
}
|
|
|
|
return *f
|
2015-08-04 16:46:51 +00:00
|
|
|
}
|
|
|
|
|
2016-05-10 10:38:17 +00:00
|
|
|
func (c *Configuration) SkipDownloadErrors() bool {
|
2016-08-16 17:20:15 +00:00
|
|
|
return c.Os.Bool("GIT_LFS_SKIP_DOWNLOAD_ERRORS", false) || c.Git.Bool("lfs.skipdownloaderrors", false)
|
2016-05-10 10:38:17 +00:00
|
|
|
}
|
|
|
|
|
2017-01-06 09:53:14 +00:00
|
|
|
func (c *Configuration) SetLockableFilesReadOnly() bool {
|
|
|
|
return c.Os.Bool("GIT_LFS_SET_LOCKABLE_READONLY", true) && c.Git.Bool("lfs.setlockablereadonly", true)
|
|
|
|
}
|
|
|
|
|
2016-08-15 20:48:43 +00:00
|
|
|
// loadGitConfig is a temporary measure to support legacy behavior dependent on
|
|
|
|
// accessing properties set by ReadGitConfig, namely:
|
|
|
|
// - `c.extensions`
|
|
|
|
// - `c.uniqRemotes`
|
|
|
|
// - `c.gitConfig`
|
|
|
|
//
|
|
|
|
// Since the *gitEnvironment is responsible for setting these values on the
|
|
|
|
// (*config.Configuration) instance, we must call that method, if it exists.
|
|
|
|
//
|
|
|
|
// loadGitConfig returns a bool returning whether or not `loadGitConfig` was
|
|
|
|
// called AND the method did not return early.
|
2015-08-28 21:19:52 +00:00
|
|
|
func (c *Configuration) loadGitConfig() bool {
|
2016-08-15 20:48:43 +00:00
|
|
|
if g, ok := c.Git.(*gitEnvironment); ok {
|
|
|
|
return g.loadGitConfig()
|
2015-10-20 16:31:56 +00:00
|
|
|
}
|
2014-03-12 15:31:41 +00:00
|
|
|
|
2016-08-15 20:48:43 +00:00
|
|
|
return false
|
2015-10-20 16:31:56 +00:00
|
|
|
}
|
2016-12-05 11:45:24 +00:00
|
|
|
|
|
|
|
// CurrentCommitter returns the name/email that would be used to author a commit
|
|
|
|
// with this configuration. In particular, the "user.name" and "user.email"
|
|
|
|
// configuration values are used
|
|
|
|
func (c *Configuration) CurrentCommitter() (name, email string) {
|
|
|
|
name, _ = c.Git.Get("user.name")
|
|
|
|
email, _ = c.Git.Get("user.email")
|
|
|
|
return
|
|
|
|
}
|