diff options
22 files changed, 347 insertions, 250 deletions
diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 3f7e823e7..510b26c11 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -1,7 +1,6 @@ package commands import ( - "fmt" "os" "path/filepath" "strings" @@ -40,6 +39,7 @@ type GitCommand struct { Bisect *git_commands.BisectCommands Worktree *git_commands.WorktreeCommands Version *git_commands.GitVersion + RepoPaths git_commands.RepoPaths Loaders Loaders } @@ -67,12 +67,15 @@ func NewGitCommand( return nil, err } - dotGitDir, err := findDotGitDir(os.Stat, os.ReadFile) + repoPaths, err := git_commands.GetRepoPaths() if err != nil { - return nil, err + return nil, errors.Errorf("Error getting repo paths: %v", err) } - repository, err := gogit.PlainOpenWithOptions(dotGitDir, &gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true}) + repository, err := gogit.PlainOpenWithOptions( + repoPaths.WorktreeGitDirPath(), + &gogit.PlainOpenOptions{DetectDotGit: false, EnableDotGitCommonDir: true}, + ) if err != nil { if strings.Contains(err.Error(), `unquoted '\' must be followed by new line`) { return nil, errors.New(cmn.Tr.GitconfigParseErr) @@ -85,7 +88,7 @@ func NewGitCommand( version, osCommand, gitConfig, - dotGitDir, + repoPaths, repository, syncMutex, ), nil @@ -96,7 +99,7 @@ func NewGitCommandAux( version *git_commands.GitVersion, osCommand *oscommands.OSCommand, gitConfig git_config.IGitConfig, - dotGitDir string, + repoPaths git_commands.RepoPaths, repo *gogit.Repository, syncMutex *deadlock.Mutex, ) *GitCommand { @@ -109,9 +112,9 @@ func NewGitCommandAux( // common ones are: cmn, osCommand, dotGitDir, configCommands configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo) - fileLoader := git_commands.NewFileLoader(cmn, cmd, configCommands) + gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands, syncMutex) - gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, dotGitDir, repo, configCommands, syncMutex) + fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands) statusCommands := git_commands.NewStatusCommands(gitCommon) flowCommands := git_commands.NewFlowCommands(gitCommon) remoteCommands := git_commands.NewRemoteCommands(gitCommon) @@ -138,10 +141,10 @@ func NewGitCommandAux( branchLoader := git_commands.NewBranchLoader(cmn, cmd, branchCommands.CurrentBranchInfo, configCommands) commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd) - commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode, gitCommon) + commitLoader := git_commands.NewCommitLoader(cmn, cmd, statusCommands.RebaseMode, gitCommon) reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd) remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes) - worktreeLoader := git_commands.NewWorktreeLoader(cmn, cmd) + worktreeLoader := git_commands.NewWorktreeLoader(gitCommon, cmd) stashLoader := git_commands.NewStashLoader(cmn, cmd) tagLoader := git_commands.NewTagLoader(cmn, cmd) @@ -176,6 +179,7 @@ func NewGitCommandAux( StashLoader: stashLoader, TagLoader: tagLoader, }, + RepoPaths: repoPaths, } } @@ -222,20 +226,6 @@ func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir f } } -// 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) -} - func setupRepository( openGitRepository func(string, *gogit.PlainOpenOptions) (*gogit.Repository, error), options gogit.PlainOpenOptions, @@ -253,43 +243,6 @@ func setupRepository( return repository, err } -func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filename string) ([]byte, error)) (string, error) { - unresolvedPath := env.GetGitDirEnv() - if unresolvedPath == "" { - var err error - unresolvedPath, err = os.Getwd() - if err != nil { - return "", err - } - unresolvedPath = filepath.Join(unresolvedPath, ".git") - } - - path, err := resolveSymlink(unresolvedPath) - if err != nil { - return "", err - } - - f, err := stat(path) - if err != nil { - return "", err - } - - if f.IsDir() { - return path, nil - } - - fileBytes, err := readFile(path) - if err != nil { - return "", err - } - - fileContent := string(fileBytes) - if !strings.HasPrefix(fileContent, "gitdir: ") { - 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 -} - 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/bisect.go b/pkg/commands/git_commands/bisect.go index bd4b3ead2..41e99bd3c 100644 --- a/pkg/commands/git_commands/bisect.go +++ b/pkg/commands/git_commands/bisect.go @@ -19,7 +19,7 @@ func NewBisectCommands(gitCommon *GitCommon) *BisectCommands { // This command is pretty cheap to run so we're not storing the result anywhere. // But if it becomes problematic we can chang that. func (self *BisectCommands) GetInfo() *BisectInfo { - return self.GetInfoForGitDir(self.dotGitDir) + return self.GetInfoForGitDir(self.repoPaths.WorktreeGitDirPath()) } func (self *BisectCommands) GetInfoForGitDir(gitDir string) *BisectInfo { diff --git a/pkg/commands/git_commands/commit_loader.go b/pkg/commands/git_commands/commit_loader.go index 7e304ea44..ac0f19363 100644 --- a/pkg/commands/git_commands/commit_loader.go +++ b/pkg/commands/git_commands/commit_loader.go @@ -47,7 +47,6 @@ type CommitLoader struct { func NewCommitLoader( cmn *common.Common, cmd oscommands.ICmdObjBuilder, - dotGitDir string, getRebaseMode func() (enums.RebaseMode, error), gitCommon *GitCommon, ) *CommitLoader { @@ -57,7 +56,6 @@ func NewCommitLoader( getRebaseMode: getRebaseMode, readFile: os.ReadFile, walkFiles: filepath.Walk, - dotGitDir: dotGitDir, mainBranches: nil, GitCommon: gitCommon, } @@ -299,7 +297,7 @@ func (self *CommitLoader) getRebasingCommits(rebaseMode enums.RebaseMode) ([]*mo func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) { rewrittenCount := 0 - bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-apply/rewritten")) + bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply/rewritten")) if err == nil { content := string(bytesContent) rewrittenCount = len(strings.Split(content, "\n")) @@ -307,7 +305,7 @@ func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) { // we know we're rebasing, so lets get all the files whose names have numbers commits := []*models.Commit{} - err = self.walkFiles(filepath.Join(self.dotGitDir, "rebase-apply"), func(path string, f os.FileInfo, err error) error { + err = self.walkFiles(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply"), func(path string, f os.FileInfo, err error) error { if rewrittenCount > 0 { rewrittenCount-- return nil @@ -348,7 +346,7 @@ func (self *CommitLoader) getNormalRebasingCommits() ([]*models.Commit, error) { // and extracts out the sha and names of commits that we still have to go // in the rebase: func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, error) { - bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo")) + bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo")) if err != nil { self.Log.Error(fmt.Sprintf("error occurred reading git-rebase-todo: %s", err.Error())) // we assume an error means the file doesn't exist so we just return @@ -393,7 +391,7 @@ func (self *CommitLoader) getInteractiveRebasingCommits() ([]*models.Commit, err } func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) string { - bytesContent, err := self.readFile(filepath.Join(self.dotGitDir, "rebase-merge/done")) + bytesContent, err := self.readFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/done")) if err != nil { self.Log.Error(fmt.Sprintf("error occurred reading rebase-merge/done: %s", err.Error())) return "" @@ -406,7 +404,7 @@ func (self *CommitLoader) getConflictedCommit(todos []todo.Todo) string { } amendFileExists := false - if _, err := os.Stat(filepath.Join(self.dotGitDir, "rebase-merge/amend")); err == nil { + if _, err := os.Stat(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/amend")); err == nil { amendFileExists = true } diff --git a/pkg/commands/git_commands/common.go b/pkg/commands/git_commands/common.go index f2f187bef..b4c8bea32 100644 --- a/pkg/commands/git_commands/common.go +++ b/pkg/commands/git_commands/common.go @@ -12,7 +12,7 @@ type GitCommon struct { version *GitVersion cmd oscommands.ICmdObjBuilder os *oscommands.OSCommand - dotGitDir string + repoPaths RepoPaths repo *gogit.Repository config *ConfigCommands // mutex for doing things like push/pull/fetch @@ -24,7 +24,7 @@ func NewGitCommon( version *GitVersion, cmd oscommands.ICmdObjBuilder, osCommand *oscommands.OSCommand, - dotGitDir string, + repoPaths RepoPaths, repo *gogit.Repository, config *ConfigCommands, syncMutex *deadlock.Mutex, @@ -34,7 +34,7 @@ func NewGitCommon( version: version, cmd: cmd, os: osCommand, - dotGitDir: dotGitDir, + repoPaths: repoPaths, repo: repo, config: config, syncMutex: syncMutex, diff --git a/pkg/commands/git_commands/file_loader.go b/pkg/commands/git_commands/file_loader.go index 1585fced6..574a8a6f0 100644 --- a/pkg/commands/git_commands/file_loader.go +++ b/pkg/commands/git_commands/file_loader.go @@ -7,7 +7,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" - "github.com/jesseduffield/lazygit/pkg/common" ) type FileLoaderConfig interface { @@ -15,15 +14,15 @@ type FileLoaderConfig interface { } type FileLoader struct { - *common.Common + *GitCommon cmd oscommands.ICmdObjBuilder config FileLoaderConfig getFileType func(string) string } -func NewFileLoader(cmn *common.Common, cmd oscommands.ICmdObjBuilder, config FileLoaderConfig) *FileLoader { +func NewFileLoader(gitCommon *GitCommon, cmd oscommands.ICmdObjBuilder, config FileLoaderConfig) *FileLoader { return &FileLoader{ - Common: cmn, + GitCommon: gitCommon, cmd: cmd, getFileType: oscommands.FileType, config: config, @@ -67,7 +66,7 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File // Go through the files to see if any of these files are actually worktrees // so that we can render them correctly - worktreePaths := linkedWortkreePaths() + worktreePaths := linkedWortkreePaths(self.repoPaths.RepoGitDirPath()) for _, file := range files { for _, worktreePath := range worktreePaths { absFilePath, err := filepath.Abs(file.Name) diff --git a/pkg/commands/git_commands/patch.go b/pkg/commands/git_commands/patch.go index 871dadc05..749e5dc22 100644 --- a/pkg/commands/git_commands/patch.go +++ b/pkg/commands/git_commands/patch.go @@ -79,7 +79,7 @@ func (self *PatchCommands) applyPatchFile(filepath string, opts ApplyPatchOpts) } func (self *PatchCommands) SaveTemporaryPatch(patch string) (string, error) { - filepath := filepath.Join(self.os.GetTempDir(), GetCurrentRepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch") + filepath := filepath.Join(self.os.GetTempDir(), self.repoPaths.RepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch") self.Log.Infof("saving temporary patch to %s", filepath) if err := self.os.CreateFileWithContent(filepath, patch); err != nil { return "", err diff --git a/pkg/commands/git_commands/paths.go b/pkg/commands/git_commands/paths.go new file mode 100644 index 000000000..de83346ce --- /dev/null +++ b/pkg/commands/git_commands/paths.go @@ -0,0 +1,235 @@ +package git_commands + +import ( + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/go-errors/errors" + "github.com/jesseduffield/lazygit/pkg/env" + "github.com/samber/lo" +) + +type RepoPaths interface { + // 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. + CurrentPath() string + // Path to the current worktree. If we're in the main worktree, this will + // be the same as RepoPath() + WorktreePath() string + // Path of the worktree's git dir. + // If we're in the main worktree, this will be the .git dir under the RepoPath(). + // If we're in a linked worktree, it will be the directory pointed at by the worktree's .git file + WorktreeGitDirPath() string + // Path of the repo. If we're in a the main worktree, this will be the same as WorktreePath() + // If we're in a bare repo, it will be the parent folder of the bare repo + RepoPath() string + // path of the git-dir for the repo. + // If this is a bare repo, it will be the location of the bare repo + // If this is a non-bare repo, it will be the location of the .git dir in + // the main worktree. + RepoGitDirPath() string + // Name of the repo. Basename of the folder containing the repo. + RepoName() string +} + +type RepoDirsImpl struct { + currentPath string + worktreePath string + worktreeGitDirPath string + repoPath string + repoGitDirPath string + repoName string +} + +var _ RepoPaths = &RepoDirsImpl{} + +func (self *RepoDirsImpl) CurrentPath() string { + return self.currentPath +} + +func (self *RepoDirsImpl) WorktreePath() string { + return self.worktreePath +} + +func (self *RepoDirsImpl) WorktreeGitDirPath() string { + return self.worktreeGitDirPath +} + +func (self *RepoDirsImpl) RepoPath() string { + return self.repoPath +} + +func (self *RepoDirsImpl) RepoGitDirPath() string { + return self.repoGitDirPath +} + +func (self *RepoDirsImpl) RepoName() string { + return self.repoName +} + +func GetRepoPaths() (RepoPaths, error) { + currentPath, err := os.Getwd() + if err != nil { + return &RepoDirsImpl{}, errors.Errorf("failed to get current path: %v", err) + } + + worktreePath := currentPath + repoGitDirPath, repoPath, err := GetCurrentRepoGitDirPath(currentPath) + if err != nil { + return &RepoDirsImpl{}, errors.Errorf("failed to get repo git dir path: %v", err) + } + worktreeGitDirPath, err := worktreeGitDirPath(currentPath) + if err != nil { + return &RepoDirsImpl{}, errors.Errorf("failed to get worktree git dir path: %v", err) + } + repoName := filepath.Base(repoPath) + + return &RepoDirsImpl{ + currentPath: currentPath, + worktreePath: worktreePath, + worktreeGitDirPath: worktreeGitDirPath, + repoPath: repoPath, + repoGitDirPath: repoGitDirPath, + repoName: repoName, + }, nil +} + +// Returns the paths of linked worktrees +func linkedWortkreePaths(repoGitDirPath string) []string { + result := []string{} + // For each directory in this path we're going to cat the `gitdir` file and append its contents to our result + // That file points us to the `.git` file in the worktree. + worktreeGitDirsPath := filepath.Join(repoGitDirPath, "worktrees") + + // ensure the directory exists + _, err := os.Stat(worktreeGitDirsPath) + if err != nil { + return result + } + + _ = filepath.Walk(worktreeGitDirsPath, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() { + return nil + } + + gitDirPath := filepath.Join(path, "gitdir") + gitDirBytes, err := os.ReadFile(gitDirPath) + if err != nil { + // ignoring error + return nil + } + trimmedGitDir := strings.TrimSpace(string(gitDirBytes)) + // removing the .git part + worktreeDir := filepath.Dir(trimmedGitDir) + result = append(result, worktreeDir) + return nil + }) + + return result +} + +// 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 worktreeGitDirPath(worktreePath string) (string, error) { + // if .git is a file, we're in a linked worktree, otherwise we're in + // the main worktree + dotGitPath := filepath.Join(worktreePath, ".git") + gitFileInfo, err := os.Stat(dotGitPath) + if err != nil { + return "", err + } + + if gitFileInfo.IsDir() { + return dotGitPath, nil + } + + return linkedWorktreeGitDirPath(worktreePath) +} + +func linkedWorktreeGitDirPath(worktreePath string) (string, error) { + dotGitPath := filepath.Join(worktreePath, ".git") + gitFileContents, err := os.ReadFile(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: ") + return gitDir, nil +} + +func GetCurrentRepoGitDirPath(currentPath string) (string, string, error) { + var unresolvedGitPath string + if env.GetGitDirEnv() != "" { + unresolvedGitPath = env.GetGitDirEnv() + } else { + unresolvedGitPath = filepath.Join(currentPath, ".git") + } + + gitPath, err := resolveSymlink(unresolvedGitPath) + if err != nil { + return "", "", err + } + + // check if .git is a file or a directory + gitFileInfo, err := os.Stat(gitPath) + if err != nil { + return "", "", err + } + + if gitFileInfo.IsDir() { + // must be in the main worktree + return gitPath, filepath.Dir(gitPath), nil + } + + // either in a submodule, or worktree + worktreeGitPath, err := linkedWorktreeGitDirPath(currentPath) + if err != nil { + return "", "", errors.Errorf("could not find git dir for %s: %v", currentPath, err) + } + + // confirm whether the next directory up is the worktrees/submodules directory + parent := filepath.Dir(worktreeGitPath) + if filepath.Base(parent) != "worktrees" && filepath.Base(parent) != "modules" { + return "", "", errors.Errorf("could not find git dir for %s", currentPath) + } + + // if it's a submodule, we treat it as its own repo + if filepath.Base(parent) == "modules" { + return worktreeGitPath, currentPath, nil + } + + gitDirPath := filepath.Dir(parent) + return gitDirPath, filepath.Dir(gitDirPath), nil +} + +// 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) +} diff --git a/pkg/commands/git_commands/rebase.go b/pkg/commands/git_commands/rebase.go index 74bb3d464..66bfbf82d 100644 --- a/pkg/commands/git_commands/rebase.go +++ b/pkg/commands/git_commands/rebase.go @@ -243,18 +243,18 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e // EditRebaseTodo sets the action for a given rebase commit in the git-rebase-todo file func (self *RebaseCommands) EditRebaseTodo(commit *models.Commit, action todo.TodoCommand) error { return utils.EditRebaseTodo( - filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action, self.config.GetCoreCommentChar()) + filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo"), commit.Sha, commit.Action, action, self.config.GetCoreCommentChar()) } // MoveTodoDown moves a rebase todo item down by one position func (self *RebaseCommands) MoveTodoDown(commit *models.Commit) error { - fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo") + fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo") return utils.MoveTodoDown(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar()) } // MoveTodoDown moves a rebase todo item down by one position func (self *RebaseCommands) MoveTodoUp(commit *models.Commit) error { - fileName := filepath.Join(self.dotGitDir, "rebase-merge/git-rebase-todo") + fileName := filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge/git-rebase-todo") return utils.MoveTodoUp(fileName, commit.Sha, commit.Action, self.config.GetCoreCommentChar()) } diff --git a/pkg/commands/git_commands/status.go b/pkg/commands/git_commands/status.go index 7f03c698e..13ff02cc0 100644 --- a/pkg/commands/git_commands/status.go +++ b/pkg/commands/git_commands/status.go @@ -24,14 +24,14 @@ func NewStatusCommands( // RebaseMode returns "" for non-rebase mode, "normal" for normal rebase // and "interactive" for interactive rebase func (self *StatusCommands) RebaseMode() (enums.RebaseMode, error) { - exists, err := self.os.FileExists(filepath.Join(self.dotGitDir, "rebase-apply")) + exists, err := self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-apply")) if err != nil { return enums.REBASE_MODE_NONE, err } if exists { return enums.REBASE_MODE_NORMAL, nil } - exists, err = self.os.FileExists(filepath.Join(self.dotGitDir, "rebase-merge")) + exists, err = self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "rebase-merge")) if exists { return enums.REBASE_MODE_INTERACTIVE, err } else { @@ -69,5 +69,5 @@ func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) { // IsInMergeState states whether we are still mid-merge func (self *StatusCommands) IsInMergeState() (bool, error) { - return self.os.FileExists(filepath.Join(self.dotGitDir, "MERGE_HEAD")) + return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD")) } diff --git a/pkg/commands/git_commands/submodule.go b/pkg/commands/git_commands/submodule.go index 3d8602b9a..d9d1ccd20 100644 --- a/pkg/commands/git_commands/submodule.go +++ b/pkg/commands/git_commands/submodule.go @@ -139,7 +139,9 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error { self.Log.Error(err) } - return os.RemoveAll(filepath.Join(self.dotGitDir, "modules", submodule.Path)) + // We may in fact want to use the repo's git dir path but git docs say not to + // mix submodules and worktrees anyway. + return os.RemoveAll(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "modules", submodule.Path)) } func (self *SubmoduleCommands) Add(name string, path string, url string) error { diff --git a/pkg/commands/git_commands/worktree.go b/pkg/commands/git_commands/worktree.go index 1b57ab122..986bb6d42 100644 --- a/pkg/commands/git_commands/worktree.go +++ b/pkg/commands/git_commands/worktree.go @@ -1,11 +1,7 @@ package git_commands import ( - "io/fs" - "log" - "os" "path/filepath" - "strings" "github.com/jesseduffield/lazygit/pkg/commands/models" ) @@ -76,89 +72,3 @@ func CheckedOutByOtherWorktree(branch *models.Branch, worktrees []*models.Worktr return !worktree.IsCurrent } - -// If in a non-bare repo, this returns the path of the main worktree -// TODO: see if this works with a bare repo. -func GetCurrentRepoPath() string { - pwd, err := os.Getwd() - if err != nil { - log.Fatalln(err.Error()) - } - - // check if .git is a file or a directory - gitPath := filepath.Join(pwd, ".git") - gitFileInfo, err := os.Stat(gitPath) - if err != nil { - // fallback - return currentPath() - } - - if gitFileInfo.IsDir() { - // must be in the main worktree - return currentPath() - } - - // either in a submodule, a worktree, or a bare repo - worktreeGitPath, ok := LinkedWorktreeGitPath(pwd) - if !ok { - // fallback - return currentPath() - } - - // confirm whether the next directory up is the 'worktrees' directory - parent := filepath.Dir(worktreeGitPath) - if filepath.Base(parent) != "worktrees" { - // fallback - return currentPath() - } - - // now we just jump up two more directories to get the repo name - return filepath.Dir(filepath.Dir(parent)) -} - -func GetCurrentRepoName() string { - return filepath.Base(GetCurrentRepoPath()) -} - -func currentPath() string { - pwd, err := os.Getwd() - if err != nil { - log.Fatalln(err.Error()) - } - return pwd -} - -func linkedWortkreePaths() []string { - // first we need to get the repo dir - repoPath := GetCurrentRepoPath() - result := []string{} - worktreePath := filepath.Join(repoPath, ".git", "worktrees") - // for each directory in this path we're going to cat the `gitdir` file and append its contents to our result - - // ensure the directory exists - _, err := os.Stat(worktreePath) - if err != nil { - return result - } - - err = filepath.Walk(worktreePath, func(path string, info fs.FileInfo, err error) error { - if info.IsDir() { - gitDirPath := filepath.Join(path, "gitdir") - gitDirBytes, err := os.ReadFile(gitDirPath) - if err != nil { - // ignoring error - return nil - } - trimmedGitDir := strings.TrimSpace(string(gitDirBytes)) - // removing the .git part - worktreeDir := filepath.Dir(trimmedGitDir) - result = append(result, worktreeDir) - } - return nil - }) - if err != nil { - return result - } - - return result -} diff --git a/pkg/commands/git_commands/worktree_loader.go b/pkg/commands/git_commands/worktree_loader.go index 42f889156..06a22d2b5 100644 --- a/pkg/commands/git_commands/worktree_loader.go +++ b/pkg/commands/git_commands/worktree_loader.go @@ -9,33 +9,28 @@ import ( "github.com/go-errors/errors" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" - "github.com/jesseduffield/lazygit/pkg/common" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" ) type WorktreeLoader struct { - *common.Common + *GitCommon cmd oscommands.ICmdObjBuilder } func NewWorktreeLoader( - common *common.Common, + gitCommon *GitCommon, cmd oscommands.ICmdObjBuilder, ) *WorktreeLoader { return &WorktreeLoader{ - Common: common, - cmd: cmd, + GitCommon: gitCommon, + cmd: cmd, } } func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) { - currentRepoPath := GetCurrentRepoPath() - - pwd, err := os.Getwd() - if err != nil { - return nil, err - } + currentRepoPath := self.repoPaths.RepoPath() + worktreePath := self.repoP |