Merge pull request #2676 from git-lfs/git-config-refactor
Git config refactor
This commit is contained in:
commit
3480fa480e
@ -63,7 +63,7 @@ func checkoutCommand(cmd *cobra.Command, args []string) {
|
||||
// firstly convert any pathspecs to the root of the repo, in case this is being
|
||||
// executed in a sub-folder
|
||||
func rootedPaths(args []string) []string {
|
||||
pathConverter, err := lfs.NewCurrentToRepoPathConverter()
|
||||
pathConverter, err := lfs.NewCurrentToRepoPathConverter(cfg)
|
||||
if err != nil {
|
||||
Panic(err, "Could not checkout")
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ func clean(to io.Writer, from io.Reader, fileName string, fileSize int64) (*lfs.
|
||||
|
||||
func cleanCommand(cmd *cobra.Command, args []string) {
|
||||
requireStdin("This command should be run by the Git 'clean' filter")
|
||||
lfs.InstallHooks(false)
|
||||
installHooks(false)
|
||||
|
||||
var fileName string
|
||||
if len(args) > 0 {
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/git-lfs/git-lfs/lfs"
|
||||
"github.com/git-lfs/git-lfs/localstorage"
|
||||
"github.com/git-lfs/git-lfs/subprocess"
|
||||
|
||||
@ -25,7 +24,7 @@ var (
|
||||
func cloneCommand(cmd *cobra.Command, args []string) {
|
||||
requireGitVersion()
|
||||
|
||||
if git.Config.IsGitVersionAtLeast("2.15.0") {
|
||||
if cfg.IsGitVersionAtLeast("2.15.0") {
|
||||
msg := []string{
|
||||
"WARNING: 'git lfs clone' is deprecated and will not be updated",
|
||||
" with new flags from 'git clone'",
|
||||
@ -73,7 +72,7 @@ func cloneCommand(cmd *cobra.Command, args []string) {
|
||||
defer os.Chdir(cwd)
|
||||
|
||||
// Also need to derive dirs now
|
||||
localstorage.ResolveDirs()
|
||||
localstorage.ResolveDirs(cfg)
|
||||
requireInRepo()
|
||||
|
||||
// Now just call pull with default args
|
||||
@ -105,7 +104,7 @@ func cloneCommand(cmd *cobra.Command, args []string) {
|
||||
// If --skip-repo wasn't given, install repo-level hooks while
|
||||
// we're still in the checkout directory.
|
||||
|
||||
if err := lfs.InstallHooks(false); err != nil {
|
||||
if err := installHooks(false); err != nil {
|
||||
ExitWithError(err)
|
||||
}
|
||||
}
|
||||
@ -114,7 +113,7 @@ func cloneCommand(cmd *cobra.Command, args []string) {
|
||||
func postCloneSubmodules(args []string) error {
|
||||
// In git 2.9+ the filter option will have been passed through to submodules
|
||||
// So we need to lfs pull inside each
|
||||
if !git.Config.IsGitVersionAtLeast("2.9.0") {
|
||||
if !cfg.IsGitVersionAtLeast("2.9.0") {
|
||||
// In earlier versions submodules would have used smudge filter
|
||||
return nil
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package commands
|
||||
|
||||
import (
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/git-lfs/git-lfs/lfs"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -11,7 +10,7 @@ func envCommand(cmd *cobra.Command, args []string) {
|
||||
config.ShowConfigWarnings = true
|
||||
endpoint := getAPIClient().Endpoints.Endpoint("download", cfg.CurrentRemote)
|
||||
|
||||
gitV, err := git.Config.Version()
|
||||
gitV, err := cfg.GitVersion()
|
||||
if err != nil {
|
||||
gitV = "Error getting git version: " + err.Error()
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ func fetchCommand(cmd *cobra.Command, args []string) {
|
||||
defer gitscanner.Close()
|
||||
|
||||
include, exclude := getIncludeExcludeArgs(cmd)
|
||||
fetchPruneCfg := lfs.NewFetchPruneConfig(cfg.Git)
|
||||
|
||||
if fetchAllArg {
|
||||
if fetchRecentArg || len(args) > 1 {
|
||||
@ -89,17 +90,16 @@ func fetchCommand(cmd *cobra.Command, args []string) {
|
||||
success = success && s
|
||||
}
|
||||
|
||||
if fetchRecentArg || cfg.FetchPruneConfig().FetchRecentAlways {
|
||||
s := fetchRecent(refs, filter)
|
||||
if fetchRecentArg || fetchPruneCfg.FetchRecentAlways {
|
||||
s := fetchRecent(fetchPruneCfg, refs, filter)
|
||||
success = success && s
|
||||
}
|
||||
}
|
||||
|
||||
if fetchPruneArg {
|
||||
fetchconf := cfg.FetchPruneConfig()
|
||||
verify := fetchconf.PruneVerifyRemoteAlways
|
||||
verify := fetchPruneCfg.PruneVerifyRemoteAlways
|
||||
// no dry-run or verbose options in fetch, assume false
|
||||
prune(fetchconf, verify, false, false)
|
||||
prune(fetchPruneCfg, verify, false, false)
|
||||
}
|
||||
|
||||
if !success {
|
||||
@ -169,9 +169,7 @@ func fetchPreviousVersions(ref string, since time.Time, filter *filepathfilter.F
|
||||
}
|
||||
|
||||
// Fetch recent objects based on config
|
||||
func fetchRecent(alreadyFetchedRefs []*git.Ref, filter *filepathfilter.Filter) bool {
|
||||
fetchconf := cfg.FetchPruneConfig()
|
||||
|
||||
func fetchRecent(fetchconf lfs.FetchPruneConfig, alreadyFetchedRefs []*git.Ref, filter *filepathfilter.Filter) bool {
|
||||
if fetchconf.FetchRecentRefsDays == 0 && fetchconf.FetchRecentCommitsDays == 0 {
|
||||
return true
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ var filterSmudgeSkip bool
|
||||
|
||||
func filterCommand(cmd *cobra.Command, args []string) {
|
||||
requireStdin("This command should be run by the Git filter process")
|
||||
lfs.InstallHooks(false)
|
||||
installHooks(false)
|
||||
|
||||
s := git.NewFilterProcessScanner(os.Stdin, os.Stdout)
|
||||
|
||||
|
@ -7,9 +7,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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/localstorage"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -24,7 +24,7 @@ var (
|
||||
// NOTE(zeroshirts): Ideally git would have hooks for fsck such that we could
|
||||
// chain a lfs-fsck, but I don't think it does.
|
||||
func fsckCommand(cmd *cobra.Command, args []string) {
|
||||
lfs.InstallHooks(false)
|
||||
installHooks(false)
|
||||
requireInRepo()
|
||||
|
||||
ref, err := git.CurrentRef()
|
||||
@ -66,7 +66,7 @@ func fsckCommand(cmd *cobra.Command, args []string) {
|
||||
return
|
||||
}
|
||||
|
||||
storageConfig := config.Config.StorageConfig()
|
||||
storageConfig := localstorage.NewConfig(cfg)
|
||||
badDir := filepath.Join(storageConfig.LfsStorageDir, "bad")
|
||||
Print("Moving corrupt objects to %s", badDir)
|
||||
|
||||
|
@ -18,27 +18,21 @@ var (
|
||||
)
|
||||
|
||||
func installCommand(cmd *cobra.Command, args []string) {
|
||||
opt := cmdInstallOptions()
|
||||
if skipSmudgeInstall {
|
||||
// assume the user is changing their smudge mode, so enable force implicitly
|
||||
opt.Force = true
|
||||
}
|
||||
|
||||
if err := lfs.InstallFilters(opt, skipSmudgeInstall); err != nil {
|
||||
if err := cmdInstallOptions().Install(); err != nil {
|
||||
Print("WARNING: %s", err.Error())
|
||||
Print("Run `git lfs install --force` to reset git config.")
|
||||
return
|
||||
}
|
||||
|
||||
if !skipRepoInstall && (localInstall || lfs.InRepo()) {
|
||||
localstorage.InitStorageOrFail()
|
||||
if !skipRepoInstall && (localInstall || cfg.InRepo()) {
|
||||
localstorage.InitStorageOrFail(cfg)
|
||||
installHooksCommand(cmd, args)
|
||||
}
|
||||
|
||||
Print("Git LFS initialized.")
|
||||
}
|
||||
|
||||
func cmdInstallOptions() lfs.InstallOptions {
|
||||
func cmdInstallOptions() *lfs.FilterOptions {
|
||||
requireGitVersion()
|
||||
|
||||
if localInstall {
|
||||
@ -52,10 +46,12 @@ func cmdInstallOptions() lfs.InstallOptions {
|
||||
if systemInstall && os.Geteuid() != 0 {
|
||||
Print("WARNING: current user is not root/admin, system install is likely to fail.")
|
||||
}
|
||||
return lfs.InstallOptions{
|
||||
Force: forceInstall,
|
||||
Local: localInstall,
|
||||
System: systemInstall,
|
||||
|
||||
return &lfs.FilterOptions{
|
||||
Force: forceInstall,
|
||||
Local: localInstall,
|
||||
System: systemInstall,
|
||||
SkipSmudge: skipSmudgeInstall,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -33,7 +32,7 @@ func logsShowCommand(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
by, err := ioutil.ReadFile(filepath.Join(config.LocalLogDir, name))
|
||||
by, err := ioutil.ReadFile(filepath.Join(cfg.LocalLogDir(), name))
|
||||
if err != nil {
|
||||
Exit("Error reading log: %s", name)
|
||||
}
|
||||
@ -43,12 +42,12 @@ func logsShowCommand(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
func logsClearCommand(cmd *cobra.Command, args []string) {
|
||||
err := os.RemoveAll(config.LocalLogDir)
|
||||
err := os.RemoveAll(cfg.LocalLogDir())
|
||||
if err != nil {
|
||||
Panic(err, "Error clearing %s", config.LocalLogDir)
|
||||
Panic(err, "Error clearing %s", cfg.LocalLogDir())
|
||||
}
|
||||
|
||||
Print("Cleared %s", config.LocalLogDir)
|
||||
Print("Cleared %s", cfg.LocalLogDir())
|
||||
}
|
||||
|
||||
func logsBoomtownCommand(cmd *cobra.Command, args []string) {
|
||||
@ -59,7 +58,7 @@ func logsBoomtownCommand(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
func sortedLogs() []string {
|
||||
fileinfos, err := ioutil.ReadDir(config.LocalLogDir)
|
||||
fileinfos, err := ioutil.ReadDir(cfg.LocalLogDir())
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ func init() {
|
||||
// When lfs.TempDir is initialized to "/tmp",
|
||||
// hard-linking can fail when another filesystem is
|
||||
// mounted at "/tmp" (such as tmpfs).
|
||||
localstorage.InitStorageOrFail()
|
||||
localstorage.InitStorageOrFail(cfg)
|
||||
}
|
||||
|
||||
cmd.AddCommand(importCmd, info)
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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/localstorage"
|
||||
@ -32,7 +31,7 @@ func pruneCommand(cmd *cobra.Command, args []string) {
|
||||
Exit("Cannot specify both --verify-remote and --no-verify-remote")
|
||||
}
|
||||
|
||||
fetchPruneConfig := cfg.FetchPruneConfig()
|
||||
fetchPruneConfig := lfs.NewFetchPruneConfig(cfg.Git)
|
||||
verify := !pruneDoNotVerifyArg &&
|
||||
(fetchPruneConfig.PruneVerifyRemoteAlways || pruneVerifyArg)
|
||||
prune(fetchPruneConfig, verify, pruneDryRunArg, pruneVerboseArg)
|
||||
@ -53,7 +52,7 @@ type PruneProgress struct {
|
||||
}
|
||||
type PruneProgressChan chan PruneProgress
|
||||
|
||||
func prune(fetchPruneConfig config.FetchPruneConfig, verifyRemote, dryRun, verbose bool) {
|
||||
func prune(fetchPruneConfig lfs.FetchPruneConfig, verifyRemote, dryRun, verbose bool) {
|
||||
localObjects := make([]localstorage.Object, 0, 100)
|
||||
retainedObjects := tools.NewStringSetWithCapacity(100)
|
||||
var reachableObjects tools.StringSet
|
||||
@ -345,7 +344,7 @@ func pruneTaskGetPreviousVersionsOfRef(gitscanner *lfs.GitScanner, ref string, s
|
||||
}
|
||||
|
||||
// Background task, must call waitg.Done() once at end
|
||||
func pruneTaskGetRetainedCurrentAndRecentRefs(gitscanner *lfs.GitScanner, fetchconf config.FetchPruneConfig, retainChan chan string, errorChan chan error, waitg *sync.WaitGroup) {
|
||||
func pruneTaskGetRetainedCurrentAndRecentRefs(gitscanner *lfs.GitScanner, fetchconf lfs.FetchPruneConfig, retainChan chan string, errorChan chan error, waitg *sync.WaitGroup) {
|
||||
defer waitg.Done()
|
||||
|
||||
// We actually increment the waitg in this func since we kick off sub-goroutines
|
||||
@ -399,7 +398,7 @@ func pruneTaskGetRetainedCurrentAndRecentRefs(gitscanner *lfs.GitScanner, fetchc
|
||||
}
|
||||
|
||||
// Background task, must call waitg.Done() once at end
|
||||
func pruneTaskGetRetainedUnpushed(gitscanner *lfs.GitScanner, fetchconf config.FetchPruneConfig, retainChan chan string, errorChan chan error, waitg *sync.WaitGroup) {
|
||||
func pruneTaskGetRetainedUnpushed(gitscanner *lfs.GitScanner, fetchconf lfs.FetchPruneConfig, retainChan chan string, errorChan chan error, waitg *sync.WaitGroup) {
|
||||
defer waitg.Done()
|
||||
|
||||
err := gitscanner.ScanUnpushed(fetchconf.PruneRemoteName, func(p *lfs.WrappedPointer, err error) {
|
||||
@ -423,7 +422,7 @@ func pruneTaskGetRetainedWorktree(gitscanner *lfs.GitScanner, retainChan chan st
|
||||
|
||||
// Retain other worktree HEADs too
|
||||
// Working copy, branch & maybe commit is different but repo is shared
|
||||
allWorktreeRefs, err := git.GetAllWorkTreeHEADs(config.LocalGitStorageDir)
|
||||
allWorktreeRefs, err := git.GetAllWorkTreeHEADs(cfg.LocalGitStorageDir())
|
||||
if err != nil {
|
||||
errorChan <- err
|
||||
return
|
||||
|
@ -151,7 +151,7 @@ func smudge(to io.Writer, from io.Reader, filename string, skip bool, filter *fi
|
||||
|
||||
func smudgeCommand(cmd *cobra.Command, args []string) {
|
||||
requireStdin("This command should be run by the Git 'smudge' filter")
|
||||
lfs.InstallHooks(false)
|
||||
installHooks(false)
|
||||
|
||||
if !smudgeSkip && cfg.Os.Bool("GIT_LFS_SKIP_SMUDGE", false) {
|
||||
smudgeSkip = true
|
||||
|
@ -10,9 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/tools"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -32,18 +30,18 @@ var (
|
||||
func trackCommand(cmd *cobra.Command, args []string) {
|
||||
requireGitVersion()
|
||||
|
||||
if config.LocalGitDir == "" {
|
||||
if cfg.LocalGitDir() == "" {
|
||||
Print("Not a git repository.")
|
||||
os.Exit(128)
|
||||
}
|
||||
|
||||
if config.LocalWorkingDir == "" {
|
||||
if cfg.LocalWorkingDir() == "" {
|
||||
Print("This operation must be run in a work tree.")
|
||||
os.Exit(128)
|
||||
}
|
||||
|
||||
if !cfg.Os.Bool("GIT_LFS_TRACK_NO_INSTALL_HOOKS", false) {
|
||||
lfs.InstallHooks(false)
|
||||
installHooks(false)
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
@ -51,7 +49,7 @@ func trackCommand(cmd *cobra.Command, args []string) {
|
||||
return
|
||||
}
|
||||
|
||||
knownPatterns := git.GetAttributePaths(config.LocalWorkingDir, config.LocalGitDir)
|
||||
knownPatterns := git.GetAttributePaths(cfg.LocalWorkingDir(), cfg.LocalGitDir())
|
||||
lineEnd := getAttributeLineEnding(knownPatterns)
|
||||
if len(lineEnd) == 0 {
|
||||
lineEnd = gitLineEnding(cfg.Git)
|
||||
@ -59,9 +57,9 @@ func trackCommand(cmd *cobra.Command, args []string) {
|
||||
|
||||
wd, _ := tools.Getwd()
|
||||
wd = tools.ResolveSymlinks(wd)
|
||||
relpath, err := filepath.Rel(config.LocalWorkingDir, wd)
|
||||
relpath, err := filepath.Rel(cfg.LocalWorkingDir(), wd)
|
||||
if err != nil {
|
||||
Exit("Current directory %q outside of git working directory %q.", wd, config.LocalWorkingDir)
|
||||
Exit("Current directory %q outside of git working directory %q.", wd, cfg.LocalWorkingDir())
|
||||
}
|
||||
|
||||
changedAttribLines := make(map[string]string)
|
||||
@ -215,7 +213,7 @@ ArgsLoop:
|
||||
}
|
||||
|
||||
func listPatterns() {
|
||||
knownPatterns := git.GetAttributePaths(config.LocalWorkingDir, config.LocalGitDir)
|
||||
knownPatterns := git.GetAttributePaths(cfg.LocalWorkingDir(), cfg.LocalGitDir())
|
||||
if len(knownPatterns) < 1 {
|
||||
return
|
||||
}
|
||||
|
@ -1,20 +1,18 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/git-lfs/git-lfs/lfs"
|
||||
"github.com/git-lfs/git-lfs/localstorage"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// uninstallCmd removes any configuration and hooks set by Git LFS.
|
||||
func uninstallCommand(cmd *cobra.Command, args []string) {
|
||||
opt := cmdInstallOptions()
|
||||
if err := lfs.UninstallFilters(opt); err != nil {
|
||||
if err := cmdInstallOptions().Uninstall(); err != nil {
|
||||
Error(err.Error())
|
||||
}
|
||||
|
||||
if localInstall || lfs.InRepo() {
|
||||
localstorage.InitStorageOrFail()
|
||||
if localInstall || cfg.InRepo() {
|
||||
localstorage.InitStorageOrFail(cfg)
|
||||
uninstallHooksCommand(cmd, args)
|
||||
}
|
||||
|
||||
@ -23,7 +21,7 @@ func uninstallCommand(cmd *cobra.Command, args []string) {
|
||||
|
||||
// uninstallHooksCmd removes any hooks created by Git LFS.
|
||||
func uninstallHooksCommand(cmd *cobra.Command, args []string) {
|
||||
if err := lfs.UninstallHooks(); err != nil {
|
||||
if err := uninstallHooks(); err != nil {
|
||||
Error(err.Error())
|
||||
}
|
||||
|
||||
|
@ -6,24 +6,22 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/lfs"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// untrackCommand takes a list of paths as an argument, and removes each path from the
|
||||
// default attributes file (.gitattributes), if it exists.
|
||||
func untrackCommand(cmd *cobra.Command, args []string) {
|
||||
if config.LocalGitDir == "" {
|
||||
if cfg.LocalGitDir() == "" {
|
||||
Print("Not a git repository.")
|
||||
os.Exit(128)
|
||||
}
|
||||
if config.LocalWorkingDir == "" {
|
||||
if cfg.LocalWorkingDir() == "" {
|
||||
Print("This operation must be run in a work tree.")
|
||||
os.Exit(128)
|
||||
}
|
||||
|
||||
lfs.InstallHooks(false)
|
||||
installHooks(false)
|
||||
|
||||
if len(args) < 1 {
|
||||
Print("git lfs untrack <path> [path]*")
|
||||
|
@ -3,8 +3,6 @@ package commands
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/git-lfs/git-lfs/lfs"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -31,10 +29,10 @@ func updateCommand(cmd *cobra.Command, args []string) {
|
||||
switch value {
|
||||
case "basic":
|
||||
case "private":
|
||||
git.Config.SetLocal("", key, "basic")
|
||||
cfg.SetGitLocalKey("", key, "basic")
|
||||
Print("Updated %s access from %s to %s.", matches[1], value, "basic")
|
||||
default:
|
||||
git.Config.UnsetLocalKey("", key)
|
||||
cfg.UnsetGitLocalKey("", key)
|
||||
Print("Removed invalid %s access of %s.", matches[1], value)
|
||||
}
|
||||
}
|
||||
@ -44,9 +42,9 @@ func updateCommand(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
if updateManual {
|
||||
Print(lfs.GetHookInstallSteps())
|
||||
Print(getHookInstallSteps())
|
||||
} else {
|
||||
if err := lfs.InstallHooks(updateForce); err != nil {
|
||||
if err := installHooks(updateForce); err != nil {
|
||||
Error(err.Error())
|
||||
Exit("To resolve this, either:\n 1: run `git lfs update --manual` for instructions on how to merge hooks.\n 2: run `git lfs update --force` to overwrite your hook.")
|
||||
} else {
|
||||
|
@ -16,9 +16,9 @@ import (
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
"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/lfsapi"
|
||||
"github.com/git-lfs/git-lfs/localstorage"
|
||||
"github.com/git-lfs/git-lfs/locking"
|
||||
"github.com/git-lfs/git-lfs/progress"
|
||||
"github.com/git-lfs/git-lfs/tools"
|
||||
@ -34,11 +34,11 @@ var (
|
||||
ErrorWriter = io.MultiWriter(os.Stderr, ErrorBuffer)
|
||||
OutputWriter = io.MultiWriter(os.Stdout, ErrorBuffer)
|
||||
ManPages = make(map[string]string, 20)
|
||||
cfg = config.Config
|
||||
tqManifest = make(map[string]*tq.Manifest)
|
||||
|
||||
tqManifest = make(map[string]*tq.Manifest)
|
||||
apiClient *lfsapi.Client
|
||||
global sync.Mutex
|
||||
cfg *config.Configuration
|
||||
apiClient *lfsapi.Client
|
||||
global sync.Mutex
|
||||
|
||||
includeArg string
|
||||
excludeArg string
|
||||
@ -91,7 +91,7 @@ func closeAPIClient() error {
|
||||
}
|
||||
|
||||
func newLockClient(remote string) *locking.Client {
|
||||
storageConfig := config.Config.StorageConfig()
|
||||
storageConfig := localstorage.NewConfig(cfg)
|
||||
lockClient, err := locking.NewClient(remote, getAPIClient())
|
||||
if err == nil {
|
||||
err = lockClient.SetupFileCache(storageConfig.LfsStorageDir)
|
||||
@ -102,8 +102,8 @@ func newLockClient(remote string) *locking.Client {
|
||||
}
|
||||
|
||||
// Configure dirs
|
||||
lockClient.LocalWorkingDir = config.LocalWorkingDir
|
||||
lockClient.LocalGitDir = config.LocalGitDir
|
||||
lockClient.LocalWorkingDir = cfg.LocalWorkingDir()
|
||||
lockClient.LocalGitDir = cfg.LocalGitDir()
|
||||
lockClient.SetLockableFilesReadOnly = cfg.SetLockableFilesReadOnly()
|
||||
|
||||
return lockClient
|
||||
@ -138,6 +138,46 @@ func downloadTransfer(p *lfs.WrappedPointer) (name, path, oid string, size int64
|
||||
return p.Name, path, p.Oid, p.Size
|
||||
}
|
||||
|
||||
// Get user-readable manual install steps for hooks
|
||||
func getHookInstallSteps() string {
|
||||
hooks := lfs.LoadHooks(cfg.HookDir())
|
||||
steps := make([]string, 0, len(hooks))
|
||||
for _, h := range hooks {
|
||||
steps = append(steps, fmt.Sprintf(
|
||||
"Add the following to .git/hooks/%s:\n\n%s",
|
||||
h.Type, tools.Indent(h.Contents)))
|
||||
}
|
||||
|
||||
return strings.Join(steps, "\n\n")
|
||||
}
|
||||
|
||||
func installHooks(force bool) error {
|
||||
hooks := lfs.LoadHooks(cfg.HookDir())
|
||||
for _, h := range hooks {
|
||||
if err := h.Install(force); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// uninstallHooks removes all hooks in range of the `hooks` var.
|
||||
func uninstallHooks() error {
|
||||
if !cfg.InRepo() {
|
||||
return errors.New("Not in a git repository")
|
||||
}
|
||||
|
||||
hooks := lfs.LoadHooks(cfg.HookDir())
|
||||
for _, h := range hooks {
|
||||
if err := h.Uninstall(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error prints a formatted message to Stderr. It also gets printed to the
|
||||
// panic log if one is created for this command.
|
||||
func Error(format string, args ...interface{}) {
|
||||
@ -253,7 +293,7 @@ func requireStdin(msg string) {
|
||||
}
|
||||
|
||||
func requireInRepo() {
|
||||
if !lfs.InRepo() {
|
||||
if !cfg.InRepo() {
|
||||
Print("Not in a git repository.")
|
||||
os.Exit(128)
|
||||
}
|
||||
@ -275,11 +315,11 @@ func logPanic(loggedError error) string {
|
||||
|
||||
now := time.Now()
|
||||
name := now.Format("20060102T150405.999999999")
|
||||
full := filepath.Join(config.LocalLogDir, name+".log")
|
||||
full := filepath.Join(cfg.LocalLogDir(), name+".log")
|
||||
|
||||
if err := os.MkdirAll(config.LocalLogDir, 0755); err != nil {
|
||||
if err := os.MkdirAll(cfg.LocalLogDir(), 0755); err != nil {
|
||||
full = ""
|
||||
fmt.Fprintf(fmtWriter, "Unable to log panic to %s: %s\n\n", config.LocalLogDir, err.Error())
|
||||
fmt.Fprintf(fmtWriter, "Unable to log panic to %s: %s\n\n", cfg.LocalLogDir(), err.Error())
|
||||
} else if file, err := os.Create(full); err != nil {
|
||||
filename := full
|
||||
full = ""
|
||||
@ -340,7 +380,7 @@ func ipAddresses() []string {
|
||||
|
||||
func logPanicToWriter(w io.Writer, loggedError error, le string) {
|
||||
// log the version
|
||||
gitV, err := git.Config.Version()
|
||||
gitV, err := cfg.GitVersion()
|
||||
if err != nil {
|
||||
gitV = "Error getting git version: " + err.Error()
|
||||
}
|
||||
@ -407,15 +447,11 @@ func buildProgressMeter(dryRun bool) *progress.ProgressMeter {
|
||||
func requireGitVersion() {
|
||||
minimumGit := "1.8.2"
|
||||
|
||||
if !git.Config.IsGitVersionAtLeast(minimumGit) {
|
||||
gitver, err := git.Config.Version()
|
||||
if !cfg.IsGitVersionAtLeast(minimumGit) {
|
||||
gitver, err := cfg.GitVersion()
|
||||
if err != nil {
|
||||
Exit("Error getting git version: %s", err)
|
||||
}
|
||||
Exit("git version >= %s is required for Git LFS, your version: %s", minimumGit, gitver)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
log.SetOutput(ErrorWriter)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func newSingleCheckout(gitEnv config.Environment, remote string) abstractCheckou
|
||||
|
||||
// Get a converter from repo-relative to cwd-relative
|
||||
// Since writing data & calling git update-index must be relative to cwd
|
||||
pathConverter, err := lfs.NewRepoToCurrentPathConverter()
|
||||
pathConverter, err := lfs.NewRepoToCurrentPathConverter(cfg)
|
||||
if err != nil {
|
||||
Panic(err, "Could not convert file paths")
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -51,6 +52,8 @@ func RegisterCommand(name string, runFn func(cmd *cobra.Command, args []string),
|
||||
// Run initializes the 'git-lfs' command and runs it with the given stdin and
|
||||
// command line args.
|
||||
func Run() {
|
||||
log.SetOutput(ErrorWriter)
|
||||
|
||||
root := NewCommand("git-lfs", gitlfsCommand)
|
||||
root.PreRun = nil
|
||||
|
||||
@ -59,6 +62,8 @@ func Run() {
|
||||
root.SetHelpFunc(helpCommand)
|
||||
root.SetUsageFunc(usageCommand)
|
||||
|
||||
cfg = config.Config
|
||||
|
||||
for _, f := range commandFuncs {
|
||||
if cmd := f(); cmd != nil {
|
||||
root.AddCommand(cmd)
|
||||
@ -78,12 +83,12 @@ func gitlfsCommand(cmd *cobra.Command, args []string) {
|
||||
// necessary to wire it up via `cobra.Command.PreRun`. When run, this function
|
||||
// will resolve the localstorage directories.
|
||||
func resolveLocalStorage(cmd *cobra.Command, args []string) {
|
||||
localstorage.ResolveDirs()
|
||||
localstorage.ResolveDirs(cfg)
|
||||
setupHTTPLogger(getAPIClient())
|
||||
}
|
||||
|
||||
func setupLocalStorage(cmd *cobra.Command, args []string) {
|
||||
config.ResolveGitBasicDirs()
|
||||
cfg.ResolveGitBasicDirs()
|
||||
setupHTTPLogger(getAPIClient())
|
||||
}
|
||||
|
||||
@ -113,7 +118,7 @@ func setupHTTPLogger(c *lfsapi.Client) {
|
||||
return
|
||||
}
|
||||
|
||||
logBase := filepath.Join(config.LocalLogDir, "http")
|
||||
logBase := filepath.Join(cfg.LocalLogDir(), "http")
|
||||
if err := os.MkdirAll(logBase, 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error logging http stats: %s\n", err)
|
||||
return
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/git-lfs/git-lfs/lfs"
|
||||
"github.com/git-lfs/git-lfs/lfsapi"
|
||||
"github.com/git-lfs/git-lfs/locking"
|
||||
@ -412,7 +411,7 @@ func ensureFile(smudgePath, cleanPath string, allowMissing bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
localPath := filepath.Join(config.LocalWorkingDir, smudgePath)
|
||||
localPath := filepath.Join(cfg.LocalWorkingDir(), smudgePath)
|
||||
file, err := os.Open(localPath)
|
||||
if err != nil {
|
||||
if allowMissing {
|
||||
@ -486,6 +485,6 @@ func disableFor(endpoint lfsapi.Endpoint) error {
|
||||
|
||||
key := strings.Join([]string{"lfs", endpoint.Url, "locksverify"}, ".")
|
||||
|
||||
_, err := git.Config.SetLocal("", key, "false")
|
||||
_, err := cfg.SetGitLocalKey("", key, "false")
|
||||
return err
|
||||
}
|
||||
|
361
config/config.go
361
config/config.go
@ -4,15 +4,11 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/git-lfs/git-lfs/tools"
|
||||
)
|
||||
|
||||
@ -23,32 +19,6 @@ var (
|
||||
gitConfigWarningPrefix = "lfs."
|
||||
)
|
||||
|
||||
// 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)
|
||||
FetchRecentRefsDays int `git:"lfs.fetchrecentrefsdays"`
|
||||
// Makes the FetchRecentRefsDays option apply to remote refs from fetch source as well (default true)
|
||||
FetchRecentRefsIncludeRemotes bool `git:"lfs.fetchrecentremoterefs"`
|
||||
// number of days prior to latest commit on a ref that we'll fetch previous
|
||||
// LFS changes too (default 0 = only fetch at ref)
|
||||
FetchRecentCommitsDays int `git:"lfs.fetchrecentcommitsdays"`
|
||||
// Whether to always fetch recent even without --recent
|
||||
FetchRecentAlways bool `git:"lfs.fetchrecentalways"`
|
||||
// Number of days added to FetchRecent*; data outside combined window will be
|
||||
// deleted when prune is run. (default 3)
|
||||
PruneOffsetDays int `git:"lfs.pruneoffsetdays"`
|
||||
// Always verify with remote before pruning
|
||||
PruneVerifyRemoteAlways bool `git:"lfs.pruneverifyremotealways"`
|
||||
// Name of remote to check for unpushed and verify checks
|
||||
PruneRemoteName string `git:"lfs.pruneremotetocheck"`
|
||||
}
|
||||
|
||||
// Storage configuration
|
||||
type StorageConfig struct {
|
||||
LfsStorageDir string `git:"lfs.storage"`
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
// Os provides a `*Environment` used to access to the system's
|
||||
// environment through os.Getenv. It is the point of entry for all
|
||||
@ -60,6 +30,12 @@ type Configuration struct {
|
||||
// configuration.
|
||||
Git Environment
|
||||
|
||||
// gitConfig can fetch or modify the current Git config and track the Git
|
||||
// version.
|
||||
gitConfig *git.Configuration
|
||||
|
||||
fs *fs
|
||||
|
||||
CurrentRemote string
|
||||
|
||||
loading sync.Mutex // guards initialization of gitConfig and remotes
|
||||
@ -68,12 +44,38 @@ type Configuration struct {
|
||||
}
|
||||
|
||||
func New() *Configuration {
|
||||
c := &Configuration{Os: EnvironmentOf(NewOsFetcher())}
|
||||
c.Git = &gitEnvironment{config: c}
|
||||
initConfig(c)
|
||||
gitConf := git.Config
|
||||
c := &Configuration{
|
||||
CurrentRemote: defaultRemote,
|
||||
Os: EnvironmentOf(NewOsFetcher()),
|
||||
gitConfig: gitConf,
|
||||
}
|
||||
c.Git = &delayedEnvironment{
|
||||
callback: func() Environment {
|
||||
sources, err := gitConf.Sources(filepath.Join(c.LocalWorkingDir(), ".lfsconfig"))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error reading git config: %s\n", err)
|
||||
}
|
||||
return c.readGitConfig(sources...)
|
||||
},
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Configuration) readGitConfig(gitconfigs ...*git.ConfigurationSource) Environment {
|
||||
gf, extensions, uniqRemotes := readGitConfig(gitconfigs...)
|
||||
c.extensions = extensions
|
||||
c.remotes = make([]string, 0, len(uniqRemotes))
|
||||
for remote, isOrigin := range uniqRemotes {
|
||||
if isOrigin {
|
||||
continue
|
||||
}
|
||||
c.remotes = append(c.remotes, remote)
|
||||
}
|
||||
|
||||
return EnvironmentOf(gf)
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -90,137 +92,29 @@ type Values struct {
|
||||
// This method should only be used during testing.
|
||||
func NewFrom(v Values) *Configuration {
|
||||
c := &Configuration{
|
||||
Os: EnvironmentOf(mapFetcher(v.Os)),
|
||||
Git: EnvironmentOf(mapFetcher(v.Git)),
|
||||
CurrentRemote: defaultRemote,
|
||||
Os: EnvironmentOf(mapFetcher(v.Os)),
|
||||
gitConfig: git.Config,
|
||||
}
|
||||
c.Git = &delayedEnvironment{
|
||||
callback: func() Environment {
|
||||
source := &git.ConfigurationSource{
|
||||
Lines: make([]string, 0, len(v.Git)),
|
||||
}
|
||||
|
||||
for key, values := range v.Git {
|
||||
for _, value := range values {
|
||||
fmt.Printf("Config: %s=%s\n", key, value)
|
||||
source.Lines = append(source.Lines, fmt.Sprintf("%s=%s", key, value))
|
||||
}
|
||||
}
|
||||
|
||||
return c.readGitConfig(source)
|
||||
},
|
||||
}
|
||||
initConfig(c)
|
||||
return c
|
||||
}
|
||||
|
||||
func initConfig(c *Configuration) {
|
||||
c.CurrentRemote = defaultRemote
|
||||
}
|
||||
|
||||
// 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
|
||||
// // 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.
|
||||
//
|
||||
// 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)
|
||||
|
||||
lookups, err := c.parseTag(sfield.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var val interface{}
|
||||
for _, lookup := range lookups {
|
||||
if _, ok := lookup.Get(); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch sfield.Type.Kind() {
|
||||
case reflect.String:
|
||||
val, _ = lookup.Get()
|
||||
case reflect.Int:
|
||||
val = lookup.Int(int(field.Int()))
|
||||
case reflect.Bool:
|
||||
val = lookup.Bool(field.Bool())
|
||||
default:
|
||||
return fmt.Errorf("lfs/config: unsupported target type for field %q: %v",
|
||||
sfield.Name, sfield.Type.String())
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
into.Field(i).Set(reflect.ValueOf(val))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
tagRe = regexp.MustCompile("((\\w+:\"[^\"]*\")\\b?)+")
|
||||
emptyEnv = EnvironmentOf(MapFetcher(nil))
|
||||
)
|
||||
|
||||
type lookup struct {
|
||||
key string
|
||||
env Environment
|
||||
}
|
||||
|
||||
func (l *lookup) Get() (interface{}, bool) { return l.env.Get(l.key) }
|
||||
func (l *lookup) Int(or int) int { return l.env.Int(l.key, or) }
|
||||
func (l *lookup) Bool(or bool) bool { return l.env.Bool(l.key, or) }
|
||||
|
||||
// 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.
|
||||
func (c *Configuration) parseTag(tag reflect.StructTag) ([]*lookup, error) {
|
||||
var lookups []*lookup
|
||||
|
||||
parts := tagRe.FindAllString(string(tag), -1)
|
||||
for _, part := range parts {
|
||||
sep := strings.SplitN(part, ":", 2)
|
||||
if len(sep) != 2 {
|
||||
return nil, errors.Errorf("config: invalid struct tag %q", tag)
|
||||
}
|
||||
|
||||
var env Environment
|
||||
switch strings.ToLower(sep[0]) {
|
||||
case "git":
|
||||
env = c.Git
|
||||
case "os":
|
||||
env = c.Os
|
||||
default:
|
||||
// ignore other struct tags, like `json:""`, etc.
|
||||
env = emptyEnv
|
||||
}
|
||||
|
||||
uq, err := strconv.Unquote(sep[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lookups = append(lookups, &lookup{
|
||||
key: uq,
|
||||
env: env,
|
||||
})
|
||||
}
|
||||
|
||||
return lookups, nil
|
||||
}
|
||||
|
||||
// BasicTransfersOnly returns whether to only allow "basic" HTTP transfers.
|
||||
// Default is false, including if the lfs.basictransfersonly is invalid
|
||||
func (c *Configuration) BasicTransfersOnly() bool {
|
||||
@ -245,7 +139,6 @@ func (c *Configuration) FetchExcludePaths() []string {
|
||||
|
||||
func (c *Configuration) Remotes() []string {
|
||||
c.loadGitConfig()
|
||||
|
||||
return c.remotes
|
||||
}
|
||||
|
||||
@ -259,34 +152,6 @@ func (c *Configuration) SortedExtensions() ([]Extension, error) {
|
||||
return SortExtensions(c.Extensions())
|
||||
}
|
||||
|
||||
func (c *Configuration) FetchPruneConfig() FetchPruneConfig {
|
||||
f := &FetchPruneConfig{
|
||||
FetchRecentRefsDays: 7,
|
||||
FetchRecentRefsIncludeRemotes: true,
|
||||
PruneOffsetDays: 3,
|
||||
PruneRemoteName: "origin",
|
||||
}
|
||||
|
||||
if err := c.Unmarshal(f); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return *f
|
||||
}
|
||||
|
||||
func (c *Configuration) StorageConfig() StorageConfig {
|
||||
s := &StorageConfig{
|
||||
LfsStorageDir: "lfs",
|
||||
}
|
||||
|
||||
if err := c.Unmarshal(s); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
if !filepath.IsAbs(s.LfsStorageDir) {
|
||||
s.LfsStorageDir = filepath.Join(LocalGitStorageDir, s.LfsStorageDir)
|
||||
}
|
||||
return *s
|
||||
}
|
||||
|
||||
func (c *Configuration) SkipDownloadErrors() bool {
|
||||
return c.Os.Bool("GIT_LFS_SKIP_DOWNLOAD_ERRORS", false) || c.Git.Bool("lfs.skipdownloaderrors", false)
|
||||
}
|
||||
@ -295,6 +160,110 @@ func (c *Configuration) SetLockableFilesReadOnly() bool {
|
||||
return c.Os.Bool("GIT_LFS_SET_LOCKABLE_READONLY", true) && c.Git.Bool("lfs.setlockablereadonly", true)
|
||||
}
|
||||
|
||||
func (c *Configuration) HookDir() string {
|
||||
if c.gitConfig.IsGitVersionAtLeast("2.9.0") {
|
||||
hp, ok := c.Git.Get("core.hooksPath")
|
||||
if ok {
|
||||
return hp
|
||||
}
|
||||
}
|
||||
return filepath.Join(c.LocalGitDir(), "hooks")
|
||||
}
|
||||
|
||||
func (c *Configuration) InRepo() bool {
|
||||
return c.fs.InRepo()
|
||||
}
|
||||
|
||||
func (c *Configuration) LocalWorkingDir() string {
|
||||
c.ResolveGitBasicDirs()
|
||||
return c.fs.WorkingDir
|
||||
}
|
||||
|
||||
func (c *Configuration) LocalGitDir() string {
|
||||
c.ResolveGitBasicDirs()
|
||||
return c.fs.GitDir
|
||||
}
|
||||
|
||||
func (c *Configuration) LocalGitStorageDir() string {
|
||||
c.ResolveGitBasicDirs()
|
||||
return c.fs.GitStorageDir
|
||||
}
|
||||
|
||||
func (c *Configuration) LocalReferenceDir() string {
|
||||
return LocalReferenceDir
|
||||
}
|
||||
|
||||
func (c *Configuration) LocalLogDir() string {
|
||||
c.ResolveGitBasicDirs()
|
||||
return c.fs.LogDir
|
||||
}
|
||||
|
||||
func (c *Configuration) SetLocalLogDir(s string) {
|
||||
c.ResolveGitBasicDirs()
|
||||
c.fs.LogDir = s
|
||||
}
|
||||
|
||||
func (c *Configuration) GitConfig() *git.Configuration {
|
||||
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 {
|
||||
return c.gitConfig.FindGlobal(key)
|
||||
}
|
||||
|
||||
func (c *Configuration) FindGitSystemKey(key string) string {
|
||||
return c.gitConfig.FindSystem(key)
|
||||
}
|
||||
|
||||
func (c *Configuration) FindGitLocalKey(key string) string {
|
||||
return c.gitConfig.FindLocal(key)
|
||||
}
|
||||
|
||||
func (c *Configuration) SetGitGlobalKey(key, val string) (string, error) {
|
||||
return c.gitConfig.SetGlobal(key, val)
|
||||
}
|
||||
|
||||
func (c *Configuration) SetGitSystemKey(key, val string) (string, error) {
|
||||
return c.gitConfig.SetSystem(key, val)
|
||||
}
|
||||
|
||||
func (c *Configuration) SetGitLocalKey(file, key, val string) (string, error) {
|
||||
return c.gitConfig.SetLocal(file, key, val)
|
||||
}
|
||||
|
||||
func (c *Configuration) UnsetGitGlobalSection(key string) (string, error) {
|
||||
return c.gitConfig.UnsetGlobalSection(key)
|
||||
}
|
||||
|
||||
func (c *Configuration) UnsetGitSystemSection(key string) (string, error) {
|
||||
return c.gitConfig.UnsetSystemSection(key)
|
||||
}
|
||||
|
||||
func (c *Configuration) UnsetGitLocalSection(key string) (string, error) {
|
||||
return c.gitConfig.UnsetLocalSection(key)
|
||||
}
|
||||
|
||||
func (c *Configuration) UnsetGitLocalKey(file, key string) (string, error) {
|
||||
return c.gitConfig.UnsetLocalKey(file, key)
|
||||
}
|
||||
|
||||
func (c *Configuration) ResolveGitBasicDirs() {
|
||||
c.loading.Lock()
|
||||
defer c.loading.Unlock()
|
||||
|
||||
if c.fs == nil {
|
||||
c.fs = resolveGitBasicDirs()
|
||||
}
|
||||
}
|
||||
|
||||
// loadGitConfig is a temporary measure to support legacy behavior dependent on
|
||||
// accessing properties set by ReadGitConfig, namely:
|
||||
// - `c.extensions`
|
||||
@ -306,12 +275,10 @@ func (c *Configuration) SetLockableFilesReadOnly() bool {
|
||||
//
|
||||
// loadGitConfig returns a bool returning whether or not `loadGitConfig` was
|
||||
// called AND the method did not return early.
|
||||
func (c *Configuration) loadGitConfig() bool {
|
||||
if g, ok := c.Git.(*gitEnvironment); ok {
|
||||
return g.loadGitConfig()
|
||||
func (c *Configuration) loadGitConfig() {
|
||||
if g, ok := c.Git.(*delayedEnvironment); ok {
|
||||
g.Load()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// CurrentCommitter returns the name/email that would be used to author a commit
|
||||
|
@ -2,7 +2,6 @@ package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@ -67,17 +66,12 @@ func TestTusTransfersAllowedInvalidValue(t *testing.T) {
|
||||
|
||||
func TestLoadValidExtension(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string][]string{},
|
||||
})
|
||||
|
||||
cfg.extensions = map[string]Extension{
|
||||
"foo": Extension{
|
||||
"foo",
|
||||
"foo-clean %f",
|
||||
"foo-smudge %f",
|
||||
2,
|
||||
Git: map[string][]string{
|
||||
"lfs.extension.foo.clean": []string{"foo-clean %f"},
|
||||
"lfs.extension.foo.smudge": []string{"foo-smudge %f"},
|
||||
"lfs.extension.foo.priority": []string{"2"},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
ext := cfg.Extensions()["foo"]
|
||||
|
||||
@ -97,40 +91,6 @@ func TestLoadInvalidExtension(t *testing.T) {
|
||||
assert.Equal(t, 0, ext.Priority)
|
||||
}
|
||||
|
||||
func TestFetchPruneConfigDefault(t *testing.T) {
|
||||
cfg := NewFrom(Values{})
|
||||
fp := cfg.FetchPruneConfig()
|
||||
|
||||
assert.Equal(t, 7, fp.FetchRecentRefsDays)
|
||||
assert.Equal(t, 0, fp.FetchRecentCommitsDays)
|
||||
assert.Equal(t, 3, fp.PruneOffsetDays)
|
||||
assert.True(t, fp.FetchRecentRefsIncludeRemotes)
|
||||
assert.Equal(t, 3, fp.PruneOffsetDays)
|
||||
assert.Equal(t, "origin", fp.PruneRemoteName)
|
||||
assert.False(t, fp.PruneVerifyRemoteAlways)
|
||||
|
||||
}
|
||||
func TestFetchPruneConfigCustom(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string][]string{
|
||||
"lfs.fetchrecentrefsdays": []string{"12"},
|
||||
"lfs.fetchrecentremoterefs": []string{"false"},
|
||||
"lfs.fetchrecentcommitsdays": []string{"9"},
|
||||
"lfs.pruneoffsetdays": []string{"30"},
|
||||
"lfs.pruneverifyremotealways": []string{"true"},
|
||||
"lfs.pruneremotetocheck": []string{"upstream"},
|
||||
},
|
||||
})
|
||||
fp := cfg.FetchPruneConfig()
|
||||
|
||||
assert.Equal(t, 12, fp.FetchRecentRefsDays)
|
||||
assert.Equal(t, 9, fp.FetchRecentCommitsDays)
|
||||
assert.False(t, fp.FetchRecentRefsIncludeRemotes)
|
||||
assert.Equal(t, 30, fp.PruneOffsetDays)
|
||||
assert.Equal(t, "upstream", fp.PruneRemoteName)
|
||||
assert.True(t, fp.PruneVerifyRemoteAlways)
|
||||
}
|
||||
|
||||
func TestFetchIncludeExcludesAreCleaned(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string][]string{
|
||||
@ -142,157 +102,3 @@ func TestFetchIncludeExcludesAreCleaned(t *testing.T) {
|
||||
assert.Equal(t, []string{"/path/to/clean"}, cfg.FetchIncludePaths())
|
||||
assert.Equal(t, []string{"/other/path/to/clean"}, cfg.FetchExcludePaths())
|
||||
}
|
||||
|
||||
func TestUnmarshalMultipleTypes(t *testing.T) {
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string][]string{
|
||||
"string": []string{"string"},
|
||||
"int": []string{"1"},
|
||||
"bool": []string{"true"},
|
||||
},
|
||||
Os: map[string][]string{
|
||||
"string": []string{"string"},
|
||||
"int": []string{"1"},
|
||||
"bool": []string{"true"},
|
||||
},
|
||||
})
|
||||
|
||||
v := &struct {
|
||||
GitString string `git:"string"`
|
||||
GitInt int `git:"int"`
|
||||
GitBool bool `git:"bool"`
|
||||
OsString string `os:"string"`
|
||||
OsInt int `os:"int"`
|
||||
OsBool bool `os:"bool"`
|
||||
}{}
|
||||
|
||||
assert.Nil(t, cfg.Unmarshal(v))
|
||||
|
||||
assert.Equal(t, "string", v.GitString)
|
||||
assert.Equal(t, 1, v.GitInt)
|
||||
assert.Equal(t, true, v.GitBool)
|
||||
assert.Equal(t, "string", v.OsString)
|
||||
assert.Equal(t, 1, v.OsInt)
|
||||
assert.Equal(t, true, v.OsBool)
|
||||
}
|
||||
|
||||
func TestUnmarshalErrsOnNonPointerType(t *testing.T) {
|
||||
type T struct {
|
||||
Foo string `git:"foo"`
|
||||
}
|
||||
|
||||
cfg := NewFrom(Values{})
|
||||
|
||||
err := cfg.Unmarshal(T{})
|
||||
|
||||
assert.Equal(t, "lfs/config: unable to parse non-pointer type of config.T", err.Error())
|
||||
}
|
||||
|
||||
func TestUnmarshalLeavesNonZeroValuesWhenKeysEmpty(t *testing.T) {
|
||||
v := &struct {
|
||||
String string `git:"string"`
|
||||
Int int `git:"int"`
|
||||
Bool bool `git:"bool"`
|
||||
}{"foo", 1, true}
|
||||
|
||||
cfg := NewFrom(Values{})
|
||||
|
||||
err := cfg.Unmarshal(v)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "foo", v.String)
|
||||
assert.Equal(t, 1, v.Int)
|
||||
assert.Equal(t, true, v.Bool)
|
||||
}
|
||||
|
||||
func TestUnmarshalOverridesNonZeroValuesWhenValuesPresent(t *testing.T) {
|
||||
v := &struct {
|
||||
String string `git:"string"`
|
||||
Int int `git:"int"`
|
||||
Bool bool `git:"bool"`
|
||||
}{"foo", 1, true}
|
||||
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string][]string{
|
||||
"string": []string{"bar"},
|
||||
"int": []string{"2"},
|
||||
"bool": []string{"false"},
|
||||
},
|
||||
})
|
||||
|
||||
err := cfg.Unmarshal(v)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "bar", v.String)
|
||||
assert.Equal(t, 2, v.Int)
|
||||
assert.Equal(t, false, v.Bool)
|
||||
}
|
||||
|
||||
func TestUnmarshalAllowsBothOsAndGitTags(t *testing.T) {
|
||||
v := &struct {
|
||||
String string `git:"string" os:"STRING"`
|
||||
}{}
|
||||
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string][]string{"string": []string{"foo"}},
|
||||
Os: map[string][]string{"STRING": []string{"bar"}},
|
||||
})
|
||||
|
||||
err := cfg.Unmarshal(v)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "foo", v.String)
|
||||
}
|
||||
|
||||
func TestUnmarshalYieldsToDefaultIfBothEnvsMissing(t *testing.T) {
|
||||
v := &struct {
|
||||
String string `git:"string" os:"STRING"`
|
||||
}{"foo"}
|
||||
|
||||
cfg := NewFrom(Values{})
|
||||
|
||||
err := cfg.Unmarshal(v)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "foo", v.String)
|
||||
}
|
||||
|
||||
func TestUnmarshalOverridesDefaultIfAnyEnvPresent(t *testing.T) {
|
||||
v := &struct {
|
||||
String string `git:"string" os:"STRING"`
|
||||
}{"foo"}
|
||||
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string][]string{"string": []string{"bar"}},
|
||||
Os: map[string][]string{"STRING": []string{"baz"}},
|
||||
})
|
||||
|
||||
err := cfg.Unmarshal(v)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "bar", v.String)
|
||||
}
|
||||
|
||||
func TestUnmarshalIgnoresUnknownEnvironments(t *testing.T) {
|
||||
v := &struct {
|
||||
String string `unknown:"string"`
|
||||
}{}
|
||||
|
||||
cfg := NewFrom(Values{})
|
||||
|
||||
assert.Nil(t, cfg.Unmarshal(v))
|
||||
}
|
||||
|
||||
func TestUnmarshalErrsOnUnsupportedTypes(t *testing.T) {
|
||||
v := &struct {
|
||||
Unsupported time.Duration `git:"duration"`
|
||||
}{}
|
||||
|
||||
cfg := NewFrom(Values{
|
||||
Git: map[string][]string{"duration": []string{"foo"}},
|
||||
})
|
||||
|
||||
err := cfg.Unmarshal(v)
|
||||
|
||||
assert.Equal(t, "lfs/config: unsupported target type for field \"Unsupported\": time.Duration", err.Error())
|
||||
}
|
||||
|
68
config/delayed_environment.go
Normal file
68
config/delayed_environment.go
Normal file
@ -0,0 +1,68 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// delayedEnvironment is an implementation of the Environment which wraps the legacy
|
||||
// behavior of `*config.Configuration.loadGitConfig()`.
|
||||
//
|
||||
// It is functionally equivelant to call `cfg.loadGitConfig()` before calling
|
||||
// methods on the Environment type.
|
||||
type delayedEnvironment struct {
|
||||
env Environment
|
||||
loading sync.Mutex
|
||||
callback func() Environment
|
||||
}
|
||||
|
||||
// Get is shorthand for calling the e.Load(), and then returning
|
||||
// `e.env.Get(key)`.
|
||||
func (e *delayedEnvironment) Get(key string) (string, bool) {
|
||||
e.Load()
|
||||
return e.env.Get(key)
|
||||
}
|
||||
|
||||
// Get is shorthand for calling the e.Load(), and then returning
|
||||
// `e.env.GetAll(key)`.
|
||||
func (e *delayedEnvironment) GetAll(key string) []string {
|
||||
e.Load()
|
||||
return e.env.GetAll(key)
|
||||
}
|
||||
|
||||
// Get is shorthand for calling the e.Load(), and then returning
|
||||
// `e.env.Bool(key, def)`.
|
||||
func (e *delayedEnvironment) Bool(key string, def bool) bool {
|
||||
e.Load()
|
||||
return e.env.Bool(key, def)
|
||||
}
|
||||
|
||||
// Get is shorthand for calling the e.Load(), and then returning
|
||||
// `e.env.Int(key, def)`.
|
||||
func (e *delayedEnvironment) Int(key string, def int) int {
|
||||
e.Load()
|
||||
return e.env.Int(key, def)
|
||||
}
|
||||
|
||||
// All returns a copy of all the key/value pairs for the current git config.
|
||||
func (e *delayedEnvironment) All() map[string][]string {
|
||||
e.Load()
|
||||
return e.env.All()
|
||||
}
|
||||
|
||||
// Load reads and parses the .gitconfig by calling ReadGitConfig. It
|
||||
// also sets values on the configuration instance `g.config`.
|
||||
//
|
||||
// If Load has already been called, this method will bail out early,
|
||||
// and return false. Otherwise it will preform the entire parse and return true.
|
||||
//
|
||||
// Load is safe to call across multiple goroutines.
|
||||
func (e *delayedEnvironment) Load() {
|
||||
e.loading.Lock()
|
||||
defer e.loading.Unlock()
|
||||
|
||||
if e.env != nil {
|
||||
return
|
||||
}
|
||||
|
||||
e.env = e.callback()
|
||||
}
|
@ -13,32 +13,48 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
LocalWorkingDir string
|
||||
LocalGitDir string // parent of index / config / hooks etc
|
||||
LocalGitStorageDir string // parent of objects/lfs (may be same as LocalGitDir but may not)
|
||||
LocalReferenceDir string // alternative local media dir (relative to clone reference repo)
|
||||
LocalLogDir string
|
||||
LocalReferenceDir string // alternative local media dir (relative to clone reference repo)
|
||||
)
|
||||
|
||||
type fs struct {
|
||||
WorkingDir string
|
||||
GitDir string // parent of index / config / hooks etc
|
||||
GitStorageDir string // parent of objects/lfs (may be same as LocalGitDir but may not)
|
||||
ReferenceDir string // alternative local media dir (relative to clone reference repo)
|
||||
LogDir string
|
||||
}
|
||||
|
||||
func (f *fs) InRepo() bool {
|
||||
if f == nil {
|
||||
return false
|
||||
}
|
||||
return len(f.GitDir) > 0
|
||||
}
|
||||
|
||||
// Determins the LocalWorkingDir, LocalGitDir etc
|
||||
func ResolveGitBasicDirs() {
|
||||
var err error
|
||||
LocalGitDir, LocalWorkingDir, err = git.GitAndRootDirs()
|
||||
if err == nil {
|
||||
// Make sure we've fully evaluated symlinks, failure to do consistently
|
||||
// can cause discrepancies
|
||||
LocalGitDir = tools.ResolveSymlinks(LocalGitDir)
|
||||
LocalWorkingDir = tools.ResolveSymlinks(LocalWorkingDir)
|
||||
|
||||
LocalGitStorageDir = resolveGitStorageDir(LocalGitDir)
|
||||
LocalReferenceDir = resolveReferenceDir(LocalGitStorageDir)
|
||||
|
||||
} else {
|
||||
func resolveGitBasicDirs() *fs {
|
||||
localGitDir, localWorkingDir, err := git.GitAndRootDirs()
|
||||
if err != nil {
|
||||
errMsg := err.Error()
|
||||
tracerx.Printf("Error running 'git rev-parse': %s", errMsg)
|
||||
if !strings.Contains(errMsg, "Not a git repository") {
|
||||
fmt.Fprintf(os.Stderr, "Error: %s\n", errMsg)
|
||||
}
|
||||
return &fs{}
|
||||
}
|
||||
|
||||
// Make sure we've fully evaluated symlinks, failure to do consistently
|
||||
// can cause discrepancies
|
||||
localGitDir = tools.ResolveSymlinks(localGitDir)
|
||||
localWorkingDir = tools.ResolveSymlinks(localWorkingDir)
|
||||
localGitStorageDir := resolveGitStorageDir(localGitDir)
|
||||
LocalReferenceDir = resolveReferenceDir(localGitStorageDir)
|
||||
|
||||
return &fs{
|
||||
GitDir: localGitDir,
|
||||
WorkingDir: localWorkingDir,
|
||||
GitStorageDir: localGitStorageDir,
|
||||
ReferenceDir: LocalReferenceDir,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,85 +0,0 @@
|
||||
package config
|
||||
|
||||
// gitEnvironment is an implementation of the Environment which wraps the legacy
|
||||
// behavior or `*config.Configuration.loadGitConfig()`.
|
||||
//
|
||||
// It is functionally equivelant to call `cfg.loadGitConfig()` before calling
|
||||
// methods on the Environment type.
|
||||
type gitEnvironment struct {
|
||||
// git is the Environment which gitEnvironment wraps.
|
||||
git Environment
|
||||
// config is the *Configuration instance which is mutated by
|
||||
// `loadGitConfig`.
|
||||
config *Configuration
|
||||
}
|
||||
|
||||
// Get is shorthand for calling the loadGitConfig, and then returning
|
||||
// `g.git.Get(key)`.
|
||||
func (g *gitEnvironment) Get(key string) (val string, ok bool) {
|
||||
g.loadGitConfig()
|
||||
|
||||
return g.git.Get(key)
|
||||
}
|
||||
|
||||
// Get is shorthand for calling the loadGitConfig, and then returning
|
||||
// `g.git.GetAll(key)`.
|
||||
func (g *gitEnvironment) GetAll(key string) []string {
|
||||
g.loadGitConfig()
|
||||
|
||||
return g.git.GetAll(key)
|
||||
}
|
||||
|
||||
// Get is shorthand for calling the loadGitConfig, and then returning
|
||||
// `g.git.Bool(key, def)`.
|
||||
func (g *gitEnvironment) Bool(key string, def bool) (val bool) {
|
||||
g.loadGitConfig()
|
||||
|
||||
return g.git.Bool(key, def)
|
||||
}
|
||||
|
||||
// Get is shorthand for calling the loadGitConfig, and then returning
|
||||
// `g.git.Int(key, def)`.
|
||||
func (g *gitEnvironment) Int(key string, def int) (val int) {
|
||||
g.loadGitConfig()
|
||||
|
||||
return g.git.Int(key, def)
|
||||
}
|
||||
|
||||
// All returns a copy of all the key/value pairs for the current git config.
|
||||
func (g *gitEnvironment) All() map[string][]string {
|
||||
g.loadGitConfig()
|
||||
|
||||
return g.git.All()
|
||||
}
|
||||
|
||||
// loadGitConfig reads and parses the .gitconfig by calling ReadGitConfig. It
|
||||
// also sets values on the configuration instance `g.config`.
|
||||
//
|
||||
// If loadGitConfig has already been called, this method will bail out early,
|
||||
// and return false. Otherwise it will preform the entire parse and return true.
|
||||
//
|
||||
// loadGitConfig is safe to call across multiple goroutines.
|
||||
func (g *gitEnvironment) loadGitConfig() bool {
|
||||
g.config.loading.Lock()
|
||||
defer g.config.loading.Unlock()
|
||||
|
||||
if g.git != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
gf, extensions, uniqRemotes := ReadGitConfig(getGitConfigs()...)
|
||||
|
||||
g.git = EnvironmentOf(gf)
|
||||
|
||||
g.config.extensions = extensions
|
||||
|
||||
g.config.remotes = make([]string, 0, len(uniqRemotes))
|
||||
for remote, isOrigin := range uniqRemotes {
|
||||
if isOrigin {
|
||||
continue
|
||||
}
|
||||
g.config.remotes = append(g.config.remotes, remote)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
@ -3,7 +3,6 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -16,19 +15,7 @@ type GitFetcher struct {
|
||||
vals map[string][]string
|
||||
}
|
||||
|
||||
type GitConfig struct {
|
||||
Lines []string
|
||||
OnlySafeKeys bool
|
||||
}
|
||||
|
||||
func NewGitConfig(gitconfiglines string, onlysafe bool) *GitConfig {
|
||||
return &GitConfig{
|
||||
Lines: strings.Split(gitconfiglines, "\n"),
|
||||
OnlySafeKeys: onlysafe,
|
||||
}
|
||||
}
|
||||
|
||||
func ReadGitConfig(configs ...*GitConfig) (gf *GitFetcher, extensions map[string]Extension, uniqRemotes map[string]bool) {
|
||||
func readGitConfig(configs ...*git.ConfigurationSource) (gf *GitFetcher, extensions map[string]Extension, uniqRemotes map[string]bool) {
|
||||
vals := make(map[string][]string)
|
||||
ignored := make([]string, 0)
|
||||
|
||||
@ -161,39 +148,6 @@ func (g *GitFetcher) All() map[string][]string {
|
||||
return newmap
|
||||
}
|
||||
|
||||
func getGitConfigs() (sources []*GitConfig) {
|
||||
if lfsconfig := getFileGitConfig(".lfsconfig"); lfsconfig != nil {
|
||||
sources = append(sources, lfsconfig)
|
||||
}
|
||||
|
||||
globalList, err := git.Config.List()
|
||||
if err == nil {
|
||||
sources = append(sources, NewGitConfig(globalList, false))
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error reading git config: %s\n", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getFileGitConfig(basename string) *GitConfig {
|
||||
fullname := filepath.Join(LocalWorkingDir, basename)
|
||||
if _, err := os.Stat(fullname); err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
fmt.Fprintf(os.Stderr, "Error reading %s: %s\n", basename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
lines, err := git.Config.ListFromFile(fullname)
|
||||
if err == nil {
|
||||
return NewGitConfig(lines, true)
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Error reading %s: %s\n", basename, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
func keyIsUnsafe(key string) bool {
|
||||
for _, safe := range safeKeys {
|
||||
if safe == key {
|
||||
|
167
git/config.go
Normal file
167
git/config.go
Normal file
@ -0,0 +1,167 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/rubyist/tracerx"
|
||||
)
|
||||
|
||||
var Config = &Configuration{}
|
||||
|
||||
// Configuration can fetch or modify the current Git config and track the Git
|
||||
// version.
|
||||
type Configuration struct {
|
||||
version string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func ParseConfigLines(lines string, onlySafeKeys bool) *ConfigurationSource {
|
||||
return &ConfigurationSource{
|
||||
Lines: strings.Split(lines, "\n"),
|
||||
OnlySafeKeys: onlySafeKeys,
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigurationSource struct {
|
||||
Lines []string
|
||||
OnlySafeKeys bool
|
||||
}
|
||||
|
||||
// Find returns the git config value for the key
|
||||
func (c *Configuration) Find(val string) string {
|
||||
output, _ := gitSimple("config", val)
|
||||
return output
|
||||
}
|
||||
|
||||
// FindGlobal returns the git config value global scope for the key
|
||||
func (c *Configuration) FindGlobal(key string) string {
|
||||
output, _ := gitSimple("config", "--global", key)
|
||||
return output
|
||||
}
|
||||
|
||||
// FindSystem returns the git config value in system scope for the key
|
||||
func (c *Configuration) FindSystem(key string) string {
|
||||
output, _ := gitSimple("config", "--system", key)
|
||||
return output
|
||||
}
|
||||
|
||||
// Find returns the git config value for the key
|
||||
func (c *Configuration) FindLocal(key string) string {
|
||||
output, _ := gitSimple("config", "--local", key)
|
||||
return output
|
||||
}
|
||||
|
||||
// SetGlobal sets the git config value for the key in the global config
|
||||
func (c *Configuration) SetGlobal(key, val string) (string, error) {
|
||||
return gitSimple("config", "--global", "--replace-all", key, val)
|
||||
}
|
||||
|
||||
// SetSystem sets the git config value for the key in the system config
|
||||
func (c *Configuration) SetSystem(key, val string) (string, error) {
|
||||
return gitSimple("config", "--system", "--replace-all", key, val)
|
||||
}
|
||||
|
||||
// UnsetGlobalSection removes the entire named section from the global config
|
||||
func (c *Configuration) UnsetGlobalSection(key string) (string, error) {
|
||||
return gitSimple("config", "--global", "--remove-section", key)
|
||||
}
|
||||
|
||||
// UnsetSystemSection removes the entire named section from the system config
|
||||
func (c *Configuration) UnsetSystemSection(key string) (string, error) {
|
||||
return gitSimple("config", "--system", "--remove-section", key)
|
||||
}
|
||||
|
||||
// UnsetLocalSection removes the entire named section from the system config
|
||||
func (c *Configuration) UnsetLocalSection(key string) (string, error) {
|
||||
return gitSimple("config", "--local", "--remove-section", key)
|
||||
}
|
||||
|
||||
// SetLocal sets the git config value for the key in the specified config file
|
||||
func (c *Configuration) SetLocal(file, key, val string) (string, error) {
|
||||
args := make([]string, 1, 6)
|
||||
args[0] = "config"
|
||||
if len(file) > 0 {
|
||||
args = append(args, "--file", file)
|
||||
}
|
||||
args = append(args, "--replace-all", key, val)
|
||||
return gitSimple(args...)
|
||||
}
|
||||
|
||||
// UnsetLocalKey removes the git config value for the key from the specified config file
|
||||
func (c *Configuration) UnsetLocalKey(file, key string) (string, error) {
|
||||
args := make([]string, 1, 5)
|
||||
args[0] = "config"
|
||||
if len(file) > 0 {
|
||||
args = append(args, "--file", file)
|
||||
}
|
||||
args = append(args, "--unset", key)
|
||||
return gitSimple(args...)
|
||||
}
|
||||
|
||||
func (c *Configuration) Sources(optionalFilename string) ([]*ConfigurationSource, error) {
|
||||
gitconfig, err := c.Source()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileconfig, err := c.FileSource(optionalFilename)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configs := make([]*ConfigurationSource, 0, 2)
|
||||
if fileconfig != nil {
|
||||
configs = append(configs, fileconfig)
|
||||
}
|
||||
|
||||
return append(configs, gitconfig), nil
|
||||
}
|
||||
|
||||
func (c *Configuration) FileSource(filename string) (*ConfigurationSource, error) {
|
||||
if _, err := os.Stat(filename); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out, err := gitSimple("config", "-l", "-f", filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseConfigLines(out, true), nil
|
||||
}
|
||||
|
||||
func (c *Configuration) Source() (*ConfigurationSource, error) {
|
||||
out, err := gitSimple("config", "-l")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseConfigLines(out, false), nil
|
||||
}
|
||||
|
||||
// Version returns the git version
|
||||
func (c *Configuration) Version() (string, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if len(c.version) == 0 {
|
||||
v, err := gitSimple("version")
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
c.version = v
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
127
git/git.go
127
git/git.go
@ -16,7 +16,6 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
lfserrors "github.com/git-lfs/git-lfs/errors"
|
||||
@ -293,7 +292,6 @@ func RemoteBranchForLocalBranch(localBranch string) string {
|
||||
} else {
|
||||
return localBranch
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func RemoteList() ([]string, error) {
|
||||
@ -462,131 +460,6 @@ func UpdateIndexFromStdin() *subprocess.Cmd {
|
||||
return git("update-index", "-q", "--refresh", "--stdin")
|
||||
}
|
||||
|
||||
type gitConfig struct {
|
||||
gitVersion string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var Config = &gitConfig{}
|
||||
|
||||
// Find returns the git config value for the key
|
||||
func (c *gitConfig) Find(val string) string {
|
||||
output, _ := gitSimple("config", val)
|
||||
return output
|
||||
}
|
||||
|
||||
// FindGlobal returns the git config value global scope for the key
|
||||
func (c *gitConfig) FindGlobal(val string) string {
|
||||
output, _ := gitSimple("config", "--global", val)
|
||||
return output
|
||||
}
|
||||
|
||||
// FindSystem returns the git config value in system scope for the key
|
||||
func (c *gitConfig) FindSystem(val string) string {
|
||||
output, _ := gitSimple("config", "--system", val)
|
||||
return output
|
||||
}
|
||||
|
||||
// Find returns the git config value for the key
|
||||
func (c *gitConfig) FindLocal(val string) string {
|
||||
output, _ := gitSimple("config", "--local", val)
|
||||
return output
|
||||
}
|
||||
|
||||
// SetGlobal sets the git config value for the key in the global config
|
||||
func (c *gitConfig) SetGlobal(key, val string) (string, error) {
|
||||
return gitSimple("config", "--global", "--replace-all", key, val)
|
||||
}
|
||||
|
||||
// SetSystem sets the git config value for the key in the system config
|
||||
func (c *gitConfig) SetSystem(key, val string) (string, error) {
|
||||
return gitSimple("config", "--system", "--replace-all", key, val)
|
||||
}
|
||||
|
||||
// UnsetGlobal removes the git config value for the key from the global config
|
||||
func (c *gitConfig) UnsetGlobal(key string) (string, error) {
|
||||
return gitSimple("config", "--global", "--unset", key)
|
||||
}
|
||||
|
||||
// UnsetSystem removes the git config value for the key from the system config
|
||||
func (c *gitConfig) UnsetSystem(key string) (string, error) {
|
||||
return gitSimple("config", "--system", "--unset", key)
|
||||
}
|
||||
|
||||
// UnsetGlobalSection removes the entire named section from the global config
|
||||
func (c *gitConfig) UnsetGlobalSection(key string) (string, error) {
|
||||
return gitSimple("config", "--global", "--remove-section", key)
|
||||
}
|
||||
|
||||
// UnsetSystemSection removes the entire named section from the system config
|
||||
func (c *gitConfig) UnsetSystemSection(key string) (string, error) {
|
||||
return gitSimple("config", "--system", "--remove-section", key)
|
||||
}
|
||||
|
||||
// UnsetLocalSection removes the entire named section from the system config
|
||||
func (c *gitConfig) UnsetLocalSection(key string) (string, error) {
|
||||
return gitSimple("config", "--local", "--remove-section", key)
|
||||
}
|
||||
|
||||
// SetLocal sets the git config value for the key in the specified config file
|
||||
func (c *gitConfig) SetLocal(file, key, val string) (string, error) {
|
||||
args := make([]string, 1, 6)
|
||||
args[0] = "config"
|
||||
if len(file) > 0 {
|
||||
args = append(args, "--file", file)
|
||||
}
|
||||
args = append(args, "--replace-all", key, val)
|
||||
return gitSimple(args...)
|
||||
}
|
||||
|
||||
// UnsetLocalKey removes the git config value for the key from the specified config file
|
||||
func (c *gitConfig) UnsetLocalKey(file, key string) (string, error) {
|
||||
args := make([]string, 1, 5)
|
||||
args[0] = "config"
|
||||
if len(file) > 0 {
|
||||
args = append(args, "--file", file)
|
||||
}
|
||||
args = append(args, "--unset", key)
|
||||
return gitSimple(args...)
|
||||
}
|
||||
|
||||
// List lists all of the git config values
|
||||
func (c *gitConfig) List() (string, error) {
|
||||
return gitSimple("config", "-l")
|
||||
}
|
||||
|
||||
// ListFromFile lists all of the git config values in the given config file
|
||||
func (c *gitConfig) ListFromFile(f string) (string, error) {
|
||||
return gitSimple("config", "-l", "-f", f)
|
||||
}
|
||||
|
||||
// Version returns the git version
|
||||
func (c *gitConfig) Version() (string, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if len(c.gitVersion) == 0 {
|
||||
v, err := gitSimple("version")
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
c.gitVersion = v
|
||||
}
|
||||
|
||||
return c.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 (c *gitConfig) 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)
|
||||
}
|
||||
|
||||
// RecentBranches returns branches with commit dates on or after the given date/time
|
||||
// Return full Ref type for easier detection of duplicate SHAs etc
|
||||
// since: refs with commits on or after this date will be included
|
||||
|
@ -26,11 +26,68 @@ type Attribute struct {
|
||||
Upgradeables map[string][]string
|
||||
}
|
||||
|
||||
// InstallOptions serves as an argument to Install().
|
||||
type InstallOptions struct {
|
||||
Force bool
|
||||
Local bool
|
||||
System bool
|
||||
// FilterOptions serves as an argument to Install().
|
||||
type FilterOptions struct {
|
||||
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",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Install instructs Git to set all keys and values relative to the root
|
||||
@ -39,7 +96,7 @@ type InstallOptions struct {
|
||||
// `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.
|
||||
func (a *Attribute) Install(opt InstallOptions) error {
|
||||
func (a *Attribute) Install(opt *FilterOptions) error {
|
||||
for k, v := range a.Properties {
|
||||
var upgradeables []string
|
||||
if a.Upgradeables != nil {
|
||||
@ -65,7 +122,7 @@ func (a *Attribute) normalizeKey(relative string) string {
|
||||
// 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.
|
||||
func (a *Attribute) set(key, value string, upgradeables []string, opt InstallOptions) error {
|
||||
func (a *Attribute) set(key, value string, upgradeables []string, opt *FilterOptions) error {
|
||||
var currentValue string
|
||||
if opt.Local {
|
||||
currentValue = git.Config.FindLocal(key)
|
||||
@ -94,7 +151,7 @@ func (a *Attribute) set(key, value string, upgradeables []string, opt InstallOpt
|
||||
}
|
||||
|
||||
// Uninstall removes all properties in the path of this property.
|
||||
func (a *Attribute) Uninstall(opt InstallOptions) {
|
||||
func (a *Attribute) Uninstall(opt *FilterOptions) {
|
||||
if opt.Local {
|
||||
git.Config.UnsetLocalSection(a.Section)
|
||||
} else if opt.System {
|
||||
|
41
lfs/config.go
Normal file
41
lfs/config.go
Normal file
@ -0,0 +1,41 @@
|
||||
package lfs
|
||||
|
||||
import "github.com/git-lfs/git-lfs/config"
|
||||
|
||||
// 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)
|
||||
FetchRecentRefsDays int
|
||||
// Makes the FetchRecentRefsDays option apply to remote refs from fetch source as well (default true)
|
||||
FetchRecentRefsIncludeRemotes bool
|
||||
// number of days prior to latest commit on a ref that we'll fetch previous
|
||||
// LFS changes too (default 0 = only fetch at ref)
|
||||
FetchRecentCommitsDays int
|
||||
// Whether to always fetch recent even without --recent
|
||||
FetchRecentAlways bool
|
||||
// Number of days added to FetchRecent*; data outside combined window will be
|
||||
// deleted when prune is run. (default 3)
|
||||
PruneOffsetDays int
|
||||
// Always verify with remote before pruning
|
||||
PruneVerifyRemoteAlways bool
|
||||
// Name of remote to check for unpushed and verify checks
|
||||
PruneRemoteName string
|
||||
}
|
||||
|
||||
func NewFetchPruneConfig(git config.Environment) FetchPruneConfig {
|
||||
pruneRemote, _ := git.Get("lfs.pruneremotetocheck")
|
||||
if len(pruneRemote) == 0 {
|
||||
pruneRemote = "origin"
|
||||
}
|
||||
|
||||
return FetchPruneConfig{
|
||||
FetchRecentRefsDays: git.Int("lfs.fetchrecentrefsdays", 7),
|
||||
FetchRecentRefsIncludeRemotes: git.Bool("lfs.fetchrecentremoterefs", true),
|
||||
FetchRecentCommitsDays: git.Int("lfs.fetchrecentcommitsdays", 0),
|
||||
FetchRecentAlways: git.Bool("lfs.fetchrecentalways", false),
|
||||
PruneOffsetDays: git.Int("lfs.pruneoffsetdays", 3),
|
||||
PruneVerifyRemoteAlways: git.Bool("lfs.pruneverifyremotealways", false),
|
||||
PruneRemoteName: pruneRemote,
|
||||
}
|
||||
}
|
42
lfs/config_test.go
Normal file
42
lfs/config_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package lfs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFetchPruneConfigDefault(t *testing.T) {
|
||||
cfg := config.NewFrom(config.Values{})
|
||||
fp := NewFetchPruneConfig(cfg.Git)
|
||||
|
||||
assert.Equal(t, 7, fp.FetchRecentRefsDays)
|
||||
assert.Equal(t, 0, fp.FetchRecentCommitsDays)
|
||||
assert.Equal(t, 3, fp.PruneOffsetDays)
|
||||
assert.True(t, fp.FetchRecentRefsIncludeRemotes)
|
||||
assert.Equal(t, 3, fp.PruneOffsetDays)
|
||||
assert.Equal(t, "origin", fp.PruneRemoteName)
|
||||
assert.False(t, fp.PruneVerifyRemoteAlways)
|
||||
}
|
||||
|
||||
func TestFetchPruneConfigCustom(t *testing.T) {
|
||||
cfg := config.NewFrom(config.Values{
|
||||
Git: map[string][]string{
|
||||
"lfs.fetchrecentrefsdays": []string{"12"},
|
||||
"lfs.fetchrecentremoterefs": []string{"false"},
|
||||
"lfs.fetchrecentcommitsdays": []string{"9"},
|
||||
"lfs.pruneoffsetdays": []string{"30"},
|
||||
"lfs.pruneverifyremotealways": []string{"true"},
|
||||
"lfs.pruneremotetocheck": []string{"upstream"},
|
||||
},
|
||||
})
|
||||
fp := NewFetchPruneConfig(cfg.Git)
|
||||
|
||||
assert.Equal(t, 12, fp.FetchRecentRefsDays)
|
||||
assert.Equal(t, 9, fp.FetchRecentCommitsDays)
|
||||
assert.False(t, fp.FetchRecentRefsIncludeRemotes)
|
||||
assert.Equal(t, 30, fp.PruneOffsetDays)
|
||||
assert.Equal(t, "upstream", fp.PruneRemoteName)
|
||||
assert.True(t, fp.PruneVerifyRemoteAlways)
|
||||
}
|
49
lfs/hook.go
49
lfs/hook.go
@ -8,9 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/git-lfs/git-lfs/tools"
|
||||
"github.com/rubyist/tracerx"
|
||||
)
|
||||
@ -26,15 +23,32 @@ var (
|
||||
type Hook struct {
|
||||
Type string
|
||||
Contents string
|
||||
Upgradeables []string
|
||||
Dir string
|
||||
upgradeables []string
|
||||
}
|
||||
|
||||
func LoadHooks(hookDir string) []*Hook {
|
||||
return []*Hook{
|
||||
NewStandardHook("pre-push", hookDir, []string{
|
||||
"#!/bin/sh\ngit lfs push --stdin $*",
|
||||
"#!/bin/sh\ngit lfs push --stdin \"$@\"",
|
||||
"#!/bin/sh\ngit lfs pre-push \"$@\"",
|
||||
"#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 0; }\ngit lfs pre-push \"$@\"",
|
||||
"#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 2; }\ngit lfs pre-push \"$@\"",
|
||||
}),
|
||||
NewStandardHook("post-checkout", hookDir, []string{}),
|
||||
NewStandardHook("post-commit", hookDir, []string{}),
|
||||
NewStandardHook("post-merge", hookDir, []string{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewStandardHook creates a new hook using the template script calling 'git lfs theType'
|
||||
func NewStandardHook(theType string, upgradeables []string) *Hook {
|
||||
func NewStandardHook(theType, hookDir string, upgradeables []string) *Hook {
|
||||
return &Hook{
|
||||
Type: theType,
|
||||
Contents: strings.Replace(hookBaseContent, "{{Command}}", theType, -1),
|
||||
Upgradeables: upgradeables,
|
||||
Dir: hookDir,
|
||||
upgradeables: upgradeables,
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,20 +61,7 @@ func (h *Hook) Exists() bool {
|
||||
// Path returns the desired (or actual, if installed) location where this hook
|
||||
// should be installed. It returns an absolute path in all cases.
|
||||
func (h *Hook) Path() string {
|
||||
return filepath.Join(h.Dir(), h.Type)
|
||||
}
|
||||
|
||||
// Dir returns the directory used by LFS for storing Git hooks. By default, it
|
||||
// will return the hooks/ sub-directory of the local repository's .git
|
||||
// directory. If `core.hooksPath` is configured and supported (Git verison is
|
||||
// greater than "2.9.0"), it will return that instead.
|
||||
func (h *Hook) Dir() string {
|
||||
customHooksSupported := git.Config.IsGitVersionAtLeast("2.9.0")
|
||||
if hp, ok := config.Config.Git.Get("core.hooksPath"); ok && customHooksSupported {
|
||||
return hp
|
||||
}
|
||||
|
||||
return filepath.Join(config.LocalGitDir, "hooks")
|
||||
return filepath.Join(h.Dir, h.Type)
|
||||
}
|
||||
|
||||
// Install installs this Git hook on disk, or upgrades it if it does exist, and
|
||||
@ -70,7 +71,7 @@ func (h *Hook) Dir() string {
|
||||
func (h *Hook) Install(force bool) error {
|
||||
msg := fmt.Sprintf("Install hook: %s, force=%t, path=%s", h.Type, force, h.Path())
|
||||
|
||||
if err := os.MkdirAll(h.Dir(), 0755); err != nil {
|
||||
if err := os.MkdirAll(h.Dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -110,10 +111,6 @@ func (h *Hook) Upgrade() error {
|
||||
// Uninstall removes the hook on disk so long as it matches the current version,
|
||||
// or any of the past versions of this hook.
|
||||
func (h *Hook) Uninstall() error {
|
||||
if !InRepo() {
|
||||
return errors.New("Not in a git repository")
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Uninstall hook: %s, path=%s", h.Type, h.Path())
|
||||
|
||||
match, err := h.matchesCurrent()
|
||||
@ -151,7 +148,7 @@ func (h *Hook) matchesCurrent() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, u := range h.Upgradeables {
|
||||
for _, u := range h.upgradeables {
|
||||
if u == contents {
|
||||
return true, nil
|
||||
}
|
||||
|
16
lfs/lfs.go
16
lfs/lfs.go
@ -78,15 +78,15 @@ func Environ(cfg *config.Configuration, manifest *tq.Manifest) []string {
|
||||
ultransfers := manifest.GetUploadAdapterNames()
|
||||
sort.Strings(ultransfers)
|
||||
|
||||
fetchPruneConfig := cfg.FetchPruneConfig()
|
||||
storageConfig := cfg.StorageConfig()
|
||||
fetchPruneConfig := NewFetchPruneConfig(cfg.Git)
|
||||
storageConfig := localstorage.NewConfig(cfg)
|
||||
|
||||
env = append(env,
|
||||
fmt.Sprintf("LocalWorkingDir=%s", config.LocalWorkingDir),
|
||||
fmt.Sprintf("LocalGitDir=%s", config.LocalGitDir),
|
||||
fmt.Sprintf("LocalGitStorageDir=%s", config.LocalGitStorageDir),
|
||||
fmt.Sprintf("LocalWorkingDir=%s", cfg.LocalWorkingDir()),
|
||||
fmt.Sprintf("LocalGitDir=%s", cfg.LocalGitDir()),
|
||||
fmt.Sprintf("LocalGitStorageDir=%s", cfg.LocalGitStorageDir()),
|
||||
fmt.Sprintf("LocalMediaDir=%s", LocalMediaDir()),
|
||||
fmt.Sprintf("LocalReferenceDir=%s", config.LocalReferenceDir),
|
||||
fmt.Sprintf("LocalReferenceDir=%s", cfg.LocalReferenceDir()),
|
||||
fmt.Sprintf("TempDir=%s", TempDir()),
|
||||
fmt.Sprintf("ConcurrentTransfers=%d", api.ConcurrentTransfers),
|
||||
fmt.Sprintf("TusTransfers=%v", cfg.TusTransfersAllowed()),
|
||||
@ -125,10 +125,6 @@ func Environ(cfg *config.Configuration, manifest *tq.Manifest) []string {
|
||||
return env
|
||||
}
|
||||
|
||||
func InRepo() bool {
|
||||
return config.LocalGitDir != ""
|
||||
}
|
||||
|
||||
func ClearTempObjects() error {
|
||||
if localstorage.Objects() == nil {
|
||||
return nil
|
||||
|
123
lfs/setup.go
123
lfs/setup.go
@ -1,123 +0,0 @@
|
||||
package lfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/git-lfs/git-lfs/tools"
|
||||
)
|
||||
|
||||
var (
|
||||
// prePushHook invokes `git lfs pre-push` at the pre-push phase.
|
||||
prePushHook = NewStandardHook("pre-push", []string{
|
||||
"#!/bin/sh\ngit lfs push --stdin $*",
|
||||
"#!/bin/sh\ngit lfs push --stdin \"$@\"",
|
||||
"#!/bin/sh\ngit lfs pre-push \"$@\"",
|
||||
"#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 0; }\ngit lfs pre-push \"$@\"",
|
||||
"#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 2; }\ngit lfs pre-push \"$@\"",
|
||||
})
|
||||
// postCheckoutHook invokes `git lfs post-checkout`
|
||||
postCheckoutHook = NewStandardHook("post-checkout", []string{})
|
||||
postCommitHook = NewStandardHook("post-commit", []string{})
|
||||
postMergeHook = NewStandardHook("post-merge", []string{})
|
||||
|
||||
hooks = []*Hook{
|
||||
prePushHook,
|
||||
postCheckoutHook,
|
||||
postCommitHook,
|
||||
postMergeHook,
|
||||
}
|
||||
|
||||
upgradeables = 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",
|
||||
},
|
||||
}
|
||||
|
||||
filters = &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,
|
||||
}
|
||||
|
||||
passFilters = &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,
|
||||
}
|
||||
)
|
||||
|
||||
// Get user-readable manual install steps for hooks
|
||||
func GetHookInstallSteps() string {
|
||||
steps := make([]string, 0, len(hooks))
|
||||
for _, h := range hooks {
|
||||
steps = append(steps, fmt.Sprintf(
|
||||
"Add the following to .git/hooks/%s:\n\n%s",
|
||||
h.Type, tools.Indent(h.Contents)))
|
||||
}
|
||||
|
||||
return strings.Join(steps, "\n\n")
|
||||
}
|
||||
|
||||
// InstallHooks installs all hooks in the `hooks` var.
|
||||
func InstallHooks(force bool) error {
|
||||
for _, h := range hooks {
|
||||
if err := h.Install(force); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UninstallHooks removes all hooks in range of the `hooks` var.
|
||||
func UninstallHooks() error {
|
||||
for _, h := range hooks {
|
||||
if err := h.Uninstall(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallFilters installs filters necessary for git-lfs to process normal git
|
||||
// operations. Currently, that list includes:
|
||||
// - smudge filter
|
||||
// - clean filter
|
||||
//
|
||||
// An error will be returned if a filter is unable to be set, or if the required
|
||||
// filters were not present.
|
||||
func InstallFilters(opt InstallOptions, passThrough bool) error {
|
||||
if passThrough {
|
||||
return passFilters.Install(opt)
|
||||
}
|
||||
return filters.Install(opt)
|
||||
}
|
||||
|
||||
// UninstallFilters proxies into the Uninstall method on the Filters type to
|
||||
// remove all installed filters.
|
||||
func UninstallFilters(opt InstallOptions) error {
|
||||
filters.Uninstall(opt)
|
||||
return nil
|
||||
}
|
12
lfs/util.go
12
lfs/util.go
@ -95,8 +95,8 @@ type PathConverter interface {
|
||||
// current working dir. Useful when needing to calling git with results from a rooted command,
|
||||
// but the user is in a subdir of their repo
|
||||
// Pass in a channel which you will fill with relative files & receive a channel which will get results
|
||||
func NewRepoToCurrentPathConverter() (PathConverter, error) {
|
||||
r, c, p, err := pathConverterArgs()
|
||||
func NewRepoToCurrentPathConverter(cfg *config.Configuration) (PathConverter, error) {
|
||||
r, c, p, err := pathConverterArgs(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,8 +133,8 @@ func (p *repoToCurrentPathConverter) Convert(filename string) string {
|
||||
// relative to the repo root. Useful when calling git with arguments that requires them
|
||||
// to be rooted but the user is in a subdir of their repo & expects to use relative args
|
||||
// Pass in a channel which you will fill with relative files & receive a channel which will get results
|
||||
func NewCurrentToRepoPathConverter() (PathConverter, error) {
|
||||
r, c, p, err := pathConverterArgs()
|
||||
func NewCurrentToRepoPathConverter(cfg *config.Configuration) (PathConverter, error) {
|
||||
r, c, p, err := pathConverterArgs(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -172,13 +172,13 @@ func (p *currentToRepoPathConverter) Convert(filename string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func pathConverterArgs() (string, string, bool, error) {
|
||||
func pathConverterArgs(cfg *config.Configuration) (string, string, bool, error) {
|
||||
currDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", "", false, fmt.Errorf("Unable to get working dir: %v", err)
|
||||
}
|
||||
currDir = tools.ResolveSymlinks(currDir)
|
||||
return config.LocalWorkingDir, currDir, config.LocalWorkingDir == currDir, nil
|
||||
return cfg.LocalWorkingDir(), currDir, cfg.LocalWorkingDir() == currDir, nil
|
||||
}
|
||||
|
||||
// Are we running on Windows? Need to handle some extra path shenanigans
|
||||
|
@ -37,8 +37,8 @@ type credsConfig struct {
|
||||
//
|
||||
// It returns an error if any configuration was invalid, or otherwise
|
||||
// un-useable.
|
||||
func getCredentialHelper(cfg *config.Configuration) (CredentialHelper, error) {
|
||||
ccfg, err := getCredentialConfig(cfg)
|
||||
func getCredentialHelper(osEnv, gitEnv config.Environment) (CredentialHelper, error) {
|
||||
ccfg, err := getCredentialConfig(osEnv, gitEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -71,13 +71,20 @@ func getCredentialHelper(cfg *config.Configuration) (CredentialHelper, error) {
|
||||
|
||||
// getCredentialConfig parses a *credsConfig given the OS and Git
|
||||
// configurations.
|
||||
func getCredentialConfig(cfg *config.Configuration) (*credsConfig, error) {
|
||||
what := &credsConfig{
|
||||
Cached: cfg.Git.Bool("lfs.cachecredentials", true),
|
||||
func getCredentialConfig(o, g config.Environment) (*credsConfig, error) {
|
||||
askpass, ok := o.Get("GIT_ASKPASS")
|
||||
if !ok {
|
||||
askpass, ok = g.Get("core.askpass")
|
||||
}
|
||||
|
||||
if err := cfg.Unmarshal(what); err != nil {
|
||||
return nil, err
|
||||
if !ok {
|
||||
askpass, ok = o.Get("SSH_ASKPASS")
|
||||
}
|
||||
helper, _ := g.Get("credential.helper")
|
||||
what := &credsConfig{
|
||||
AskPass: askpass,
|
||||
Helper: helper,
|
||||
Cached: g.Bool("lfs.cachecredentials", true),
|
||||
SkipPrompt: o.Bool("GIT_TERMINAL_PROMPT", false),
|
||||
}
|
||||
|
||||
return what, nil
|
||||
|
@ -66,8 +66,7 @@ func NewClient(osEnv Env, gitEnv Env) (*Client, error) {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("bad netrc file %s", netrcfile))
|
||||
}
|
||||
|
||||
creds, err := getCredentialHelper(&config.Configuration{
|
||||
Os: osEnv, Git: gitEnv})
|
||||
creds, err := getCredentialHelper(osEnv, gitEnv)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot find credential helper(s)")
|
||||
}
|
||||
|
@ -27,16 +27,15 @@ func Objects() *LocalStorage {
|
||||
return objects
|
||||
}
|
||||
|
||||
func InitStorage() error {
|
||||
if len(config.LocalGitStorageDir) == 0 || len(config.LocalGitDir) == 0 {
|
||||
func InitStorage(cfg *config.Configuration) error {
|
||||
if len(cfg.LocalGitStorageDir()) == 0 || len(cfg.LocalGitDir()) == 0 {
|
||||
return notInRepoErr
|
||||
}
|
||||
|
||||
cfg := config.Config.StorageConfig()
|
||||
|
||||
TempDir = filepath.Join(cfg.LfsStorageDir, "tmp") // temp files per worktree
|
||||
storCfg := NewConfig(cfg)
|
||||
TempDir = filepath.Join(storCfg.LfsStorageDir, "tmp") // temp files per worktree
|
||||
objs, err := NewStorage(
|
||||
filepath.Join(cfg.LfsStorageDir, "objects"),
|
||||
filepath.Join(storCfg.LfsStorageDir, "objects"),
|
||||
filepath.Join(TempDir, "objects"),
|
||||
)
|
||||
|
||||
@ -45,16 +44,16 @@ func InitStorage() error {
|
||||
}
|
||||
|
||||
objects = objs
|
||||
config.LocalLogDir = filepath.Join(objs.RootDir, "logs")
|
||||
if err := os.MkdirAll(config.LocalLogDir, localLogDirPerms); err != nil {
|
||||
cfg.SetLocalLogDir(filepath.Join(objs.RootDir, "logs"))
|
||||
if err := os.MkdirAll(cfg.LocalLogDir(), localLogDirPerms); err != nil {
|
||||
return errors.Wrap(err, "create log dir")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitStorageOrFail() {
|
||||
if err := InitStorage(); err != nil {
|
||||
func InitStorageOrFail(cfg *config.Configuration) {
|
||||
if err := InitStorage(cfg); err != nil {
|
||||
if err == notInRepoErr {
|
||||
return
|
||||
}
|
||||
@ -64,9 +63,9 @@ func InitStorageOrFail() {
|
||||
}
|
||||
}
|
||||
|
||||
func ResolveDirs() {
|
||||
config.ResolveGitBasicDirs()
|
||||
InitStorageOrFail()
|
||||
func ResolveDirs(cfg *config.Configuration) {
|
||||
cfg.ResolveGitBasicDirs()
|
||||
InitStorageOrFail(cfg)
|
||||
}
|
||||
|
||||
func TempFile(prefix string) (*os.File, error) {
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -55,6 +57,25 @@ func (s *LocalStorage) BuildObjectPath(oid string) (string, error) {
|
||||
return filepath.Join(dir, oid), nil
|
||||
}
|
||||
|
||||
// Storage configuration
|
||||
type Configuration struct {
|
||||
LfsStorageDir string
|
||||
}
|
||||
|
||||
func NewConfig(cfg *config.Configuration) (c Configuration) {
|
||||
dir, _ := cfg.Git.Get("lfs.storage")
|
||||
if len(dir) == 0 {
|
||||
dir = "lfs"
|
||||
}
|
||||
|
||||
if filepath.IsAbs(dir) {
|
||||
c.LfsStorageDir = dir
|
||||
} else {
|
||||
c.LfsStorageDir = filepath.Join(cfg.LocalGitStorageDir(), dir)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func localObjectDir(s *LocalStorage, oid string) string {
|
||||
return filepath.Join(s.RootDir, oid[0:2], oid[2:4])
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/git-lfs/git-lfs/config"
|
||||
"github.com/git-lfs/git-lfs/errors"
|
||||
"github.com/git-lfs/git-lfs/git"
|
||||
"github.com/git-lfs/git-lfs/lfs"
|
||||
@ -83,7 +84,7 @@ func (r *Repo) Pushd() {
|
||||
r.callback.Fatalf("Can't chdir %v", err)
|
||||
}
|
||||
r.popDir = oldwd
|
||||
localstorage.ResolveDirs()
|
||||
localstorage.ResolveDirs(config.Config)
|
||||
}
|
||||
|
||||
func (r *Repo) Popd() {
|
||||
@ -220,7 +221,7 @@ func (infile *FileInput) writeLFSPointer(inputData io.Reader) (*lfs.Pointer, err
|
||||
|
||||
// this only created the temp file, move to final location
|
||||
tmpfile := cleaned.Filename
|
||||
storageOnce.Do(localstorage.ResolveDirs)
|
||||
storageOnce.Do(func() { localstorage.ResolveDirs(config.Config) })
|
||||
mediafile, err := lfs.LocalMediaPath(cleaned.Oid)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "local media path")
|
||||
|
Loading…
Reference in New Issue
Block a user