Add test for opening lazygit in the worktree of a bare repo

This commit is contained in:
Jesse Duffield 2023-07-27 21:52:24 +10:00
parent e874f94cf8
commit 2b24c15938
5 changed files with 109 additions and 28 deletions

View file

@ -1,6 +1,7 @@
package commands package commands
import ( import (
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -66,13 +67,16 @@ func NewGitCommand(
return nil, err return nil, err
} }
repo, err := setupRepository(gogit.PlainOpenWithOptions, gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true}, cmn.Tr.GitconfigParseErr) dotGitDir, err := findDotGitDir(os.Stat, os.ReadFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
dotGitDir, err := findDotGitDir(os.Stat, os.ReadFile) repository, err := gogit.PlainOpenWithOptions(dotGitDir, &gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true})
if err != nil { if err != nil {
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
return nil, errors.New(cmn.Tr.GitconfigParseErr)
}
return nil, err return nil, err
} }
@ -82,7 +86,7 @@ func NewGitCommand(
osCommand, osCommand,
gitConfig, gitConfig,
dotGitDir, dotGitDir,
repo, repository,
syncMutex, syncMutex,
), nil ), nil
} }
@ -218,8 +222,8 @@ func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir f
} }
} }
// resolvePath takes a path containing a symlink and returns the true path // takes a path containing a symlink and returns the true path
func resolvePath(path string) (string, error) { func resolveSymlink(path string) (string, error) {
l, err := os.Lstat(path) l, err := os.Lstat(path)
if err != nil { if err != nil {
return "", err return "", err
@ -232,27 +236,17 @@ func resolvePath(path string) (string, error) {
return filepath.EvalSymlinks(path) return filepath.EvalSymlinks(path)
} }
func setupRepository(openGitRepository func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error), options gogit.PlainOpenOptions, gitConfigParseErrorStr string) (*gogit.Repository, error) { func setupRepository(
unresolvedPath := env.GetGitDirEnv() openGitRepository func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error),
if unresolvedPath == "" { options gogit.PlainOpenOptions,
var err error gitConfigParseErrorStr string,
unresolvedPath, err = os.Getwd() path string,
if err != nil { ) (*gogit.Repository, error) {
return nil, err
}
}
path, err := resolvePath(unresolvedPath)
if err != nil {
return nil, err
}
repository, err := openGitRepository(path, &options) repository, err := openGitRepository(path, &options)
if err != nil { if err != nil {
if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) { if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) {
return nil, errors.New(gitConfigParseErrorStr) return nil, errors.New(gitConfigParseErrorStr)
} }
return nil, err return nil, err
} }
@ -260,26 +254,38 @@ func setupRepository(openGitRepository func(string, *gogit.PlainOpenOptions) (*g
} }
func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filename string) ([]byte, error)) (string, error) { func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filename string) ([]byte, error)) (string, error) {
if env.GetGitDirEnv() != "" { unresolvedPath := env.GetGitDirEnv()
return env.GetGitDirEnv(), nil if unresolvedPath == "" {
var err error
unresolvedPath, err = os.Getwd()
if err != nil {
return "", err
}
unresolvedPath = filepath.Join(unresolvedPath, ".git")
} }
f, err := stat(".git") path, err := resolveSymlink(unresolvedPath)
if err != nil {
return "", err
}
f, err := stat(path)
if err != nil { if err != nil {
return "", err return "", err
} }
if f.IsDir() { if f.IsDir() {
return ".git", nil return path, nil
} }
fileBytes, err := readFile(".git") fileBytes, err := readFile(path)
if err != nil { if err != nil {
return "", err return "", err
} }
fileContent := string(fileBytes) fileContent := string(fileBytes)
if !strings.HasPrefix(fileContent, "gitdir: ") { if !strings.HasPrefix(fileContent, "gitdir: ") {
return "", errors.New(".git is a file which suggests we are in a submodule or a worktree but the file's contents do not contain a gitdir pointing to the actual .git directory") return "", errors.New(fmt.Sprintf("%s is a file which suggests we are in a submodule or a worktree but the file's contents do not contain a gitdir pointing to the actual .git directory", path))
} }
return strings.TrimSpace(strings.TrimPrefix(fileContent, "gitdir: ")), nil return strings.TrimSpace(strings.TrimPrefix(fileContent, "gitdir: ")), nil
} }

View file

@ -46,6 +46,11 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
current = nil current = nil
continue continue
} }
if splitLine == "bare" {
current = nil
continue
}
if strings.HasPrefix(splitLine, "worktree ") { if strings.HasPrefix(splitLine, "worktree ") {
path := strings.SplitN(splitLine, " ", 2)[1] path := strings.SplitN(splitLine, " ", 2)[1]
isMain := path == currentRepoPath isMain := path == currentRepoPath

View file

@ -85,7 +85,7 @@ func (self *Shell) CreateFile(path string, content string) *Shell {
func (self *Shell) DeleteFile(path string) *Shell { func (self *Shell) DeleteFile(path string) *Shell {
fullPath := filepath.Join(self.dir, path) fullPath := filepath.Join(self.dir, path)
err := os.Remove(fullPath) err := os.RemoveAll(fullPath)
if err != nil { if err != nil {
self.fail(fmt.Sprintf("error deleting file: %s\n%s", fullPath, err)) self.fail(fmt.Sprintf("error deleting file: %s\n%s", fullPath, err))
} }
@ -328,3 +328,11 @@ func (self *Shell) CopyFile(source string, destination string) *Shell {
return self return self
} }
// NOTE: this only takes effect before running the test;
// the test will still run in the original directory
func (self *Shell) Chdir(path string) *Shell {
self.dir = filepath.Join(self.dir, path)
return self
}

View file

@ -223,6 +223,7 @@ var tests = []*components.IntegrationTest{
worktree.AddFromBranch, worktree.AddFromBranch,
worktree.AddFromBranchDetached, worktree.AddFromBranchDetached,
worktree.AddFromCommit, worktree.AddFromCommit,
worktree.BareRepo,
worktree.Bisect, worktree.Bisect,
worktree.Crud, worktree.Crud,
worktree.CustomCommand, worktree.CustomCommand,

View file

@ -0,0 +1,61 @@
package worktree
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var BareRepo = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Open lazygit in the worktree of a bare repo",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
// we're going to have a directory structure like this:
// project
// - .bare
// - repo (a worktree)
// - worktree2 (another worktree)
//
// The first repo is called 'repo' because that's the
// directory that all lazygit tests start in
shell.NewBranch("mybranch")
shell.CreateFileAndAdd("blah", "blah")
shell.Commit("initial commit")
shell.RunCommand([]string{"git", "clone", "--bare", ".", "../.bare"})
shell.DeleteFile(".git")
shell.Chdir("..")
// This is the dir we were just in (and the dir that lazygit starts in when the test runs)
// We're going to replace it with a worktree
shell.DeleteFile("repo")
shell.RunCommand([]string{"git", "--git-dir", ".bare", "worktree", "add", "-b", "repo", "repo", "mybranch"})
shell.RunCommand([]string{"git", "--git-dir", ".bare", "worktree", "add", "-b", "worktree2", "worktree2", "mybranch"})
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Branches().
Lines(
Contains("repo"),
Contains("mybranch"),
Contains("worktree2 (worktree)"),
)
t.Views().Worktrees().
Focus().
Lines(
Contains("repo").IsSelected(),
Contains("worktree2"),
).
NavigateToLine(Contains("worktree2")).
Press(keys.Universal.Select).
Lines(
Contains("worktree2").IsSelected(),
Contains("repo"),
)
},
})