tools: add a function to canonicalize paths

We have several different places in our code that need to canonicalize
paths.  In our case, that usually involves resolving a Cygwin path to a
native Windows path, turning the path absolute, and calling
filepath.EvalSymlinks.  Let's add a function that does exactly that and
call it from the places in the git package where we do this already.

We pass an additional argument to indicate whether it's acceptable if
the path is missing, and if so, we return an absolute but
uncanonicalized path in that case.  This is useful for canonicalizing
Git environment variables which may or may not point to a valid
location; we want Git, not us, to make the decision about whether a
missing path is a problem in such a case.
This commit is contained in:
brian m. carlson 2020-09-30 21:46:02 +00:00
parent 8d234c690b
commit 19b2cd8e90
No known key found for this signature in database
GPG Key ID: 2D0C9BC12F82B3A1
2 changed files with 29 additions and 29 deletions

@ -747,20 +747,11 @@ func GitAndRootDirs() (string, string, error) {
paths := strings.Split(output, "\n")
pathLen := len(paths)
for i := 0; i < pathLen; i++ {
if paths[i] != "" {
paths[i], err = tools.TranslateCygwinPath(paths[i])
if err != nil {
return "", "", fmt.Errorf("error translating cygwin path: %s", err)
}
}
}
if pathLen == 0 {
return "", "", fmt.Errorf("bad git rev-parse output: %q", output)
}
absGitDir, err := canonicalizeDir(paths[0])
absGitDir, err := tools.CanonicalizePath(paths[0], false)
if err != nil {
return "", "", fmt.Errorf("error converting %q to absolute: %s", paths[0], err)
}
@ -769,21 +760,10 @@ func GitAndRootDirs() (string, string, error) {
return absGitDir, "", nil
}
absRootDir, err := canonicalizeDir(paths[1])
absRootDir, err := tools.CanonicalizePath(paths[1], false)
return absGitDir, absRootDir, err
}
func canonicalizeDir(path string) (string, error) {
if len(path) > 0 {
path, err := filepath.Abs(path)
if err != nil {
return "", err
}
return filepath.EvalSymlinks(path)
}
return "", nil
}
func RootDir() (string, error) {
cmd := gitNoLFS("rev-parse", "--show-toplevel")
out, err := cmd.Output()
@ -796,7 +776,7 @@ func RootDir() (string, error) {
if err != nil {
return "", err
}
return canonicalizeDir(path)
return tools.CanonicalizePath(path, false)
}
func GitDir() (string, error) {
@ -809,11 +789,7 @@ func GitDir() (string, error) {
return "", fmt.Errorf("failed to call git rev-parse --git-dir: %v %v: %v", err, string(out), buf.String())
}
path := strings.TrimSpace(string(out))
path, err = tools.TranslateCygwinPath(path)
if err != nil {
return "", err
}
return canonicalizeDir(path)
return tools.CanonicalizePath(path, false)
}
func GitCommonDir() (string, error) {
@ -836,7 +812,7 @@ func GitCommonDir() (string, error) {
if err != nil {
return "", err
}
return canonicalizeDir(path)
return tools.CanonicalizePath(path, false)
}
// GetAllWorkTreeHEADs returns the refs that all worktrees are using as HEADs

@ -478,3 +478,27 @@ func ExecutablePermissions(perms os.FileMode) os.FileMode {
// Copy read bits to executable bits.
return perms | ((perms & 0444) >> 2)
}
// CanonicalizePath takes a path and produces a canonical absolute path,
// performing any OS- or environment-specific path transformations (within the
// limitations of the Go standard library). If the path is empty, it returns
// the empty path with no error. If missingOk is true, then if the
// canonicalized path does not exist, an absolute path is given instead.
func CanonicalizePath(path string, missingOk bool) (string, error) {
path, err := TranslateCygwinPath(path)
if err != nil {
return "", err
}
if len(path) > 0 {
path, err := filepath.Abs(path)
if err != nil {
return "", err
}
result, err := filepath.EvalSymlinks(path)
if err != nil && os.IsNotExist(err) && missingOk {
return path, nil
}
return result, err
}
return "", nil
}