From 77993158e2bd3f25a1cad3270bd41ccc0b2e7eee Mon Sep 17 00:00:00 2001 From: lnu Date: Wed, 22 Dec 2021 17:28:15 +0100 Subject: [PATCH] fix(git): use the right git executable for WSL git executable detection path conversion when needed --- src/environment.go | 4 ++++ src/environment_unix.go | 30 +++++++++++++++++++++++ src/environment_windows.go | 17 +++++++++++++ src/segment_git.go | 49 ++++++++++++++++++++++++-------------- src/segment_git_test.go | 27 ++++++++++++++------- src/segment_path_test.go | 20 ++++++++++++++++ 6 files changed, 121 insertions(+), 26 deletions(-) diff --git a/src/environment.go b/src/environment.go index 8ec42dba..89aa7973 100644 --- a/src/environment.go +++ b/src/environment.go @@ -100,12 +100,16 @@ type environmentInfo interface { doGet(url string, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error) hasParentFilePath(path string) (fileInfo *fileInfo, err error) isWsl() bool + isWsl2() bool stackCount() int getTerminalWidth() (int, error) getCachePath() string cache() cache close() logs() string + inWSLSharedDrive() bool + convertToLinuxPath(path string) string + convertToWindowsPath(path string) string } type commandCache struct { diff --git a/src/environment_unix.go b/src/environment_unix.go index d6523ae7..17fa42e0 100644 --- a/src/environment_unix.go +++ b/src/environment_unix.go @@ -5,6 +5,7 @@ package main import ( "errors" "os" + "strings" "time" "github.com/shirou/gopsutil/v3/host" @@ -33,6 +34,15 @@ func (env *environment) isWsl() bool { return env.getenv("WSL_DISTRO_NAME") != "" } +func (env *environment) isWsl2() bool { + defer env.trace(time.Now(), "isWsl2") + if !env.isWsl() { + return false + } + uname := env.getFileContent("/proc/sys/kernel/osrelease") + return strings.Contains(uname, "WSL2") +} + func (env *environment) getTerminalWidth() (int, error) { defer env.trace(time.Now(), "getTerminalWidth") width, err := terminal.Width() @@ -63,3 +73,23 @@ func (env *environment) getCachePath() string { func (env *environment) getWindowsRegistryKeyValue(path string) (*windowsRegistryValue, error) { return nil, errors.New("not implemented") } + +func (env *environment) inWSLSharedDrive() bool { + return env.isWsl() && strings.HasPrefix(env.getcwd(), "/mnt/") +} + +func (env *environment) convertToWindowsPath(path string) string { + windowsPath, err := env.runCommand("wslpath", "-w", path) + if err == nil { + return windowsPath + } + return path +} + +func (env *environment) convertToLinuxPath(path string) string { + linuxPath, err := env.runCommand("wslpath", "-u", path) + if err == nil { + return linuxPath + } + return path +} diff --git a/src/environment_windows.go b/src/environment_windows.go index d4ce5b7e..40e28da6 100644 --- a/src/environment_windows.go +++ b/src/environment_windows.go @@ -75,6 +75,11 @@ func (env *environment) isWsl() bool { return false } +func (env *environment) isWsl2() bool { + defer env.trace(time.Now(), "isWsl2") + return false +} + func (env *environment) getTerminalWidth() (int, error) { defer env.trace(time.Now(), "getTerminalWidth") handle, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) @@ -243,3 +248,15 @@ func (env *environment) getWindowsRegistryKeyValue(path string) (*windowsRegistr return nil, errors.New(errorLogMsg) } } + +func (env *environment) inWSLSharedDrive() bool { + return false +} + +func (env *environment) convertToWindowsPath(path string) string { + return path +} + +func (env *environment) convertToLinuxPath(path string) string { + return path +} diff --git a/src/segment_git.go b/src/segment_git.go index 7ecca3c9..b13583fc 100644 --- a/src/segment_git.go +++ b/src/segment_git.go @@ -45,11 +45,13 @@ type git struct { WorktreeCount int IsWorkTree bool - gitWorkingFolder string // .git working folder, can be different of root if using worktree - gitRootFolder string // .git root folder - gitWorktreeFolder string // .git real worktree path + gitWorkingFolder string // .git working folder + gitRootFolder string // .git root folder + gitRealFolder string // .git real folder(can be different from current path when in worktrees) gitCommand string + + IsWslSharedPath bool } const ( @@ -102,6 +104,10 @@ const ( ) func (g *git) enabled() bool { + // when in wsl/wsl2 and in a windows shared folder + // we must use git.exe and convert paths accordingly + // for worktrees, stashes, and path to work + g.IsWslSharedPath = g.env.inWSLSharedDrive() if !g.env.hasCommand(g.getGitCommand()) { return false } @@ -116,7 +122,8 @@ func (g *git) enabled() bool { if gitdir.isDir { g.gitWorkingFolder = gitdir.path g.gitRootFolder = gitdir.path - g.gitWorktreeFolder = strings.TrimSuffix(gitdir.path, ".git") + // convert the worktree file path to a windows one when in wsl 2 shared folder + g.gitRealFolder = strings.TrimSuffix(g.convertToWindowsPath(gitdir.path), ".git") return true } // handle worktree @@ -124,13 +131,15 @@ func (g *git) enabled() bool { dirPointer := strings.Trim(g.env.getFileContent(gitdir.path), " \r\n") matches := findNamedRegexMatch(`^gitdir: (?P.*)$`, dirPointer) if matches != nil && matches["dir"] != "" { - g.gitWorkingFolder = matches["dir"] + // if we open a worktree file in a shared wsl2 folder, we have to convert it back + // to the mounted path + g.gitWorkingFolder = g.convertToLinuxPath(matches["dir"]) // in worktrees, the path looks like this: gitdir: path/.git/worktrees/branch // strips the last .git/worktrees part // :ind+5 = index + /.git ind := strings.LastIndex(g.gitWorkingFolder, "/.git/worktrees") g.gitRootFolder = g.gitWorkingFolder[:ind+5] - g.gitWorktreeFolder = strings.TrimSuffix(g.env.getFileContent(g.gitWorkingFolder+"/gitdir"), ".git\n") + g.gitRealFolder = strings.TrimSuffix(g.env.getFileContent(g.gitWorkingFolder+"/gitdir"), ".git\n") g.IsWorkTree = true return true } @@ -278,25 +287,15 @@ func (g *git) getGitCommand() string { if len(g.gitCommand) > 0 { return g.gitCommand } - inWSL2SharedDrive := func(env environmentInfo) bool { - if !env.isWsl() { - return false - } - if !strings.HasPrefix(env.getcwd(), "/mnt/") { - return false - } - uname, _ := g.env.runCommand("uname", "-r") - return strings.Contains(uname, "WSL2") - } g.gitCommand = "git" - if g.env.getRuntimeGOOS() == windowsPlatform || inWSL2SharedDrive(g.env) { + if g.env.getRuntimeGOOS() == windowsPlatform || g.IsWslSharedPath { g.gitCommand = "git.exe" } return g.gitCommand } func (g *git) getGitCommandOutput(args ...string) string { - args = append([]string{"-C", g.gitWorktreeFolder, "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...) + args = append([]string{"-C", g.gitRealFolder, "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...) val, _ := g.env.runCommand(g.getGitCommand(), args...) return val } @@ -490,3 +489,17 @@ func (g *git) getOriginURL(upstream string) string { } return keyVal } + +func (g *git) convertToWindowsPath(path string) string { + if !g.IsWslSharedPath { + return path + } + return g.env.convertToWindowsPath(path) +} + +func (g *git) convertToLinuxPath(path string) string { + if !g.IsWslSharedPath { + return path + } + return g.env.convertToLinuxPath(path) +} diff --git a/src/segment_git_test.go b/src/segment_git_test.go index ee813a68..89907fd7 100644 --- a/src/segment_git_test.go +++ b/src/segment_git_test.go @@ -15,6 +15,7 @@ const ( func TestEnabledGitNotFound(t *testing.T) { env := new(MockedEnvironment) + env.On("inWSLSharedDrive", nil).Return(false) env.On("hasCommand", "git").Return(false) env.On("getRuntimeGOOS", nil).Return("") env.On("isWsl", nil).Return(false) @@ -28,6 +29,7 @@ func TestEnabledGitNotFound(t *testing.T) { func TestEnabledInWorkingDirectory(t *testing.T) { env := new(MockedEnvironment) + env.On("inWSLSharedDrive", nil).Return(false) env.On("hasCommand", "git").Return(true) env.On("getRuntimeGOOS", nil).Return("") env.On("isWsl", nil).Return(false) @@ -48,6 +50,7 @@ func TestEnabledInWorkingDirectory(t *testing.T) { func TestEnabledInWorkingTree(t *testing.T) { env := new(MockedEnvironment) + env.On("inWSLSharedDrive", nil).Return(false) env.On("hasCommand", "git").Return(true) env.On("getRuntimeGOOS", nil).Return("") env.On("isWsl", nil).Return(false) @@ -66,7 +69,7 @@ func TestEnabledInWorkingTree(t *testing.T) { } assert.True(t, g.enabled()) assert.Equal(t, "/dev/real_folder/.git/worktrees/folder_worktree", g.gitWorkingFolder) - assert.Equal(t, "/dev/folder_worktree", g.gitWorktreeFolder) + assert.Equal(t, "/dev/folder_worktree", g.gitRealFolder) } func TestGetGitOutputForCommand(t *testing.T) { @@ -195,6 +198,7 @@ func TestSetGitHEADContextClean(t *testing.T) { } for _, tc := range cases { env := new(MockedEnvironment) + env.On("inWSLSharedDrive", nil).Return(false) env.On("getRuntimeGOOS", nil).Return("unix") env.On("isWsl", nil).Return(false) env.mockGitCommand("", "describe", "--tags", "--exact-match") @@ -537,17 +541,18 @@ func TestShouldIgnoreRootRepository(t *testing.T) { func TestGetGitCommand(t *testing.T) { cases := []struct { - Case string - Expected string - IsWSL bool - IsWSL1 bool - GOOS string - CWD string + Case string + Expected string + IsWSL bool + IsWSL1 bool + GOOS string + CWD string + IsWslSharedPath bool }{ {Case: "On Windows", Expected: "git.exe", GOOS: windowsPlatform}, {Case: "Non Windows", Expected: "git"}, {Case: "Iside WSL2, non shared", IsWSL: true, Expected: "git"}, - {Case: "Iside WSL2, shared", Expected: "git.exe", IsWSL: true, CWD: "/mnt/bill"}, + {Case: "Iside WSL2, shared", Expected: "git.exe", IsWSL: true, IsWslSharedPath: true, CWD: "/mnt/bill"}, {Case: "Iside WSL1, shared", Expected: "git", IsWSL: true, IsWSL1: true, CWD: "/mnt/bill"}, } @@ -566,6 +571,12 @@ func TestGetGitCommand(t *testing.T) { env: env, }, } + if tc.IsWslSharedPath { + env.On("inWSLSharedDrive", nil).Return(true) + g.IsWslSharedPath = tc.IsWslSharedPath + } else { + env.On("inWSLSharedDrive", nil).Return(false) + } assert.Equal(t, tc.Expected, g.getGitCommand(), tc.Case) } } diff --git a/src/segment_path_test.go b/src/segment_path_test.go index 7b7051ba..e3ee0b25 100644 --- a/src/segment_path_test.go +++ b/src/segment_path_test.go @@ -154,6 +154,11 @@ func (env *MockedEnvironment) isWsl() bool { return args.Bool(0) } +func (env *MockedEnvironment) isWsl2() bool { + args := env.Called(nil) + return args.Bool(0) +} + func (env *MockedEnvironment) getTerminalWidth() (int, error) { args := env.Called(nil) return args.Int(0), args.Error(1) @@ -178,6 +183,21 @@ func (env *MockedEnvironment) logs() string { return args.String(0) } +func (env *MockedEnvironment) inWSLSharedDrive() bool { + args := env.Called(nil) + return args.Bool(0) +} + +func (env *MockedEnvironment) convertToWindowsPath(path string) string { + args := env.Called(nil) + return args.String(0) +} + +func (env *MockedEnvironment) convertToLinuxPath(path string) string { + args := env.Called(nil) + return args.String(0) +} + const ( homeBill = "/home/bill" homeJan = "/usr/home/jan"