Fix a bunch of issues related to checkout in non-root directories

Have to convert file paths to/from cwd-relative and root-relative:
1. cwd->root for include filter arguments (user is in cwd, ls-tree is rooted)
2. root->cwd when writing files & update-index since both are relative to cwd not root like results
This commit is contained in:
Steve Streeting 2015-07-28 18:03:15 +01:00
parent 1ac8ed6840
commit 760c7d75e1
2 changed files with 98 additions and 3 deletions

@ -21,7 +21,19 @@ var (
func checkoutCommand(cmd *cobra.Command, args []string) {
// Parameters are filters
checkoutWithIncludeExclude(args, nil)
// firstly convert any pathspecs to the root of the repo, in case this is being executed in a sub-folder
var rootedpaths = make([]string, len(args))
inchan := make(chan string, 1)
outchan, err := lfs.ConvertCwdFilesRelativeToRepo(inchan)
if err != nil {
Panic(err, "Could not checkout")
}
for _, arg := range args {
inchan <- arg
rootedpaths = append(rootedpaths, <-outchan)
}
close(inchan)
checkoutWithIncludeExclude(rootedpaths, nil)
}
func init() {
@ -78,6 +90,14 @@ func checkoutWithChan(in <-chan *lfs.WrappedPointer) {
Panic(err, "Could not update the index")
}
// Get a converter from repo-relative to cwd-relative
// Since writing data & calling git update-index must be relative to cwd
repopathchan := make(chan string, 1)
cwdpathchan, err := lfs.ConvertRepoFilesRelativeToCwd(repopathchan)
if err != nil {
Panic(err, "Could not convert file paths")
}
// As files come in, write them to the wd and update the index
for pointer := range in {
@ -96,13 +116,16 @@ func checkoutWithChan(in <-chan *lfs.WrappedPointer) {
continue
}
// OK now we can (over)write the file content
err = lfs.PointerSmudgeToFile(pointer.Name, pointer.Pointer, nil)
repopathchan <- pointer.Name
cwdfilepath := <-cwdpathchan
err = lfs.PointerSmudgeToFile(cwdfilepath, pointer.Pointer, nil)
if err != nil {
Panic(err, "Could not checkout file")
}
updateIdxStdin.Write([]byte(pointer.Name + "\n"))
updateIdxStdin.Write([]byte(cwdfilepath + "\n"))
}
close(repopathchan)
updateIdxStdin.Close()
if err := cmd.Wait(); err != nil {

@ -173,6 +173,78 @@ func GetPlatform() Platform {
return currentPlatform
}
// 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
func ConvertRepoFilesRelativeToCwd(repochan <-chan string) (<-chan string, error) {
wd, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("Unable to get working dir: %v", err)
}
// Early-out if working dir is root dir, same result
passthrough := false
if LocalWorkingDir == wd {
passthrough = true
}
outchan := make(chan string, 1)
go func() {
for f := range repochan {
if passthrough {
outchan <- f
continue
}
abs := filepath.Join(LocalWorkingDir, f)
rel, err := filepath.Rel(wd, abs)
if err != nil {
// Use absolute file instead
outchan <- abs
} else {
outchan <- rel
}
}
close(outchan)
}()
return outchan, nil
}
// 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
func ConvertCwdFilesRelativeToRepo(cwdchan <-chan string) (<-chan string, error) {
curdir, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("Could not retrieve current directory: %v", err)
}
outchan := make(chan string, 1)
go func() {
for p := range cwdchan {
var abs string
if filepath.IsAbs(p) {
abs = p
} else {
abs = filepath.Join(curdir, p)
}
reltoroot, err := filepath.Rel(LocalWorkingDir, abs)
if err != nil {
// Can't do this, use absolute as best fallback
outchan <- abs
} else {
outchan <- reltoroot
}
}
close(outchan)
}()
return outchan, nil
}
// Are we running on Windows? Need to handle some extra path shenanigans
func IsWindows() bool {
return GetPlatform() == PlatformWindows