mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-28 20:39:40 -08:00
fix(git): better cross-platform path resolution and tests
The simple filepath.IsAbs() is not enough on Windows, where the relative path may start with a separator and should then be taken relative to the volume name. So, introduce resolveGitPath() helper function that will do the right thing on both Windows and other systems. As a bonus, it returns paths converted to slashes for ease of use with the rest of the git segment: - Git for Windows uses only slashes. - Slashes do work as a path separator on Windows anyway. - Some tests in git segment still use (and rightfully so) slashes. Add tests for resolveGitPath() on both types of systems and fix TestEnableInWorktree using a system-dependent testing constant as a root path.
This commit is contained in:
parent
3895210ab3
commit
a2533c8f5b
|
@ -196,18 +196,14 @@ func (g *Git) hasWorktree(gitdir *environment.FileInfo) bool {
|
|||
// we need the parent folder to detect where the real .git folder is
|
||||
ind = strings.LastIndex(g.workingFolder, "/.git/modules")
|
||||
if ind > -1 {
|
||||
if !filepath.IsAbs(g.workingFolder) {
|
||||
g.rootFolder = filepath.Join(gitdir.ParentFolder, g.workingFolder)
|
||||
} else {
|
||||
g.rootFolder = g.workingFolder
|
||||
}
|
||||
g.rootFolder = resolveGitPath(gitdir.ParentFolder, g.workingFolder)
|
||||
// this might be both a worktree and a submodule, where the path would look like
|
||||
// this: path/.git/modules/module/path/worktrees/location. We cannot distinguish
|
||||
// between worktree and a module path containing the word 'worktree,' however.
|
||||
ind = strings.LastIndex(g.rootFolder, "/worktrees/")
|
||||
if ind > -1 && g.env.HasFilesInDir(g.rootFolder, "gitdir") {
|
||||
gitDir := filepath.Join(g.rootFolder, "gitdir")
|
||||
realGitFolder := filepath.Clean(g.env.FileContent(gitDir))
|
||||
realGitFolder := g.env.FileContent(gitDir)
|
||||
g.realFolder = strings.TrimSuffix(realGitFolder, ".git\n")
|
||||
g.rootFolder = g.rootFolder[:ind]
|
||||
g.workingFolder = g.rootFolder
|
||||
|
|
|
@ -57,6 +57,11 @@ func TestEnabledInWorkingDirectory(t *testing.T) {
|
|||
assert.Equal(t, fileInfo.Path, g.workingFolder)
|
||||
}
|
||||
|
||||
func TestResolveEmptyGitPath(t *testing.T) {
|
||||
base := "base"
|
||||
assert.Equal(t, base, resolveGitPath(base, ""))
|
||||
}
|
||||
|
||||
func TestEnabledInWorktree(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
|
@ -67,63 +72,59 @@ func TestEnabledInWorktree(t *testing.T) {
|
|||
ExpectedRealFolder string
|
||||
ExpectedWorkingFolder string
|
||||
ExpectedRootFolder string
|
||||
WindowsPaths bool
|
||||
}{
|
||||
{
|
||||
Case: "worktree",
|
||||
ExpectedEnabled: true,
|
||||
WorkingFolder: "/dev/.git/worktrees/folder_worktree",
|
||||
WorkingFolder: TestRootPath + "dev/.git/worktrees/folder_worktree",
|
||||
WorkingFolderAddon: "gitdir",
|
||||
WorkingFolderContent: "/dev/worktree.git\n",
|
||||
ExpectedWorkingFolder: "/dev/.git/worktrees/folder_worktree",
|
||||
ExpectedRealFolder: "/dev/worktree",
|
||||
ExpectedRootFolder: "/dev/.git",
|
||||
WorkingFolderContent: TestRootPath + "dev/worktree.git\n",
|
||||
ExpectedWorkingFolder: TestRootPath + "dev/.git/worktrees/folder_worktree",
|
||||
ExpectedRealFolder: TestRootPath + "dev/worktree",
|
||||
ExpectedRootFolder: TestRootPath + "dev/.git",
|
||||
},
|
||||
{
|
||||
Case: "submodule",
|
||||
ExpectedEnabled: true,
|
||||
WorkingFolder: "./.git/modules/submodule",
|
||||
ExpectedWorkingFolder: "/dev/.git/modules/submodule",
|
||||
ExpectedRealFolder: "/dev/.git/modules/submodule",
|
||||
ExpectedRootFolder: "/dev/.git/modules/submodule",
|
||||
WindowsPaths: true,
|
||||
ExpectedWorkingFolder: TestRootPath + "dev/.git/modules/submodule",
|
||||
ExpectedRealFolder: TestRootPath + "dev/.git/modules/submodule",
|
||||
ExpectedRootFolder: TestRootPath + "dev/.git/modules/submodule",
|
||||
},
|
||||
{
|
||||
Case: "submodule with root working folder",
|
||||
ExpectedEnabled: true,
|
||||
WorkingFolder: "/repo/.git/modules/submodule",
|
||||
ExpectedWorkingFolder: "/repo/.git/modules/submodule",
|
||||
ExpectedRealFolder: "/repo/.git/modules/submodule",
|
||||
ExpectedRootFolder: "/repo/.git/modules/submodule",
|
||||
WindowsPaths: true,
|
||||
WorkingFolder: TestRootPath + "repo/.git/modules/submodule",
|
||||
ExpectedWorkingFolder: TestRootPath + "repo/.git/modules/submodule",
|
||||
ExpectedRealFolder: TestRootPath + "repo/.git/modules/submodule",
|
||||
ExpectedRootFolder: TestRootPath + "repo/.git/modules/submodule",
|
||||
},
|
||||
{
|
||||
Case: "submodule with worktrees",
|
||||
ExpectedEnabled: true,
|
||||
WorkingFolder: "/dev/.git/modules/module/path/worktrees/location",
|
||||
WorkingFolder: TestRootPath + "dev/.git/modules/module/path/worktrees/location",
|
||||
WorkingFolderAddon: "gitdir",
|
||||
WorkingFolderContent: "/dev/worktree.git\n",
|
||||
ExpectedWorkingFolder: "/dev/.git/modules/module/path",
|
||||
ExpectedRealFolder: "/dev/worktree",
|
||||
ExpectedRootFolder: "/dev/.git/modules/module/path",
|
||||
WindowsPaths: true,
|
||||
WorkingFolderContent: TestRootPath + "dev/worktree.git\n",
|
||||
ExpectedWorkingFolder: TestRootPath + "dev/.git/modules/module/path",
|
||||
ExpectedRealFolder: TestRootPath + "dev/worktree",
|
||||
ExpectedRootFolder: TestRootPath + "dev/.git/modules/module/path",
|
||||
},
|
||||
{
|
||||
Case: "separate git dir",
|
||||
ExpectedEnabled: true,
|
||||
WorkingFolder: "/dev/separate/.git/posh",
|
||||
ExpectedWorkingFolder: "/dev/",
|
||||
ExpectedRealFolder: "/dev/",
|
||||
ExpectedRootFolder: "/dev/separate/.git/posh",
|
||||
WorkingFolder: TestRootPath + "dev/separate/.git/posh",
|
||||
ExpectedWorkingFolder: TestRootPath + "dev/",
|
||||
ExpectedRealFolder: TestRootPath + "dev/",
|
||||
ExpectedRootFolder: TestRootPath + "dev/separate/.git/posh",
|
||||
},
|
||||
}
|
||||
fileInfo := &environment.FileInfo{
|
||||
Path: "/dev/.git",
|
||||
ParentFolder: "/dev",
|
||||
Path: TestRootPath + "dev/.git",
|
||||
ParentFolder: TestRootPath + "dev",
|
||||
}
|
||||
for _, tc := range cases {
|
||||
env := new(mock.MockedEnvironment)
|
||||
env.On("FileContent", "/dev/.git").Return(fmt.Sprintf("gitdir: %s", tc.WorkingFolder))
|
||||
env.On("FileContent", TestRootPath+"dev/.git").Return(fmt.Sprintf("gitdir: %s", tc.WorkingFolder))
|
||||
env.On("FileContent", filepath.Join(tc.WorkingFolder, tc.WorkingFolderAddon)).Return(tc.WorkingFolderContent)
|
||||
env.On("HasFilesInDir", tc.WorkingFolder, tc.WorkingFolderAddon).Return(true)
|
||||
env.On("HasFilesInDir", tc.WorkingFolder, "HEAD").Return(true)
|
||||
|
@ -135,11 +136,6 @@ func TestEnabledInWorktree(t *testing.T) {
|
|||
},
|
||||
}
|
||||
assert.Equal(t, tc.ExpectedEnabled, g.hasWorktree(fileInfo), tc.Case)
|
||||
if tc.WindowsPaths {
|
||||
tc.ExpectedRealFolder = filepath.Clean(tc.ExpectedRealFolder)
|
||||
tc.ExpectedRootFolder = filepath.Clean(tc.ExpectedRootFolder)
|
||||
tc.ExpectedWorkingFolder = filepath.Clean(tc.ExpectedWorkingFolder)
|
||||
}
|
||||
assert.Equal(t, tc.ExpectedWorkingFolder, g.workingFolder, tc.Case)
|
||||
assert.Equal(t, tc.ExpectedRealFolder, g.realFolder, tc.Case)
|
||||
assert.Equal(t, tc.ExpectedRootFolder, g.rootFolder, tc.Case)
|
||||
|
|
13
src/segments/git_unix.go
Normal file
13
src/segments/git_unix.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
//go:build !windows
|
||||
|
||||
package segments
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// resolveGitPath resolves path relative to base.
|
||||
func resolveGitPath(base, path string) string {
|
||||
if filepath.IsAbs(path) {
|
||||
return path
|
||||
}
|
||||
return filepath.Join(base, path)
|
||||
}
|
36
src/segments/git_unix_test.go
Normal file
36
src/segments/git_unix_test.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
//go:build !windows
|
||||
|
||||
package segments
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const TestRootPath = "/"
|
||||
|
||||
func TestResolveGitPath(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Base string
|
||||
Path string
|
||||
Expected string
|
||||
}{
|
||||
{
|
||||
Case: "relative path",
|
||||
Base: "dir/",
|
||||
Path: "sub",
|
||||
Expected: "dir/sub",
|
||||
},
|
||||
{
|
||||
Case: "absolute path",
|
||||
Base: "/base",
|
||||
Path: "/absolute/path",
|
||||
Expected: "/absolute/path",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
assert.Equal(t, tc.Expected, resolveGitPath(tc.Base, tc.Path), tc.Case)
|
||||
}
|
||||
}
|
22
src/segments/git_windows.go
Normal file
22
src/segments/git_windows.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package segments
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// resolveGitPath resolves path relative to base.
|
||||
func resolveGitPath(base, path string) string {
|
||||
if len(path) == 0 {
|
||||
return base
|
||||
}
|
||||
if filepath.IsAbs(path) {
|
||||
return path
|
||||
}
|
||||
// Note that git on Windows uses slashes exclusively. And it's okay
|
||||
// because Windows actually accepts both directory separators. More
|
||||
// importantly, however, parts of the git segment depend on those
|
||||
// slashes.
|
||||
if path[0] == '/' {
|
||||
// path is a disk-relative path.
|
||||
return filepath.VolumeName(base) + path
|
||||
}
|
||||
return filepath.ToSlash(filepath.Join(base, path))
|
||||
}
|
42
src/segments/git_windows_test.go
Normal file
42
src/segments/git_windows_test.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
//go:build windows
|
||||
|
||||
package segments
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const TestRootPath = "C:/"
|
||||
|
||||
func TestResolveGitPath(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Base string
|
||||
Path string
|
||||
Expected string
|
||||
}{
|
||||
{
|
||||
Case: "relative path",
|
||||
Base: "dir\\",
|
||||
Path: "sub",
|
||||
Expected: "dir/sub",
|
||||
},
|
||||
{
|
||||
Case: "absolute path",
|
||||
Base: "C:\\base",
|
||||
Path: "C:/absolute/path",
|
||||
Expected: "C:/absolute/path",
|
||||
},
|
||||
{
|
||||
Case: "disk-relative path",
|
||||
Base: "C:\\base",
|
||||
Path: "/absolute/path",
|
||||
Expected: "C:/absolute/path",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
assert.Equal(t, tc.Expected, resolveGitPath(tc.Base, tc.Path), tc.Case)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue