fix(git): use the right git executable for WSL

git executable detection
path conversion when needed
This commit is contained in:
lnu 2021-12-22 17:28:15 +01:00 committed by Jan De Dobbeleer
parent 52d7badbac
commit 77993158e2
6 changed files with 121 additions and 26 deletions

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

View file

@ -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<dir>.*)$`, 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)
}

View file

@ -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)
}
}

View file

@ -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"