summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Haller <stefan@haller-berlin.de>2024-01-24 08:47:49 +0100
committerGitHub <noreply@github.com>2024-01-24 08:47:49 +0100
commitb7d4db2446c1914fb9093b7f0513136cf48fc134 (patch)
tree8cd1102b24e36b52c28199e564f7e3f65a056c56
parent74d937881edf7e3271976fa7484286c82650fb2e (diff)
parent3d9f1e02e5063e1ce24f4b9122963eaef64b7262 (diff)
Use git rev-parse to obtain repository and worktree paths (#3183)
- **PR Description** This changes GetRepoPaths() to pull information from `git rev-parse` instead of partially reimplementing git's logic for pathfinding. This change fixes issues with bare repos, esp. versioned homedir use cases, by aligning lazygit's path handling to what git itself does. I believe it also paves the way for lazygit to run from any subdirectory of a working tree with relatively minor changes. Addresses #1294 and #3175. - **Please check if the PR fulfills these requirements** * [x] Cheatsheets are up-to-date (run `go generate ./...`) * [x] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting)) * [x] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide) * [x] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation)) * [x] Docs (specifically `docs/Config.md`) have been updated if necessary * [x] You've read through your own file changes for silly mistakes etc <!-- Be sure to name your PR with an imperative e.g. 'Add worktrees view' see https://github.com/jesseduffield/lazygit/releases/tag/v0.40.0 for examples -->
-rw-r--r--.editorconfig4
-rw-r--r--pkg/commands/git.go63
-rw-r--r--pkg/commands/git_commands/commit.go1
-rw-r--r--pkg/commands/git_commands/commit_test.go15
-rw-r--r--pkg/commands/git_commands/diff.go9
-rw-r--r--pkg/commands/git_commands/repo_paths.go197
-rw-r--r--pkg/commands/git_commands/repo_paths_test.go166
-rw-r--r--pkg/commands/git_commands/stash.go1
-rw-r--r--pkg/commands/git_commands/stash_test.go11
-rw-r--r--pkg/commands/git_commands/working_tree.go2
-rw-r--r--pkg/commands/git_commands/working_tree_test.go28
-rw-r--r--pkg/commands/git_commands/worktree_loader.go33
-rw-r--r--pkg/commands/git_commands/worktree_loader_test.go37
-rw-r--r--pkg/commands/git_test.go74
-rw-r--r--pkg/commands/oscommands/cmd_obj_builder.go8
-rw-r--r--pkg/integration/components/env.go65
-rw-r--r--pkg/integration/components/runner.go34
-rw-r--r--pkg/integration/components/shell.go14
-rw-r--r--pkg/integration/components/test.go8
-rw-r--r--pkg/integration/tests/test_list.go3
-rw-r--r--pkg/integration/tests/worktree/bare_repo_worktree_config.go92
-rw-r--r--pkg/integration/tests/worktree/double_nested_linked_submodule.go93
-rw-r--r--pkg/integration/tests/worktree/symlink_into_repo_subdir.go63
-rw-r--r--pkg/tasks/tasks.go2
l---------test/.gitconfig1
25 files changed, 586 insertions, 438 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..62f9670c8
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,4 @@
+root = true
+
+[*.go]
+indent_style = tab
diff --git a/pkg/commands/git.go b/pkg/commands/git.go
index b1b04a72f..b43c8c4e5 100644
--- a/pkg/commands/git.go
+++ b/pkg/commands/git.go
@@ -2,12 +2,9 @@ package commands
import (
"os"
- "path"
- "path/filepath"
"strings"
"github.com/go-errors/errors"
- "github.com/spf13/afero"
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
@@ -15,7 +12,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/common"
- "github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -64,39 +60,14 @@ func NewGitCommand(
osCommand *oscommands.OSCommand,
gitConfig git_config.IGitConfig,
) (*GitCommand, error) {
- currentPath, err := os.Getwd()
+ repoPaths, err := git_commands.GetRepoPaths(osCommand.Cmd, version)
if err != nil {
- return nil, utils.WrapError(err)
- }
-
- // converting to forward slashes for the sake of windows (which uses backwards slashes). We want everything
- // to have forward slashes internally
- currentPath = filepath.ToSlash(currentPath)
-
- gitDir := env.GetGitDirEnv()
- if gitDir != "" {
- // we've been given the git directory explicitly so no need to navigate to it
- _, err := cmn.Fs.Stat(gitDir)
- if err != nil {
- return nil, utils.WrapError(err)
- }
- } else {
- // we haven't been given the git dir explicitly so we assume it's in the current working directory as `.git/` (or an ancestor directory)
-
- rootDirectory, err := findWorktreeRoot(cmn.Fs, currentPath)
- if err != nil {
- return nil, utils.WrapError(err)
- }
- currentPath = rootDirectory
- err = os.Chdir(rootDirectory)
- if err != nil {
- return nil, utils.WrapError(err)
- }
+ return nil, errors.Errorf("Error getting repo paths: %v", err)
}
- repoPaths, err := git_commands.GetRepoPaths(cmn.Fs, currentPath)
+ err = os.Chdir(repoPaths.WorktreePath())
if err != nil {
- return nil, errors.Errorf("Error getting repo paths: %v", err)
+ return nil, utils.WrapError(err)
}
repository, err := gogit.PlainOpenWithOptions(
@@ -208,32 +179,6 @@ func NewGitCommandAux(
}
}
-// this returns the root of the current worktree. So if you start lazygit from within
-// a subdirectory of the worktree, it will start in the context of the root of that worktree
-func findWorktreeRoot(fs afero.Fs, currentPath string) (string, error) {
- for {
- // we don't care if .git is a directory or a file: either is okay.
- _, err := fs.Stat(path.Join(currentPath, ".git"))
-
- if err == nil {
- return currentPath, nil
- }
-
- if !os.IsNotExist(err) {
- return "", utils.WrapError(err)
- }
-
- currentPath = path.Dir(currentPath)
-
- atRoot := currentPath == path.Dir(currentPath)
- if atRoot {
- // we should never really land here: the code that creates GitCommand should
- // verify we're in a git directory
- return "", errors.New("Must open lazygit in a git repository")
- }
- }
-}
-
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run()
}
diff --git a/pkg/commands/git_commands/commit.go b/pkg/commands/git_commands/commit.go
index e0b5b8a9a..dfb0b4085 100644
--- a/pkg/commands/git_commands/commit.go
+++ b/pkg/commands/git_commands/commit.go
@@ -250,6 +250,7 @@ func (self *CommitCommands) ShowCmdObj(sha string, filterPath string) oscommands
Arg(sha).
ArgIf(self.AppState.IgnoreWhitespaceInDiffView, "--ignore-all-space").
ArgIf(filterPath != "", "--", filterPath).
+ Dir(self.repoPaths.worktreePath).
ToArgv()
return self.cmd.New(cmdArgs).DontLog()
diff --git a/pkg/commands/git_commands/commit_test.go b/pkg/commands/git_commands/commit_test.go
index 0ade25a8d..a2b674eeb 100644
--- a/pkg/commands/git_commands/commit_test.go
+++ b/pkg/commands/git_commands/commit_test.go
@@ -197,7 +197,7 @@ func TestCommitShowCmdObj(t *testing.T) {
contextSize: 3,
ignoreWhitespace: false,
extDiffCmd: "",
- expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
+ expected: []string{"-C", "/path/to/worktree", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
},
{
testName: "Default case with filter path",
@@ -205,7 +205,7 @@ func TestCommitShowCmdObj(t *testing.T) {
contextSize: 3,
ignoreWhitespace: false,
extDiffCmd: "",
- expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
+ expected: []string{"-C", "/path/to/worktree", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--", "file.txt"},
},
{
testName: "Show diff with custom context size",
@@ -213,7 +213,7 @@ func TestCommitShowCmdObj(t *testing.T) {
contextSize: 77,
ignoreWhitespace: false,
extDiffCmd: "",
- expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
+ expected: []string{"-C", "/path/to/worktree", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890"},
},
{
testName: "Show diff, ignoring whitespace",
@@ -221,7 +221,7 @@ func TestCommitShowCmdObj(t *testing.T) {
contextSize: 77,
ignoreWhitespace: true,
extDiffCmd: "",
- expected: []string{"show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
+ expected: []string{"-C", "/path/to/worktree", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space"},
},
{
testName: "Show diff with external diff command",
@@ -229,7 +229,7 @@ func TestCommitShowCmdObj(t *testing.T) {
contextSize: 3,
ignoreWhitespace: false,
extDiffCmd: "difft --color=always",
- expected: []string{"-c", "diff.external=difft --color=always", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
+ expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890"},
},
}
@@ -243,7 +243,10 @@ func TestCommitShowCmdObj(t *testing.T) {
appState.DiffContextSize = s.contextSize
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
- instance := buildCommitCommands(commonDeps{userConfig: userConfig, appState: appState, runner: runner})
+ repoPaths := RepoPaths{
+ worktreePath: "/path/to/worktree",
+ }
+ instance := buildCommitCommands(commonDeps{userConfig: userConfig, appState: appState, runner: runner, repoPaths: &repoPaths})
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath).Run())
runner.CheckForMissingCalls()
diff --git a/pkg/commands/git_commands/diff.go b/pkg/commands/git_commands/diff.go
index 372939024..73b30bc48 100644
--- a/pkg/commands/git_commands/diff.go
+++ b/pkg/commands/git_commands/diff.go
@@ -14,14 +14,19 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
return self.cmd.New(
- NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
+ NewGitCmd("diff").
+ Arg("--submodule", "--no-ext-diff", "--color").
+ Arg(diffArgs...).
+ Dir(self.repoPaths.worktreePath).
+ ToArgv(),
)
}
func (self *DiffCommands) internalDiffCmdObj(diffArgs ...string) *GitCommandBuilder {
return NewGitCmd("diff").
Arg("--no-ext-diff", "--no-color").
- Arg(diffArgs...)
+ Arg(diffArgs...).
+ Dir(self.repoPaths.worktreePath)
}
func (self *DiffCommands) GetPathDiff(path string, staged bool) (string, error) {
diff --git a/pkg/commands/git_commands/repo_paths.go b/pkg/commands/git_commands/repo_paths.go
index 13cda86a4..b0e1970db 100644
--- a/pkg/commands/git_commands/repo_paths.go
+++ b/pkg/commands/git_commands/repo_paths.go
@@ -1,21 +1,18 @@
package git_commands
import (
- "fmt"
ioFs "io/fs"
- "os"
"path"
"path/filepath"
"strings"
"github.com/go-errors/errors"
- "github.com/jesseduffield/lazygit/pkg/env"
- "github.com/samber/lo"
+ "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
+ "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/spf13/afero"
)
type RepoPaths struct {
- currentPath string
worktreePath string
worktreeGitDirPath string
repoPath string
@@ -23,12 +20,7 @@ type RepoPaths struct {
repoName string
}
-// Current working directory of the program. Currently, this will always
-// be the same as WorktreePath(), but in future we may support running
-// lazygit from inside a subdirectory of the worktree.
-func (self *RepoPaths) CurrentPath() string {
- return self.currentPath
-}
+var gitPathFormatVersion GitVersion = GitVersion{2, 31, 0, ""}
// Path to the current worktree. If we're in the main worktree, this will
// be the same as RepoPath()
@@ -65,7 +57,6 @@ func (self *RepoPaths) RepoName() string {
// Returns the repo paths for a typical repo
func MockRepoPaths(currentPath string) *RepoPaths {
return &RepoPaths{
- currentPath: currentPath,
worktreePath: currentPath,
worktreeGitDirPath: path.Join(currentPath, ".git"),
repoPath: currentPath,
@@ -75,44 +66,41 @@ func MockRepoPaths(currentPath string) *RepoPaths {
}
func GetRepoPaths(
- fs afero.Fs,
- currentPath string,
-) (*RepoPaths, error) {
- return getRepoPathsAux(afero.NewOsFs(), resolveSymlink, currentPath)
-}
-
-func getRepoPathsAux(
- fs afero.Fs,
- resolveSymlinkFn func(string) (string, error),
- currentPath string,
+ cmd oscommands.ICmdObjBuilder,
+ version *GitVersion,
) (*RepoPaths, error) {
- worktreePath := currentPath
- repoGitDirPath, repoPath, err := getCurrentRepoGitDirPath(fs, resolveSymlinkFn, currentPath)
+ gitDirOutput, err := callGitRevParse(cmd, version, "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree")
if err != nil {
- return nil, errors.Errorf("failed to get repo git dir path: %v", err)
+ return nil, err
}
- var worktreeGitDirPath string
- if env.GetWorkTreeEnv() != "" {
- // This env is set when you pass --work-tree to lazygit. In that case,
- // we're not dealing with a linked work-tree, we're dealing with a 'specified'
- // worktree (for lack of a better term). In this case, the worktree has no
- // .git file and it just contains a bunch of files: it has no idea it's
- // pointed to by a bare repo. As such it does not have its own git dir within
- // the bare repo's git dir. Instead, we just use the bare repo's git dir.
- worktreeGitDirPath = repoGitDirPath
- } else {
- var err error
- worktreeGitDirPath, err = getWorktreeGitDirPath(fs, currentPath)
+ gitDirResults := strings.Split(utils.NormalizeLinefeeds(gitDirOutput), "\n")
+ worktreePath := gitDirResults[0]
+ worktreeGitDirPath := gitDirResults[1]
+ repoGitDirPath := gitDirResults[2]
+ if version.IsOlderThanVersion(&gitPathFormatVersion) {
+ repoGitDirPath, err = filepath.Abs(repoGitDirPath)
if err != nil {
- return nil, errors.Errorf("failed to get worktree git dir path: %v", err)
+ return nil, err
}
}
+ // If we're in a submodule, --show-superproject-working-tree will return
+ // a value, meaning gitDirResults will be length 4. In that case
+ // return the worktree path as the repoPath. Otherwise we're in a
+ // normal repo or a worktree so return the parent of the git common
+ // dir (repoGitDirPath)
+ isSubmodule := len(gitDirResults) == 4
+
+ var repoPath string
+ if isSubmodule {
+ repoPath = worktreePath
+ } else {
+ repoPath = path.Dir(repoGitDirPath)
+ }
repoName := path.Base(repoPath)
return &RepoPaths{
- currentPath: currentPath,
worktreePath: worktreePath,
worktreeGitDirPath: worktreeGitDirPath,
repoPath: repoPath,
@@ -121,124 +109,31 @@ func getRepoPathsAux(
}, nil
}
-// Returns the path of the git-dir for the worktree. For linked worktrees, the worktree has
-// a .git file that points to the git-dir (which itself lives in the git-dir
-// of the repo)
-func getWorktreeGitDirPath(fs afero.Fs, worktreePath string) (string, error) {
- // if .git is a file, we're in a linked worktree, otherwise we're in
- // the main worktree
- dotGitPath := path.Join(worktreePath, ".git")
- gitFileInfo, err := fs.Stat(dotGitPath)
- if err != nil {
- return "", err
- }
-
- if gitFileInfo.IsDir() {
- return dotGitPath, nil
- }
-
- return linkedWorktreeGitDirPath(fs, worktreePath)
+func callGitRevParse(
+ cmd oscommands.ICmdObjBuilder,
+ version *GitVersion,
+ gitRevArgs ...string,
+) (string, error) {
+ return callGitRevParseWithDir(cmd, version, "", gitRevArgs...)
}
-func linkedWorktreeGitDirPath(fs afero.Fs, worktreePath string) (string, error) {
- dotGitPath := path.Join(worktreePath, ".git")
- gitFileContents, err := afero.ReadFile(fs, dotGitPath)
- if err != nil {
- return "", err
- }
-
- // The file will have `gitdir: /path/to/.git/worktrees/<worktree-name>`
- gitDirLine := lo.Filter(strings.Split(string(gitFileContents), "\n"), func(line string, _ int) bool {
- return strings.HasPrefix(line, "gitdir: ")
- })
-
- if len(gitDirLine) == 0 {
- 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", dotGitPath))
- }
-
- gitDir := strings.TrimPrefix(gitDirLine[0], "gitdir: ")
-
- gitDir = filepath.Clean(gitDir)
- // For windows support
- gitDir = filepath.ToSlash(gitDir)
-
- return gitDir, nil
-}
-
-func getCurrentRepoGitDirPath(
- fs afero.Fs,
- resolveSymlinkFn func(string) (string, error),
- currentPath string,
-) (string, string, error) {
- var unresolvedGitPath string
- if env.GetGitDirEnv() != "" {
- unresolvedGitPath = env.GetGitDirEnv()
- } else {
- unresolvedGitPath = path.Join(currentPath, ".git")
+func callGitRevParseWithDir(
+ cmd oscommands.ICmdObjBuilder,
+ version *GitVersion,
+ dir string,
+ gitRevArgs ...string,
+) (string, error) {
+ gitRevParse := NewGitCmd("rev-parse").ArgIf(version.IsAtLeastVersion(&gitPathFormatVersion), "--path-format=absolute").Arg(gitRevArgs...)
+ if dir != "" {
+ gitRevParse.Dir(dir)
}
- gitPath, err := resolveSymlinkFn(unresolvedGitPath)
+ gitCmd := cmd.New(gitRevParse.ToArgv()).DontLog()
+ res, err := gitCmd.RunWithOutput()
if err != nil {
- return "", "", err
+ return "", errors.Errorf("'%s' failed: %v", gitCmd.ToString(), err)
}
-
- // check if .git is a file or a directory
- gitFileInfo, err := fs.Stat(gitPath)
- if err != nil {
- return "", "", err
- }
-
- if gitFileInfo.IsDir() {
- // must be in the main worktree
- return gitPath, path.Dir(gitPath), nil
- }
-
- // either in a submodule, or worktree
- worktreeGitPath, err := linkedWorktreeGitDirPath(fs, currentPath)
- if err != nil {
- return "", "", errors.Errorf("could not find git dir for %s: %v", currentPath, err)
- }
-
- _, err = fs.Stat(worktreeGitPath)
- if err != nil {
- if os.IsNotExist(err) {
- // hardcoding error to get around windows-specific error message
- return "", "", errors.Errorf("could not find git dir for %s. %s does not exist", currentPath, worktreeGitPath)
- }
- return "", "", errors.Errorf("could not find git dir for %s: %v", currentPath, err)
- }
-
- // confirm whether the next directory up is the worktrees directory
- parent := path.Dir(worktreeGitPath)
- if path.Base(parent) == "worktrees" {
- gitDirPath := path.Dir(parent)
- return gitDirPath, path.Dir(gitDirPath), nil
- }
-
- // Unlike worktrees, submodules can be nested arbitrarily deep, so we check
- // if the `modules` directory is anywhere up the chain.
- if strings.Contains(worktreeGitPath, "/modules/") {
- // For submodules, we just return the path directly
- return worktreeGitPath, currentPath, nil
- }
-
- // If this error causes issues, we could relax the constraint and just always
- // return the path
- return "", "", errors.Errorf("could not find git dir for %s: the path '%s' is not under `worktrees` or `modules` directories", currentPath, worktreeGitPath)
-}
-
-// takes a path containing a symlink and returns the true path
-func resolveSymlink(path string) (string, error) {
- l, err := os.Lstat(path)
- if err != nil {
- return "", err
- }
-
- if l.Mode()&os.ModeSymlink == 0 {
- return path, nil
- }
-
- return filepath.EvalSymlinks(path)
+ return strings.TrimSpace(res), nil
}
// Returns the paths of linked worktrees
diff --git a/pkg/commands/git_commands/repo_paths_test.go b/pkg/commands/git_commands/repo_paths_test.go
index 5e6275522..ae4526737 100644
--- a/pkg/commands/git_commands/repo_paths_test.go
+++ b/pkg/commands/git_commands/repo_paths_test.go
@@ -1,34 +1,50 @@
package git_commands
import (
+ "fmt"
+ "strings"
"testing"
"github.com/go-errors/errors"
- "github.com/spf13/afero"
+ "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/stretchr/testify/assert"
)
-func mockResolveSymlinkFn(p string) (string, error) { return p, nil }
+type (
+ argFn func() []string
+ errFn func(getRevParseArgs argFn) error
+)
type Scenario struct {
Name string
- BeforeFunc func(fs afero.Fs)
+ BeforeFunc func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn)
Path string
Expected *RepoPaths
- Err error
+ Err errFn
}
-func TestGetRepoPathsAux(t *testing.T) {
+func TestGetRepoPaths(t *testing.T) {
scenarios := []Scenario{
{
Name: "typical case",
- BeforeFunc: func(fs afero.Fs) {
+ BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
// setup for main worktree
- _ = fs.MkdirAll("/path/to/repo/.git", 0o755)
+ expectedOutput := []string{
+ // --show-toplevel
+ "/path/to/repo",
+ // --git-dir
+ "/path/to/repo/.git",
+ // --git-common-dir
+ "/path/to/repo/.git",
+ // --show-superproject-working-tree
+ }
+ runner.ExpectGitArgs(
+ append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
+ strings.Join(expectedOutput, "\n"),
+ nil)
},
Path: "/path/to/repo",
Expected: &RepoPaths{
- currentPath: "/path/to/repo",
worktreePath: "/path/to/repo",
worktreeGitDirPath: "/path/to/repo/.git",
repoPath: "/path/to/repo",
@@ -38,70 +54,25 @@ func TestGetRepoPathsAux(t *testing.T) {
Err: nil,
},
{
- Name: "linked worktree",
- BeforeFunc: func(fs afero.Fs) {
- // setup for linked worktree
- _ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree1", 0o755)
- _ = afero.WriteFile(fs, "/path/to/repo/worktree1/.git", []byte("gitdir: /path/to/repo/.git/worktrees/worktree1"), 0o644)
- },
- Path: "/path/to/repo/worktree1",
- Expected: &RepoPaths{
- currentPath: "/path/to/repo/worktree1",
- worktreePath: "/path/to/repo/worktree1",
- worktreeGitDirPath: "/path/to/repo/.git/worktrees/worktree1",
- repoPath: "/path/to/repo",
- repoGitDirPath: "/path/to/repo/.git",
- repoName: "repo",
- },
- Err: nil,
- },
- {
- Name: "worktree with trailing separator in path",
- BeforeFunc: func(fs afero.Fs) {
- // setup for linked worktree
- _ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree1", 0o755)
- _ = afero.WriteFile(fs, "/path/to/repo/worktree1/.git", []byte("gitdir: /path/to/repo/.git/worktrees/worktree1/"), 0o644)
- },
- Path: "/path/to/repo/worktree1",
- Expected: &RepoPaths{
- currentPath: "/path/to/repo/worktree1",
- worktreePath: "/path/to/repo/worktree1",
- worktreeGitDirPath: "/path/to/repo/.git/worktrees/worktree1",
- repoPath: "/path/to/repo",
- repoGitDirPath: "/path/to/repo/.git",
- repoName: "repo",
- },
- Err: nil,
- },
- {
- Name: "worktree .git file missing gitdir directive",
- BeforeFunc: func(fs afero.Fs) {
- _ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree2", 0o755)
- _ = afero.WriteFile(fs, "/path/to/repo/worktree2/.git", []byte("blah"), 0o644)
- },
- Path: "/path/to/repo/worktree2",
- Expected: nil,
- Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/worktree2: /path/to/repo/worktree2/.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"),
- },
- {
- Name: "worktree .git file gitdir directive points to a non-existing directory",
- BeforeFunc: func(fs afero.Fs) {
- _ = fs.MkdirAll("/path/to/repo/.git/worktrees/worktree2", 0o755)
- _ = afero.WriteFile(fs, "/path/to/repo/worktree2/.git", []byte("gitdir: /nonexistant"), 0o644)
- },
- Path: "/path/to/repo/worktree2",
- Expected: nil,
- Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/worktree2. /nonexistant does not exist"),
- },
- {
Name: "submodule",
- BeforeFunc: func(fs afero.Fs) {
- _ = fs.MkdirAll("/path/to/repo/.git/modules/submodule1", 0o755)
- _ = afero.WriteFile(fs, "/path/to/repo/submodule1/.git", []byte("gitdir: /path/to/repo/.git/modules/submodule1"), 0o644)
+ BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
+ expectedOutput := []string{
+ // --show-toplevel
+ "/path/to/repo/submodule1",
+ // --git-dir
+ "/path/to/repo/.git/modules/submodule1",
+ // --git-common-dir
+ "/path/to/repo/.git/modules/submodule1",
+ // --show-superproject-working-tree
+ "/path/to/repo",
+ }
+ runner.ExpectGitArgs(
+ append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
+ strings.Join(expectedOutput, "\n"),
+ nil)
},
Path: "/path/to/repo/submodule1",
Expected: &RepoPaths{
- currentPath: "/path/to/repo/submodule1",
worktreePath: "/path/to/repo/submodule1",
worktreeGitDirPath: "/path/to/repo/.git/modules/submodule1",
repoPath: "/path/to/repo/submodule1",
@@ -111,49 +82,52 @@ func TestGetRepoPathsAux(t *testing.T) {
Err: nil,
},
{
- Name: "submodule in nested directory",
- BeforeFunc: func(fs afero.Fs) {
- _ = fs.MkdirAll("/path/to/repo/.git/modules/my/submodule1", 0o755)
- _ = afero.WriteFile(fs, "/path/to/repo/my/submodule1/.git", []byte("gitdir: /path/to/repo/.git/modules/my/submodule1"), 0o644)
- },
- Path: "/path/to/repo/my/submodule1",
- Expected: &RepoPaths{
- currentPath: "/path/to/repo/my/submodule1",
- worktreePath: "/path/to/repo/my/submodule1",
- worktreeGitDirPath: "/path/to/repo/.git/modules/my/submodule1",
- repoPath: "/path/to/repo/my/submodule1",
- repoGitDirPath: "/path/to/repo/.git/modules/my/submodule1",
- repoName: "submodule1",
- },
- Err: nil,
- },
- {
- Name: "submodule git dir not under .git/modules",
- BeforeFunc: func(fs afero.Fs) {
- _ = fs.MkdirAll("/random/submodule1", 0o755)
- _ = afero.WriteFile(fs, "/path/to/repo/my/submodule1/.git", []byte("gitdir: /random/submodule1"), 0o644)
+ Name: "git rev-parse returns an error",
+ BeforeFunc: func(runner *oscommands.FakeCmdObjRunner, getRevParseArgs argFn) {
+ runner.ExpectGitArgs(
+ append(getRevParseArgs(), "--show-toplevel", "--absolute-git-dir", "--git-common-dir", "--show-superproject-working-tree"),
+ "",
+ errors.New("fatal: invalid gitfile format: /path/to/repo/worktree2/.git"))
},
- Path: "/path/to/repo/my/submodule1",
+ Path: "/path/to/repo/worktree2",
Expected: nil,
- Err: errors.New("failed to get repo git dir path: could not find git dir for /path/to/repo/my/submodule1: the path '/random/submodule1' is not under `worktrees` or `modules` directories"),
+ Err: func(getRevParseArgs argFn) error {
+ args := strings.Join(getRevParseArgs(), " ")
+ return errors.New(
+ fmt.Sprintf("'git %v --show-toplevel --absolute-git-dir --git-common-dir --show-superproject-working-tree' failed: fatal: invalid gitfile format: /path/to/repo/worktree2/.git", args),
+ )
+ },
},
}
for _, s := range scenarios {
s := s
t.Run(s.Name, func(t *testing.T) {
- fs := afero.NewMemMapFs()
+ runner := oscommands.NewFakeRunner(t)
+ cmd := oscommands.NewDummyCmdObjBuilder(runner)
+ version, err := GetGitVersion(oscommands.NewDummyOSCommand())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ getRevParseArgs := func() []string {
+ args := []string{"rev-parse"}
+ if version.IsAtLeast(2, 31, 0) {
+ args = append(args, "--path-format=absolute")
+ }
+ return args
+ }
// prepare the filesystem for the scenario