2015-03-19 19:30:55 +00:00
|
|
|
package lfs
|
2014-08-06 22:04:34 +00:00
|
|
|
|
|
|
|
import (
|
2014-08-07 16:44:42 +00:00
|
|
|
"fmt"
|
2014-08-06 22:04:34 +00:00
|
|
|
"io"
|
2016-04-05 19:04:08 +00:00
|
|
|
"io/ioutil"
|
2014-08-07 16:44:42 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2015-07-27 14:09:22 +00:00
|
|
|
"runtime"
|
2018-02-16 00:33:46 +00:00
|
|
|
"strings"
|
2014-08-06 22:04:34 +00:00
|
|
|
|
2016-11-15 17:01:18 +00:00
|
|
|
"github.com/git-lfs/git-lfs/config"
|
|
|
|
"github.com/git-lfs/git-lfs/tools"
|
2016-05-06 14:04:47 +00:00
|
|
|
)
|
2014-08-06 22:04:34 +00:00
|
|
|
|
2015-07-28 09:57:12 +00:00
|
|
|
type Platform int
|
|
|
|
|
|
|
|
const (
|
|
|
|
PlatformWindows = Platform(iota)
|
|
|
|
PlatformLinux = Platform(iota)
|
|
|
|
PlatformOSX = Platform(iota)
|
|
|
|
PlatformOther = Platform(iota) // most likely a *nix variant e.g. freebsd
|
|
|
|
PlatformUndetermined = Platform(iota)
|
|
|
|
)
|
|
|
|
|
|
|
|
var currentPlatform = PlatformUndetermined
|
|
|
|
|
2018-02-16 00:33:46 +00:00
|
|
|
func join(parts ...string) string {
|
|
|
|
return strings.Join(parts, "/")
|
|
|
|
}
|
|
|
|
|
2017-11-22 02:00:50 +00:00
|
|
|
func (f *GitFilter) CopyCallbackFile(event, filename string, index, totalFiles int) (tools.CopyCallback, *os.File, error) {
|
2017-10-24 19:46:32 +00:00
|
|
|
logPath, _ := f.cfg.Os.Get("GIT_LFS_PROGRESS")
|
2014-10-06 15:40:34 +00:00
|
|
|
if len(logPath) == 0 || len(filename) == 0 || len(event) == 0 {
|
2014-08-14 16:38:25 +00:00
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
|
2014-10-06 15:40:34 +00:00
|
|
|
if !filepath.IsAbs(logPath) {
|
2015-03-19 19:30:55 +00:00
|
|
|
return nil, nil, fmt.Errorf("GIT_LFS_PROGRESS must be an absolute path")
|
2014-08-07 16:44:42 +00:00
|
|
|
}
|
|
|
|
|
2014-10-06 15:40:34 +00:00
|
|
|
cbDir := filepath.Dir(logPath)
|
2014-08-07 16:44:42 +00:00
|
|
|
if err := os.MkdirAll(cbDir, 0755); err != nil {
|
2014-10-06 15:40:34 +00:00
|
|
|
return nil, nil, wrapProgressError(err, event, logPath)
|
2014-08-07 16:44:42 +00:00
|
|
|
}
|
|
|
|
|
2014-10-06 15:40:34 +00:00
|
|
|
file, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
2014-08-07 16:44:42 +00:00
|
|
|
if err != nil {
|
2014-10-06 15:40:34 +00:00
|
|
|
return nil, file, wrapProgressError(err, event, logPath)
|
2014-08-07 16:44:42 +00:00
|
|
|
}
|
|
|
|
|
2014-08-14 15:25:55 +00:00
|
|
|
var prevWritten int64
|
2014-08-07 16:44:42 +00:00
|
|
|
|
2017-11-22 02:00:50 +00:00
|
|
|
cb := tools.CopyCallback(func(total int64, written int64, current int) error {
|
2014-08-14 15:25:55 +00:00
|
|
|
if written != prevWritten {
|
|
|
|
_, err := file.Write([]byte(fmt.Sprintf("%s %d/%d %d/%d %s\n", event, index, totalFiles, written, total, filename)))
|
2014-08-12 16:46:10 +00:00
|
|
|
file.Sync()
|
2014-08-14 15:25:55 +00:00
|
|
|
prevWritten = written
|
2014-10-06 15:40:34 +00:00
|
|
|
return wrapProgressError(err, event, logPath)
|
2014-08-07 16:44:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return cb, file, nil
|
|
|
|
}
|
|
|
|
|
2014-08-07 17:37:04 +00:00
|
|
|
func wrapProgressError(err error, event, filename string) error {
|
2015-05-12 08:54:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error writing Git LFS %s progress to %s: %s", event, filename, err.Error())
|
2014-08-07 16:44:42 +00:00
|
|
|
}
|
|
|
|
|
2015-05-12 08:54:17 +00:00
|
|
|
return nil
|
2014-08-07 16:44:42 +00:00
|
|
|
}
|
2015-07-27 14:09:22 +00:00
|
|
|
|
2016-07-07 16:16:13 +00:00
|
|
|
var localDirSet = tools.NewStringSetFromSlice([]string{".", "./", ".\\"})
|
2015-07-30 14:59:32 +00:00
|
|
|
|
2015-07-28 09:57:12 +00:00
|
|
|
func GetPlatform() Platform {
|
|
|
|
if currentPlatform == PlatformUndetermined {
|
|
|
|
switch runtime.GOOS {
|
|
|
|
case "windows":
|
|
|
|
currentPlatform = PlatformWindows
|
|
|
|
case "linux":
|
|
|
|
currentPlatform = PlatformLinux
|
|
|
|
case "darwin":
|
|
|
|
currentPlatform = PlatformOSX
|
|
|
|
default:
|
|
|
|
currentPlatform = PlatformOther
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return currentPlatform
|
|
|
|
}
|
|
|
|
|
2016-12-14 18:34:29 +00:00
|
|
|
type PathConverter interface {
|
|
|
|
Convert(string) string
|
|
|
|
}
|
|
|
|
|
2015-07-28 17:03:15 +00:00
|
|
|
// Convert filenames expressed relative to the root of the repo relative to the
|
|
|
|
// 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
|
2017-10-19 00:09:33 +00:00
|
|
|
func NewRepoToCurrentPathConverter(cfg *config.Configuration) (PathConverter, error) {
|
|
|
|
r, c, p, err := pathConverterArgs(cfg)
|
2015-07-28 17:03:15 +00:00
|
|
|
if err != nil {
|
2016-12-14 18:34:29 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &repoToCurrentPathConverter{
|
|
|
|
repoDir: r,
|
|
|
|
currDir: c,
|
|
|
|
passthrough: p,
|
|
|
|
}, nil
|
|
|
|
}
|
2015-07-28 17:03:15 +00:00
|
|
|
|
2016-12-14 18:34:29 +00:00
|
|
|
type repoToCurrentPathConverter struct {
|
|
|
|
repoDir string
|
|
|
|
currDir string
|
|
|
|
passthrough bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *repoToCurrentPathConverter) Convert(filename string) string {
|
|
|
|
if p.passthrough {
|
|
|
|
return filename
|
|
|
|
}
|
|
|
|
|
2018-02-16 00:33:46 +00:00
|
|
|
abs := join(p.repoDir, filename)
|
2016-12-14 18:34:29 +00:00
|
|
|
rel, err := filepath.Rel(p.currDir, abs)
|
|
|
|
if err != nil {
|
|
|
|
// Use absolute file instead
|
|
|
|
return abs
|
|
|
|
}
|
2018-02-19 20:13:27 +00:00
|
|
|
return filepath.ToSlash(rel)
|
2015-07-28 17:03:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convert filenames expressed relative to the current directory to be
|
|
|
|
// 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
|
2017-10-19 00:09:33 +00:00
|
|
|
func NewCurrentToRepoPathConverter(cfg *config.Configuration) (PathConverter, error) {
|
|
|
|
r, c, p, err := pathConverterArgs(cfg)
|
2015-07-28 17:03:15 +00:00
|
|
|
if err != nil {
|
2016-12-14 18:34:29 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ¤tToRepoPathConverter{
|
|
|
|
repoDir: r,
|
|
|
|
currDir: c,
|
|
|
|
passthrough: p,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type currentToRepoPathConverter struct {
|
|
|
|
repoDir string
|
|
|
|
currDir string
|
|
|
|
passthrough bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *currentToRepoPathConverter) Convert(filename string) string {
|
|
|
|
if p.passthrough {
|
|
|
|
return filename
|
|
|
|
}
|
2015-07-28 17:03:15 +00:00
|
|
|
|
2016-12-14 18:34:29 +00:00
|
|
|
var abs string
|
|
|
|
if filepath.IsAbs(filename) {
|
|
|
|
abs = tools.ResolveSymlinks(filename)
|
|
|
|
} else {
|
2018-02-16 00:33:46 +00:00
|
|
|
abs = join(p.currDir, filename)
|
2016-12-14 18:34:29 +00:00
|
|
|
}
|
|
|
|
reltoroot, err := filepath.Rel(p.repoDir, abs)
|
|
|
|
if err != nil {
|
|
|
|
// Can't do this, use absolute as best fallback
|
|
|
|
return abs
|
|
|
|
}
|
2018-02-19 20:13:27 +00:00
|
|
|
return filepath.ToSlash(reltoroot)
|
2016-12-14 18:34:29 +00:00
|
|
|
}
|
2015-07-28 17:03:15 +00:00
|
|
|
|
2017-10-19 00:09:33 +00:00
|
|
|
func pathConverterArgs(cfg *config.Configuration) (string, string, bool, error) {
|
2016-12-14 18:34:29 +00:00
|
|
|
currDir, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return "", "", false, fmt.Errorf("Unable to get working dir: %v", err)
|
|
|
|
}
|
|
|
|
currDir = tools.ResolveSymlinks(currDir)
|
2017-10-19 00:09:33 +00:00
|
|
|
return cfg.LocalWorkingDir(), currDir, cfg.LocalWorkingDir() == currDir, nil
|
2015-07-28 17:03:15 +00:00
|
|
|
}
|
|
|
|
|
2015-07-27 14:09:22 +00:00
|
|
|
// Are we running on Windows? Need to handle some extra path shenanigans
|
|
|
|
func IsWindows() bool {
|
2015-07-28 09:57:12 +00:00
|
|
|
return GetPlatform() == PlatformWindows
|
2015-07-27 14:09:22 +00:00
|
|
|
}
|
2015-07-31 10:15:31 +00:00
|
|
|
|
2017-10-25 00:59:36 +00:00
|
|
|
func CopyFileContents(cfg *config.Configuration, src string, dst string) error {
|
|
|
|
tmp, err := ioutil.TempFile(cfg.TempDir(), filepath.Base(dst))
|
2016-04-05 19:04:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
tmp.Close()
|
2016-04-05 19:34:23 +00:00
|
|
|
os.Remove(tmp.Name())
|
2016-04-05 19:04:08 +00:00
|
|
|
}()
|
2016-01-31 21:06:40 +00:00
|
|
|
in, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer in.Close()
|
2016-04-05 19:04:08 +00:00
|
|
|
_, err = io.Copy(tmp, in)
|
2016-01-31 21:06:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-04-05 19:34:23 +00:00
|
|
|
err = tmp.Close()
|
2016-01-31 21:06:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-04-05 19:04:08 +00:00
|
|
|
return os.Rename(tmp.Name(), dst)
|
2016-01-31 21:06:40 +00:00
|
|
|
}
|
|
|
|
|
2017-10-25 00:59:36 +00:00
|
|
|
func LinkOrCopy(cfg *config.Configuration, src string, dst string) error {
|
2016-04-05 19:04:08 +00:00
|
|
|
if src == dst {
|
|
|
|
return nil
|
|
|
|
}
|
2016-01-31 21:06:40 +00:00
|
|
|
err := os.Link(src, dst)
|
|
|
|
if err == nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-10-25 00:59:36 +00:00
|
|
|
return CopyFileContents(cfg, src, dst)
|
2016-01-31 21:06:40 +00:00
|
|
|
}
|