11 "github.com/git-lfs/git-lfs/config"
12 "github.com/git-lfs/git-lfs/progress"
13 "github.com/git-lfs/git-lfs/tools"
19 PlatformWindows = Platform(iota)
20 PlatformLinux = Platform(iota)
21 PlatformOSX = Platform(iota)
22 PlatformOther = Platform(iota) // most likely a *nix variant e.g. freebsd
23 PlatformUndetermined = Platform(iota)
26 var currentPlatform = PlatformUndetermined
28 func CopyCallbackFile(event, filename string, index, totalFiles int) (progress.CopyCallback, *os.File, error) {
29 logPath, _ := config.Config.Os.Get("GIT_LFS_PROGRESS")
30 if len(logPath) == 0 || len(filename) == 0 || len(event) == 0 {
34 if !filepath.IsAbs(logPath) {
35 return nil, nil, fmt.Errorf("GIT_LFS_PROGRESS must be an absolute path")
38 cbDir := filepath.Dir(logPath)
39 if err := os.MkdirAll(cbDir, 0755); err != nil {
40 return nil, nil, wrapProgressError(err, event, logPath)
43 file, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
45 return nil, file, wrapProgressError(err, event, logPath)
50 cb := progress.CopyCallback(func(total int64, written int64, current int) error {
51 if written != prevWritten {
52 _, err := file.Write([]byte(fmt.Sprintf("%s %d/%d %d/%d %s\n", event, index, totalFiles, written, total, filename)))
55 return wrapProgressError(err, event, logPath)
64 func wrapProgressError(err error, event, filename string) error {
66 return fmt.Errorf("Error writing Git LFS %s progress to %s: %s", event, filename, err.Error())
72 var localDirSet = tools.NewStringSetFromSlice([]string{".", "./", ".\\"})
74 func GetPlatform() Platform {
75 if currentPlatform == PlatformUndetermined {
78 currentPlatform = PlatformWindows
80 currentPlatform = PlatformLinux
82 currentPlatform = PlatformOSX
84 currentPlatform = PlatformOther
87 return currentPlatform
90 type PathConverter interface {
91 Convert(string) string
94 // Convert filenames expressed relative to the root of the repo relative to the
95 // current working dir. Useful when needing to calling git with results from a rooted command,
96 // but the user is in a subdir of their repo
97 // Pass in a channel which you will fill with relative files & receive a channel which will get results
98 func NewRepoToCurrentPathConverter() (PathConverter, error) {
99 r, c, p, err := pathConverterArgs()
104 return &repoToCurrentPathConverter{
111 type repoToCurrentPathConverter struct {
117 func (p *repoToCurrentPathConverter) Convert(filename string) string {
122 abs := filepath.Join(p.repoDir, filename)
123 rel, err := filepath.Rel(p.currDir, abs)
125 // Use absolute file instead
132 // Convert filenames expressed relative to the current directory to be
133 // relative to the repo root. Useful when calling git with arguments that requires them
134 // to be rooted but the user is in a subdir of their repo & expects to use relative args
135 // Pass in a channel which you will fill with relative files & receive a channel which will get results
136 func NewCurrentToRepoPathConverter() (PathConverter, error) {
137 r, c, p, err := pathConverterArgs()
142 return ¤tToRepoPathConverter{
149 type currentToRepoPathConverter struct {
155 func (p *currentToRepoPathConverter) Convert(filename string) string {
161 if filepath.IsAbs(filename) {
162 abs = tools.ResolveSymlinks(filename)
164 abs = filepath.Join(p.currDir, filename)
166 reltoroot, err := filepath.Rel(p.repoDir, abs)
168 // Can't do this, use absolute as best fallback
175 func pathConverterArgs() (string, string, bool, error) {
176 currDir, err := os.Getwd()
178 return "", "", false, fmt.Errorf("Unable to get working dir: %v", err)
180 currDir = tools.ResolveSymlinks(currDir)
181 return config.LocalWorkingDir, currDir, config.LocalWorkingDir == currDir, nil
184 // Are we running on Windows? Need to handle some extra path shenanigans
185 func IsWindows() bool {
186 return GetPlatform() == PlatformWindows
189 func CopyFileContents(src string, dst string) error {
190 tmp, err := ioutil.TempFile(TempDir(), filepath.Base(dst))
196 os.Remove(tmp.Name())
198 in, err := os.Open(src)
203 _, err = io.Copy(tmp, in)
211 return os.Rename(tmp.Name(), dst)
214 func LinkOrCopy(src string, dst string) error {
218 err := os.Link(src, dst)
222 return CopyFileContents(src, dst)