4a992015e5
We use a sync.Mutex to synchronize access to the string pointer `gitVersion`, which indicates the version of Git used by the system running Git LFS. Our basic usage of the sync.Mutex is not incorrect, but we can improve the readability by instead using a sync.Once. A sync.Once determines very quickly (and in an atomic, goroutine-safe fashion) whether or not _any_ function has been run, and if it hasn't, run it. By doing this, we can--at the first request--produce a value for the result of running 'git version', and then return it later to the caller. This has the following benefits: - If 'git version' has already been run, we do not need to hold the lock for the entire duration of the function. - If 'git version' has not already been run, we only run it once, retaining the existing behavior. Only one change, which is the introduction of the `gitVersionErr` variable. This is a consequence of executing the 'git version' call in a closure: since we're in a new stack frame, we can't return from our parent. Instead, we retain the value for all time, and return _it_, along with whatever value we got from running 'git version' in the first place.
78 lines
2.1 KiB
Go
78 lines
2.1 KiB
Go
package git
|
|
|
|
import (
|
|
"regexp"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/git-lfs/git-lfs/subprocess"
|
|
"github.com/rubyist/tracerx"
|
|
)
|
|
|
|
var (
|
|
gitVersionOnce sync.Once
|
|
gitVersion string
|
|
gitVersionErr error
|
|
)
|
|
|
|
func Version() (string, error) {
|
|
gitVersionOnce.Do(func() {
|
|
gitVersion, gitVersionErr =
|
|
subprocess.SimpleExec("git", "version")
|
|
})
|
|
return gitVersion, gitVersionErr
|
|
}
|
|
|
|
// 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
|
|
}
|