45b0574aa4
Fix submodule lfsconfig
254 lines
6.9 KiB
Go
254 lines
6.9 KiB
Go
package lfs
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/github/git-lfs/git"
|
|
"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
|
|
)
|
|
|
|
const (
|
|
Version = "1.1.0"
|
|
tempDirPerms = 0755
|
|
localMediaDirPerms = 0755
|
|
localLogDirPerms = 0755
|
|
)
|
|
|
|
var (
|
|
LargeSizeThreshold = 5 * 1024 * 1024
|
|
TempDir = filepath.Join(os.TempDir(), "git-lfs")
|
|
GitCommit string
|
|
UserAgent string
|
|
LocalWorkingDir string
|
|
LocalGitDir string // parent of index / config / hooks etc
|
|
LocalGitStorageDir string // parent of objects/lfs (may be same as LocalGitDir but may not)
|
|
LocalMediaDir string // root of lfs objects
|
|
LocalObjectTempDir string // where temporarily downloading objects are stored
|
|
LocalLogDir string
|
|
checkedTempDir string
|
|
)
|
|
|
|
func TempFile(prefix string) (*os.File, error) {
|
|
if checkedTempDir != TempDir {
|
|
if err := os.MkdirAll(TempDir, tempDirPerms); err != nil {
|
|
return nil, err
|
|
}
|
|
checkedTempDir = TempDir
|
|
}
|
|
|
|
return ioutil.TempFile(TempDir, prefix)
|
|
}
|
|
|
|
func ResetTempDir() error {
|
|
checkedTempDir = ""
|
|
return os.RemoveAll(TempDir)
|
|
}
|
|
|
|
func localMediaDirNoCreate(sha string) string {
|
|
return filepath.Join(LocalMediaDir, sha[0:2], sha[2:4])
|
|
}
|
|
func localMediaPathNoCreate(sha string) string {
|
|
return filepath.Join(localMediaDirNoCreate(sha), sha)
|
|
}
|
|
|
|
func LocalMediaPath(sha string) (string, error) {
|
|
path := localMediaDirNoCreate(sha)
|
|
if err := os.MkdirAll(path, localMediaDirPerms); err != nil {
|
|
return "", fmt.Errorf("Error trying to create local media directory in '%s': %s", path, err)
|
|
}
|
|
|
|
return filepath.Join(path, sha), nil
|
|
}
|
|
|
|
func ObjectExistsOfSize(sha string, size int64) bool {
|
|
path := localMediaPathNoCreate(sha)
|
|
return FileExistsOfSize(path, size)
|
|
}
|
|
|
|
func Environ() []string {
|
|
osEnviron := os.Environ()
|
|
env := make([]string, 0, len(osEnviron)+7)
|
|
env = append(env,
|
|
fmt.Sprintf("LocalWorkingDir=%s", LocalWorkingDir),
|
|
fmt.Sprintf("LocalGitDir=%s", LocalGitDir),
|
|
fmt.Sprintf("LocalGitStorageDir=%s", LocalGitStorageDir),
|
|
fmt.Sprintf("LocalMediaDir=%s", LocalMediaDir),
|
|
fmt.Sprintf("TempDir=%s", TempDir),
|
|
fmt.Sprintf("ConcurrentTransfers=%d", Config.ConcurrentTransfers()),
|
|
fmt.Sprintf("BatchTransfer=%v", Config.BatchTransfer()),
|
|
)
|
|
|
|
for _, e := range osEnviron {
|
|
if !strings.Contains(e, "GIT_") {
|
|
continue
|
|
}
|
|
env = append(env, e)
|
|
}
|
|
|
|
return env
|
|
}
|
|
|
|
func InRepo() bool {
|
|
return LocalGitDir != ""
|
|
}
|
|
|
|
func ResolveDirs() {
|
|
var err error
|
|
LocalGitDir, LocalWorkingDir, err = git.GitAndRootDirs()
|
|
if err == nil {
|
|
LocalGitStorageDir = resolveGitStorageDir(LocalGitDir)
|
|
LocalMediaDir = filepath.Join(LocalGitStorageDir, "lfs", "objects")
|
|
LocalLogDir = filepath.Join(LocalMediaDir, "logs")
|
|
TempDir = filepath.Join(LocalGitDir, "lfs", "tmp") // temp files per worktree
|
|
if err := os.MkdirAll(LocalMediaDir, localMediaDirPerms); err != nil {
|
|
panic(fmt.Errorf("Error trying to create objects directory in '%s': %s", LocalMediaDir, err))
|
|
}
|
|
|
|
if err := os.MkdirAll(LocalLogDir, localLogDirPerms); err != nil {
|
|
panic(fmt.Errorf("Error trying to create log directory in '%s': %s", LocalLogDir, err))
|
|
}
|
|
|
|
LocalObjectTempDir = filepath.Join(TempDir, "objects")
|
|
if err := os.MkdirAll(LocalObjectTempDir, tempDirPerms); err != nil {
|
|
panic(fmt.Errorf("Error trying to create temp directory in '%s': %s", TempDir, err))
|
|
}
|
|
} else {
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
tracerx.DefaultKey = "GIT"
|
|
tracerx.Prefix = "trace git-lfs: "
|
|
|
|
ResolveDirs()
|
|
|
|
gitCommit := ""
|
|
if len(GitCommit) > 0 {
|
|
gitCommit = "; git " + GitCommit
|
|
}
|
|
UserAgent = fmt.Sprintf("git-lfs/%s (GitHub; %s %s; go %s%s)",
|
|
Version,
|
|
runtime.GOOS,
|
|
runtime.GOARCH,
|
|
strings.Replace(runtime.Version(), "go", "", 1),
|
|
gitCommit,
|
|
)
|
|
}
|
|
|
|
func processGitRedirectFile(file, prefix string) (string, error) {
|
|
data, err := ioutil.ReadFile(file)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
contents := string(data)
|
|
var dir string
|
|
if len(prefix) > 0 {
|
|
if !strings.HasPrefix(contents, prefix) {
|
|
// Prefix required & not found
|
|
return "", nil
|
|
}
|
|
dir = strings.TrimSpace(contents[len(prefix):])
|
|
} else {
|
|
dir = strings.TrimSpace(contents)
|
|
}
|
|
|
|
if !filepath.IsAbs(dir) {
|
|
// The .git file contains a relative path.
|
|
// Create an absolute path based on the directory the .git file is located in.
|
|
dir = filepath.Join(filepath.Dir(file), dir)
|
|
}
|
|
|
|
return dir, nil
|
|
}
|
|
|
|
// From a git dir, get the location that objects are to be stored (we will store lfs alongside)
|
|
// Sometimes there is an additional level of redirect on the .git folder by way of a commondir file
|
|
// before you find object storage, e.g. 'git worktree' uses this. It redirects to gitdir either by GIT_DIR
|
|
// (during setup) or .git/git-dir: (during use), but this only contains the index etc, the objects
|
|
// are found in another git dir via 'commondir'.
|
|
func resolveGitStorageDir(gitDir string) string {
|
|
commondirpath := filepath.Join(gitDir, "commondir")
|
|
if FileExists(commondirpath) && !DirExists(filepath.Join(gitDir, "objects")) {
|
|
// no git-dir: prefix in commondir
|
|
storage, err := processGitRedirectFile(commondirpath, "")
|
|
if err == nil {
|
|
return storage
|
|
}
|
|
}
|
|
return gitDir
|
|
}
|
|
|
|
const (
|
|
gitExt = ".git"
|
|
gitPtrPrefix = "gitdir: "
|
|
)
|
|
|
|
// AllLocalObjects returns a slice of the the objects stored in the local LFS store
|
|
// This does not necessarily mean referenced by commits, just stored
|
|
// Note: reports final SHA only, extensions are ignored
|
|
func AllLocalObjects() []*Pointer {
|
|
c := AllLocalObjectsChan()
|
|
ret := make([]*Pointer, 0, 100)
|
|
for p := range c {
|
|
ret = append(ret, p)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// AllLocalObjectsChan returns a channel of all the objects stored in the local LFS store
|
|
// This does not necessarily mean referenced by commits, just stored
|
|
// You should not alter the store until this channel is closed
|
|
// Note: reports final SHA only, extensions are ignored
|
|
func AllLocalObjectsChan() <-chan *Pointer {
|
|
ret := make(chan *Pointer, chanBufSize)
|
|
|
|
go func() {
|
|
defer close(ret)
|
|
|
|
scanStorageDir(LocalMediaDir, ret)
|
|
}()
|
|
return ret
|
|
}
|
|
|
|
func scanStorageDir(dir string, c chan *Pointer) {
|
|
// ioutil.ReadDir and filepath.Walk do sorting which is unnecessary & inefficient
|
|
dirf, err := os.Open(dir)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer dirf.Close()
|
|
|
|
direntries, err := dirf.Readdir(0)
|
|
if err != nil {
|
|
tracerx.Printf("Problem with Readdir in %v: %v", dir, err)
|
|
return
|
|
}
|
|
for _, dirfi := range direntries {
|
|
if dirfi.IsDir() {
|
|
subpath := filepath.Join(dir, dirfi.Name())
|
|
scanStorageDir(subpath, c)
|
|
} else {
|
|
// Make sure it's really an object file & not .DS_Store etc
|
|
if oidRE.MatchString(dirfi.Name()) {
|
|
c <- NewPointer(dirfi.Name(), dirfi.Size(), nil)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func traceHttpReq(req *http.Request) string {
|
|
return fmt.Sprintf("%s %s", req.Method, strings.SplitN(req.URL.String(), "?", 2)[0])
|
|
}
|