From 760c7d75e1cb4ea95f2a80f0ecf6d45620d56624 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 28 Jul 2015 18:03:15 +0100 Subject: [PATCH] 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 --- commands/command_checkout.go | 29 +++++++++++++-- lfs/util.go | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/commands/command_checkout.go b/commands/command_checkout.go index 305a2a2d..e91d7ace 100644 --- a/commands/command_checkout.go +++ b/commands/command_checkout.go @@ -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 { diff --git a/lfs/util.go b/lfs/util.go index cf965448..be098be8 100644 --- a/lfs/util.go +++ b/lfs/util.go @@ -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