git: remove static Config, keep Version() static

This commit is contained in:
rick olson 2017-10-25 20:23:43 -06:00
parent 7d003a17c6
commit e807aa3192
11 changed files with 115 additions and 109 deletions

@ -23,7 +23,7 @@ var (
func cloneCommand(cmd *cobra.Command, args []string) { func cloneCommand(cmd *cobra.Command, args []string) {
requireGitVersion() requireGitVersion()
if cfg.IsGitVersionAtLeast("2.15.0") { if git.IsGitVersionAtLeast("2.15.0") {
msg := []string{ msg := []string{
"WARNING: 'git lfs clone' is deprecated and will not be updated", "WARNING: 'git lfs clone' is deprecated and will not be updated",
" with new flags from 'git clone'", " with new flags from 'git clone'",
@ -110,7 +110,7 @@ func cloneCommand(cmd *cobra.Command, args []string) {
func postCloneSubmodules(args []string) error { func postCloneSubmodules(args []string) error {
// In git 2.9+ the filter option will have been passed through to submodules // In git 2.9+ the filter option will have been passed through to submodules
// So we need to lfs pull inside each // So we need to lfs pull inside each
if !cfg.IsGitVersionAtLeast("2.9.0") { if !git.IsGitVersionAtLeast("2.9.0") {
// In earlier versions submodules would have used smudge filter // In earlier versions submodules would have used smudge filter
return nil return nil
} }

@ -2,6 +2,7 @@ package commands
import ( import (
"github.com/git-lfs/git-lfs/config" "github.com/git-lfs/git-lfs/config"
"github.com/git-lfs/git-lfs/git"
"github.com/git-lfs/git-lfs/lfs" "github.com/git-lfs/git-lfs/lfs"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -10,7 +11,7 @@ func envCommand(cmd *cobra.Command, args []string) {
config.ShowConfigWarnings = true config.ShowConfigWarnings = true
endpoint := getAPIClient().Endpoints.Endpoint("download", cfg.CurrentRemote) endpoint := getAPIClient().Endpoints.Endpoint("download", cfg.CurrentRemote)
gitV, err := cfg.GitVersion() gitV, err := git.Version()
if err != nil { if err != nil {
gitV = "Error getting git version: " + err.Error() gitV = "Error getting git version: " + err.Error()
} }

@ -271,7 +271,7 @@ func fetchAndReportToChan(allpointers []*lfs.WrappedPointer, filter *filepathfil
// Lazily initialize the current remote. // Lazily initialize the current remote.
if len(cfg.CurrentRemote) == 0 { if len(cfg.CurrentRemote) == 0 {
// Actively find the default remote, don't just assume origin // Actively find the default remote, don't just assume origin
defaultRemote, err := git.DefaultRemote() defaultRemote, err := cfg.GitConfig().DefaultRemote()
if err != nil { if err != nil {
Exit("No default remote") Exit("No default remote")
} }

@ -27,7 +27,7 @@ func pullCommand(cmd *cobra.Command, args []string) {
remote = args[0] remote = args[0]
} else { } else {
// Actively find the default remote, don't just assume origin // Actively find the default remote, don't just assume origin
defaultRemote, err := git.DefaultRemote() defaultRemote, err := cfg.GitConfig().DefaultRemote()
if err != nil { if err != nil {
Panic(err, "No default remote") Panic(err, "No default remote")
} }

@ -210,7 +210,7 @@ func statusScanRefRange(ref *git.Ref) {
Print("On branch %s", ref.Name) Print("On branch %s", ref.Name)
remoteRef, err := git.CurrentRemoteRef() remoteRef, err := cfg.GitConfig().CurrentRemoteRef()
if err != nil { if err != nil {
return return
} }

@ -16,6 +16,7 @@ import (
"github.com/git-lfs/git-lfs/config" "github.com/git-lfs/git-lfs/config"
"github.com/git-lfs/git-lfs/errors" "github.com/git-lfs/git-lfs/errors"
"github.com/git-lfs/git-lfs/filepathfilter" "github.com/git-lfs/git-lfs/filepathfilter"
"github.com/git-lfs/git-lfs/git"
"github.com/git-lfs/git-lfs/lfs" "github.com/git-lfs/git-lfs/lfs"
"github.com/git-lfs/git-lfs/lfsapi" "github.com/git-lfs/git-lfs/lfsapi"
"github.com/git-lfs/git-lfs/locking" "github.com/git-lfs/git-lfs/locking"
@ -378,7 +379,7 @@ func ipAddresses() []string {
func logPanicToWriter(w io.Writer, loggedError error, le string) { func logPanicToWriter(w io.Writer, loggedError error, le string) {
// log the version // log the version
gitV, err := cfg.GitVersion() gitV, err := git.Version()
if err != nil { if err != nil {
gitV = "Error getting git version: " + err.Error() gitV = "Error getting git version: " + err.Error()
} }
@ -445,8 +446,8 @@ func buildProgressMeter(dryRun bool) *progress.ProgressMeter {
func requireGitVersion() { func requireGitVersion() {
minimumGit := "1.8.2" minimumGit := "1.8.2"
if !cfg.IsGitVersionAtLeast(minimumGit) { if !git.IsGitVersionAtLeast(minimumGit) {
gitver, err := cfg.GitVersion() gitver, err := git.Version()
if err != nil { if err != nil {
Exit("Error getting git version: %s", err) Exit("Error getting git version: %s", err)
} }

@ -108,7 +108,7 @@ func NewFrom(v Values) *Configuration {
c := &Configuration{ c := &Configuration{
CurrentRemote: defaultRemote, CurrentRemote: defaultRemote,
Os: EnvironmentOf(mapFetcher(v.Os)), Os: EnvironmentOf(mapFetcher(v.Os)),
gitConfig: git.Config, gitConfig: git.NewConfig("", ""),
} }
c.Git = &delayedEnvironment{ c.Git = &delayedEnvironment{
callback: func() Environment { callback: func() Environment {
@ -175,7 +175,7 @@ func (c *Configuration) SetLockableFilesReadOnly() bool {
} }
func (c *Configuration) HookDir() string { func (c *Configuration) HookDir() string {
if c.gitConfig.IsGitVersionAtLeast("2.9.0") { if git.IsGitVersionAtLeast("2.9.0") {
hp, ok := c.Git.Get("core.hooksPath") hp, ok := c.Git.Get("core.hooksPath")
if ok { if ok {
return hp return hp
@ -284,14 +284,6 @@ func (c *Configuration) GitConfig() *git.Configuration {
return c.gitConfig return c.gitConfig
} }
func (c *Configuration) GitVersion() (string, error) {
return c.gitConfig.Version()
}
func (c *Configuration) IsGitVersionAtLeast(ver string) bool {
return c.gitConfig.IsGitVersionAtLeast(ver)
}
func (c *Configuration) FindGitGlobalKey(key string) string { func (c *Configuration) FindGitGlobalKey(key string) string {
return c.gitConfig.FindGlobal(key) return c.gitConfig.FindGlobal(key)
} }

@ -7,11 +7,8 @@ import (
"sync" "sync"
"github.com/git-lfs/git-lfs/subprocess" "github.com/git-lfs/git-lfs/subprocess"
"github.com/rubyist/tracerx"
) )
var Config = &Configuration{}
// Configuration can fetch or modify the current Git config and track the Git // Configuration can fetch or modify the current Git config and track the Git
// version. // version.
type Configuration struct { type Configuration struct {
@ -138,33 +135,6 @@ func (c *Configuration) Source() (*ConfigurationSource, error) {
return ParseConfigLines(out, false), nil return ParseConfigLines(out, false), nil
} }
// Version returns the git version
func (c *Configuration) Version() (string, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.version == nil {
v, err := gitSimple("version")
c.version = &v
if err != nil {
return v, err
}
}
return *c.version, nil
}
// IsVersionAtLeast returns whether the git version is the one specified or higher
// argument is plain version string separated by '.' e.g. "2.3.1" but can omit minor/patch
func (c *Configuration) IsGitVersionAtLeast(ver string) bool {
gitver, err := c.Version()
if err != nil {
tracerx.Printf("Error getting git version: %v", err)
return false
}
return IsVersionAtLeast(gitver, ver)
}
func (c *Configuration) gitConfig(args ...string) (string, error) { func (c *Configuration) gitConfig(args ...string) (string, error) {
args = append([]string{"config"}, args...) args = append([]string{"config"}, args...)
subprocess.Trace("git", args...) subprocess.Trace("git", args...)

@ -94,7 +94,7 @@ func gitConfigNoLFS(args ...string) []string {
// causes difficult issues with passing through Stdin for login prompts // causes difficult issues with passing through Stdin for login prompts
// This way is simpler & more practical. // This way is simpler & more practical.
filterOverride := "" filterOverride := ""
if !Config.IsGitVersionAtLeast("2.8.0") { if !IsGitVersionAtLeast("2.8.0") {
filterOverride = "cat" filterOverride = "cat"
} }
@ -233,8 +233,8 @@ func CurrentRef() (*Ref, error) {
return ResolveRef("HEAD") return ResolveRef("HEAD")
} }
func CurrentRemoteRef() (*Ref, error) { func (c *Configuration) CurrentRemoteRef() (*Ref, error) {
remoteref, err := RemoteRefNameForCurrentBranch() remoteref, err := c.RemoteRefNameForCurrentBranch()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -243,12 +243,12 @@ func CurrentRemoteRef() (*Ref, error) {
} }
// RemoteForCurrentBranch returns the name of the remote that the current branch is tracking // RemoteForCurrentBranch returns the name of the remote that the current branch is tracking
func RemoteForCurrentBranch() (string, error) { func (c *Configuration) RemoteForCurrentBranch() (string, error) {
ref, err := CurrentRef() ref, err := CurrentRef()
if err != nil { if err != nil {
return "", err return "", err
} }
remote := RemoteForBranch(ref.Name) remote := c.RemoteForBranch(ref.Name)
if remote == "" { if remote == "" {
return "", fmt.Errorf("remote not found for branch %q", ref.Name) return "", fmt.Errorf("remote not found for branch %q", ref.Name)
} }
@ -257,7 +257,7 @@ func RemoteForCurrentBranch() (string, error) {
// RemoteRefForCurrentBranch returns the full remote ref (refs/remotes/{remote}/{remotebranch}) // RemoteRefForCurrentBranch returns the full remote ref (refs/remotes/{remote}/{remotebranch})
// that the current branch is tracking. // that the current branch is tracking.
func RemoteRefNameForCurrentBranch() (string, error) { func (c *Configuration) RemoteRefNameForCurrentBranch() (string, error) {
ref, err := CurrentRef() ref, err := CurrentRef()
if err != nil { if err != nil {
return "", err return "", err
@ -267,26 +267,26 @@ func RemoteRefNameForCurrentBranch() (string, error) {
return "", errors.New("not on a branch") return "", errors.New("not on a branch")
} }
remote := RemoteForBranch(ref.Name) remote := c.RemoteForBranch(ref.Name)
if remote == "" { if remote == "" {
return "", fmt.Errorf("remote not found for branch %q", ref.Name) return "", fmt.Errorf("remote not found for branch %q", ref.Name)
} }
remotebranch := RemoteBranchForLocalBranch(ref.Name) remotebranch := c.RemoteBranchForLocalBranch(ref.Name)
return fmt.Sprintf("refs/remotes/%s/%s", remote, remotebranch), nil return fmt.Sprintf("refs/remotes/%s/%s", remote, remotebranch), nil
} }
// RemoteForBranch returns the remote name that a given local branch is tracking (blank if none) // RemoteForBranch returns the remote name that a given local branch is tracking (blank if none)
func RemoteForBranch(localBranch string) string { func (c *Configuration) RemoteForBranch(localBranch string) string {
return Config.Find(fmt.Sprintf("branch.%s.remote", localBranch)) return c.Find(fmt.Sprintf("branch.%s.remote", localBranch))
} }
// RemoteBranchForLocalBranch returns the name (only) of the remote branch that the local branch is tracking // RemoteBranchForLocalBranch returns the name (only) of the remote branch that the local branch is tracking
// If no specific branch is configured, returns local branch name // If no specific branch is configured, returns local branch name
func RemoteBranchForLocalBranch(localBranch string) string { func (c *Configuration) RemoteBranchForLocalBranch(localBranch string) string {
// get remote ref to track, may not be same name // get remote ref to track, may not be same name
merge := Config.Find(fmt.Sprintf("branch.%s.merge", localBranch)) merge := c.Find(fmt.Sprintf("branch.%s.merge", localBranch))
if strings.HasPrefix(merge, "refs/heads/") { if strings.HasPrefix(merge, "refs/heads/") {
return merge[11:] return merge[11:]
} else { } else {
@ -429,8 +429,8 @@ func ValidateRemoteURL(remote string) error {
// 3. Any other SINGLE remote defined in .git/config // 3. Any other SINGLE remote defined in .git/config
// Returns an error if all of these fail, i.e. no tracked remote branch, no // Returns an error if all of these fail, i.e. no tracked remote branch, no
// "origin", and either no remotes defined or 2+ non-"origin" remotes // "origin", and either no remotes defined or 2+ non-"origin" remotes
func DefaultRemote() (string, error) { func (c *Configuration) DefaultRemote() (string, error) {
tracked, err := RemoteForCurrentBranch() tracked, err := c.RemoteForCurrentBranch()
if err == nil { if err == nil {
return tracked, nil return tracked, nil
} }
@ -730,48 +730,6 @@ func parseRefFile(filename string) (*Ref, error) {
return ResolveRef(contents) return ResolveRef(contents)
} }
// IsVersionAtLeast compares 2 version strings (ok to be prefixed with 'git version', ignores)
func IsVersionAtLeast(actualVersion, desiredVersion string) bool {
// Capture 1-3 version digits, optionally prefixed with 'git version' and possibly
// with suffixes which we'll ignore (e.g. unstable builds, MinGW versions)
verregex := regexp.MustCompile(`(?:git version\s+)?(\d+)(?:.(\d+))?(?:.(\d+))?.*`)
var atleast uint64
// Support up to 1000 in major/minor/patch digits
const majorscale = 1000 * 1000
const minorscale = 1000
if match := verregex.FindStringSubmatch(desiredVersion); match != nil {
// Ignore errors as regex won't match anything other than digits
major, _ := strconv.Atoi(match[1])
atleast += uint64(major * majorscale)
if len(match) > 2 {
minor, _ := strconv.Atoi(match[2])
atleast += uint64(minor * minorscale)
}
if len(match) > 3 {
patch, _ := strconv.Atoi(match[3])
atleast += uint64(patch)
}
}
var actual uint64
if match := verregex.FindStringSubmatch(actualVersion); match != nil {
major, _ := strconv.Atoi(match[1])
actual += uint64(major * majorscale)
if len(match) > 2 {
minor, _ := strconv.Atoi(match[2])
actual += uint64(minor * minorscale)
}
if len(match) > 3 {
patch, _ := strconv.Atoi(match[3])
actual += uint64(patch)
}
}
return actual >= atleast
}
// IsBare returns whether or not a repository is bare. It requires that the // IsBare returns whether or not a repository is bare. It requires that the
// current working directory is a repository. // current working directory is a repository.
// //

@ -48,8 +48,11 @@ func TestCurrentRefAndCurrentRemoteRef(t *testing.T) {
}, },
}, },
} }
outputs := repo.AddCommits(inputs) outputs := repo.AddCommits(inputs)
// last commit was on branch3 // last commit was on branch3
gitConf := repo.GitConfig()
ref, err := CurrentRef() ref, err := CurrentRef()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, &Ref{"branch3", RefTypeLocalBranch, outputs[3].Sha}, ref) assert.Equal(t, &Ref{"branch3", RefTypeLocalBranch, outputs[3].Sha}, ref)
@ -60,15 +63,15 @@ func TestCurrentRefAndCurrentRemoteRef(t *testing.T) {
// Check remote // Check remote
repo.AddRemote("origin") repo.AddRemote("origin")
test.RunGitCommand(t, true, "push", "-u", "origin", "master:someremotebranch") test.RunGitCommand(t, true, "push", "-u", "origin", "master:someremotebranch")
ref, err = CurrentRemoteRef() ref, err = gitConf.CurrentRemoteRef()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, &Ref{"origin/someremotebranch", RefTypeRemoteBranch, outputs[2].Sha}, ref) assert.Equal(t, &Ref{"origin/someremotebranch", RefTypeRemoteBranch, outputs[2].Sha}, ref)
refname, err := RemoteRefNameForCurrentBranch() refname, err := gitConf.RemoteRefNameForCurrentBranch()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "refs/remotes/origin/someremotebranch", refname) assert.Equal(t, "refs/remotes/origin/someremotebranch", refname)
remote, err := RemoteForCurrentBranch() remote, err := gitConf.RemoteForCurrentBranch()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "origin", remote) assert.Equal(t, "origin", remote)
@ -201,7 +204,7 @@ func TestResolveEmptyCurrentRef(t *testing.T) {
func TestWorkTrees(t *testing.T) { func TestWorkTrees(t *testing.T) {
// Only git 2.5+ // Only git 2.5+
if !Config.IsGitVersionAtLeast("2.5.0") { if !IsGitVersionAtLeast("2.5.0") {
return return
} }

81
git/version.go Normal file

@ -0,0 +1,81 @@
package git
import (
"regexp"
"strconv"
"sync"
"github.com/git-lfs/git-lfs/subprocess"
"github.com/rubyist/tracerx"
)
var (
gitVersion *string
gitVersionMu sync.Mutex
)
func Version() (string, error) {
gitVersionMu.Lock()
defer gitVersionMu.Unlock()
if gitVersion == nil {
v, err := subprocess.SimpleExec("git", "version")
gitVersion = &v
return v, err
}
return *gitVersion, nil
}
// IsVersionAtLeast returns whether the git version is the one specified or higher
// argument is plain version string separated by '.' e.g. "2.3.1" but can omit minor/patch
func IsGitVersionAtLeast(ver string) bool {
gitver, err := Version()
if err != nil {
tracerx.Printf("Error getting git version: %v", err)
return false
}
return IsVersionAtLeast(gitver, ver)
}
// IsVersionAtLeast compares 2 version strings (ok to be prefixed with 'git version', ignores)
func IsVersionAtLeast(actualVersion, desiredVersion string) bool {
// Capture 1-3 version digits, optionally prefixed with 'git version' and possibly
// with suffixes which we'll ignore (e.g. unstable builds, MinGW versions)
verregex := regexp.MustCompile(`(?:git version\s+)?(\d+)(?:.(\d+))?(?:.(\d+))?.*`)
var atleast uint64
// Support up to 1000 in major/minor/patch digits
const majorscale = 1000 * 1000
const minorscale = 1000
if match := verregex.FindStringSubmatch(desiredVersion); match != nil {
// Ignore errors as regex won't match anything other than digits
major, _ := strconv.Atoi(match[1])
atleast += uint64(major * majorscale)
if len(match) > 2 {
minor, _ := strconv.Atoi(match[2])
atleast += uint64(minor * minorscale)
}
if len(match) > 3 {
patch, _ := strconv.Atoi(match[3])
atleast += uint64(patch)
}
}
var actual uint64
if match := verregex.FindStringSubmatch(actualVersion); match != nil {
major, _ := strconv.Atoi(match[1])
actual += uint64(major * majorscale)
if len(match) > 2 {
minor, _ := strconv.Atoi(match[2])
actual += uint64(minor * minorscale)
}
if len(match) > 3 {
patch, _ := strconv.Atoi(match[3])
actual += uint64(patch)
}
}
return actual >= atleast
}