diff --git a/pkg/commands/git_commands/file_loader.go b/pkg/commands/git_commands/file_loader.go index 685062a58c1..1585fced6d6 100644 --- a/pkg/commands/git_commands/file_loader.go +++ b/pkg/commands/git_commands/file_loader.go @@ -65,7 +65,7 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File files = append(files, file) } - // Go through the worktrees to see if any of these files are actually worktrees + // Go through the files to see if any of these files are actually worktrees // so that we can render them correctly worktreePaths := linkedWortkreePaths() for _, file := range files { diff --git a/pkg/commands/git_commands/worktree.go b/pkg/commands/git_commands/worktree.go index e35b3a715a9..1b57ab1225a 100644 --- a/pkg/commands/git_commands/worktree.go +++ b/pkg/commands/git_commands/worktree.go @@ -1,7 +1,6 @@ package git_commands import ( - "errors" "io/fs" "log" "os" @@ -59,36 +58,6 @@ func (self *WorktreeCommands) Detach(worktreePath string) error { return self.cmd.New(cmdArgs).Run() } -func (self *WorktreeCommands) IsCurrentWorktree(path string) bool { - return IsCurrentWorktree(path) -} - -func IsCurrentWorktree(path string) bool { - pwd, err := os.Getwd() - if err != nil { - return false - } - - return EqualPath(pwd, path) -} - -func (self *WorktreeCommands) IsWorktreePathMissing(path string) bool { - if _, err := os.Stat(path); err != nil { - if errors.Is(err, fs.ErrNotExist) { - return true - } - self.Log.Errorf("failed to check if worktree path `%s` exists\n%v", path, err) - return false - } - return false -} - -// checks if two paths are equal -// TODO: support relative paths -func EqualPath(a string, b string) bool { - return a == b -} - func WorktreeForBranch(branch *models.Branch, worktrees []*models.Worktree) (*models.Worktree, bool) { for _, worktree := range worktrees { if worktree.Branch == branch.Name { @@ -105,7 +74,7 @@ func CheckedOutByOtherWorktree(branch *models.Branch, worktrees []*models.Worktr return false } - return !IsCurrentWorktree(worktree.Path) + return !worktree.IsCurrent } // If in a non-bare repo, this returns the path of the main worktree diff --git a/pkg/commands/git_commands/worktree_loader.go b/pkg/commands/git_commands/worktree_loader.go index bbd8367f32c..42f889156a5 100644 --- a/pkg/commands/git_commands/worktree_loader.go +++ b/pkg/commands/git_commands/worktree_loader.go @@ -1,10 +1,12 @@ package git_commands import ( + "io/fs" "os" "path/filepath" "strings" + "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" @@ -30,6 +32,11 @@ func NewWorktreeLoader( func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) { currentRepoPath := GetCurrentRepoPath() + pwd, err := os.Getwd() + if err != nil { + return nil, err + } + cmdArgs := NewGitCmd("worktree").Arg("list", "--porcelain").ToArgv() worktreesOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput() if err != nil { @@ -54,6 +61,8 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) { if strings.HasPrefix(splitLine, "worktree ") { path := strings.SplitN(splitLine, " ", 2)[1] isMain := path == currentRepoPath + isCurrent := path == pwd + isPathMissing := self.pathExists(path) var gitDir string if isMain { @@ -67,9 +76,11 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) { } current = &models.Worktree{ - IsMain: path == currentRepoPath, - Path: path, - GitDir: gitDir, + IsMain: isMain, + IsCurrent: isCurrent, + IsPathMissing: isPathMissing, + Path: path, + GitDir: gitDir, } } else if strings.HasPrefix(splitLine, "branch ") { branch := strings.SplitN(splitLine, " ", 2)[1] @@ -85,14 +96,9 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) { worktree.NameField = names[index] } - pwd, err := os.Getwd() - if err != nil { - return nil, err - } - // move current worktree to the top for i, worktree := range worktrees { - if EqualPath(worktree.Path, pwd) { + if worktree.IsCurrent { worktrees = append(worktrees[:i], worktrees[i+1:]...) worktrees = append([]*models.Worktree{worktree}, worktrees...) break @@ -130,6 +136,17 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) { return worktrees, nil } +func (self *WorktreeLoader) pathExists(path string) bool { + if _, err := os.Stat(path); err != nil { + if errors.Is(err, fs.ErrNotExist) { + return true + } + self.Log.Errorf("failed to check if worktree path `%s` exists\n%v", path, err) + return false + } + return false +} + func rebaseBranch(worktree *models.Worktree) (string, bool) { for _, dir := range []string{"rebase-merge", "rebase-apply"} { if bytesContent, err := os.ReadFile(filepath.Join(worktree.GitDir, dir, "head-name")); err == nil { diff --git a/pkg/commands/models/worktree.go b/pkg/commands/models/worktree.go index c1430423376..662d137dc8d 100644 --- a/pkg/commands/models/worktree.go +++ b/pkg/commands/models/worktree.go @@ -4,8 +4,12 @@ package models type Worktree struct { // if false, this is a linked worktree IsMain bool + // if true, this is the worktree that is currently checked out + IsCurrent bool // path to the directory of the worktree i.e. the directory that contains all the user's files Path string + // if true, the path is not found + IsPathMissing bool // path of the git directory for this worktree. The equivalent of the .git directory // in the main worktree. For linked worktrees this would be /.git/worktrees/ GitDir string @@ -39,3 +43,11 @@ func (w *Worktree) Name() string { func (w *Worktree) Main() bool { return w.IsMain } + +func (w *Worktree) Current() bool { + return w.IsCurrent +} + +func (w *Worktree) PathMissing() bool { + return w.IsPathMissing +} diff --git a/pkg/gui/context/worktrees_context.go b/pkg/gui/context/worktrees_context.go index 17dd534a6c3..224a233e119 100644 --- a/pkg/gui/context/worktrees_context.go +++ b/pkg/gui/context/worktrees_context.go @@ -25,8 +25,6 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext { return presentation.GetWorktreeDisplayStrings( c.Tr, viewModel.GetFilteredList(), - c.Git().Worktree.IsCurrentWorktree, - c.Git().Worktree.IsWorktreePathMissing, ) } diff --git a/pkg/gui/controllers/branches_controller.go b/pkg/gui/controllers/branches_controller.go index c5a5c1c78b1..b3c3fdcdca2 100644 --- a/pkg/gui/controllers/branches_controller.go +++ b/pkg/gui/controllers/branches_controller.go @@ -203,7 +203,7 @@ func (self *BranchesController) press(selectedBranch *models.Branch) error { } worktreeForRef, ok := self.worktreeForBranch(selectedBranch) - if ok && !self.c.Git().Worktree.IsCurrentWorktree(worktreeForRef.Path) { + if ok && !worktreeForRef.Current() { return self.promptToCheckoutWorktree(worktreeForRef) } @@ -220,7 +220,7 @@ func (self *BranchesController) promptToCheckoutWorktree(worktree *models.Worktr Title: "Switch to worktree", Prompt: fmt.Sprintf("This branch is checked out by worktree %s. Do you want to switch to that worktree?", worktree.Name()), HandleConfirm: func() error { - return self.c.Helpers().Worktree.Switch(worktree.Path, context.LOCAL_BRANCHES_CONTEXT_KEY) + return self.c.Helpers().Worktree.Switch(worktree, context.LOCAL_BRANCHES_CONTEXT_KEY) }, }) } @@ -342,7 +342,7 @@ func (self *BranchesController) promptWorktreeBranchDelete(selectedBranch *model { Label: "Switch to worktree", OnPress: func() error { - return self.c.Helpers().Worktree.Switch(worktree.Path, context.LOCAL_BRANCHES_CONTEXT_KEY) + return self.c.Helpers().Worktree.Switch(worktree, context.LOCAL_BRANCHES_CONTEXT_KEY) }, }, { @@ -432,7 +432,7 @@ func (self *BranchesController) fastForward(branch *models.Branch) error { worktreeGitDir := "" // if it is the current worktree path, no need to specify the path - if !git_commands.IsCurrentWorktree(worktree.Path) { + if !worktree.Current() { worktreeGitDir = worktree.GitDir } diff --git a/pkg/gui/controllers/helpers/worktree_helper.go b/pkg/gui/controllers/helpers/worktree_helper.go index f122ac01e54..0a0a505ac5e 100644 --- a/pkg/gui/controllers/helpers/worktree_helper.go +++ b/pkg/gui/controllers/helpers/worktree_helper.go @@ -1,9 +1,6 @@ package helpers import ( - "errors" - "io/fs" - "os" "strings" "github.com/jesseduffield/gocui" @@ -61,27 +58,6 @@ func (self *WorktreeHelper) GetLinkedWorktreeName() string { return currentWorktree.Name() } -func (self *WorktreeHelper) IsCurrentWorktree(w *models.Worktree) bool { - pwd, err := os.Getwd() - if err != nil { - self.c.Log.Errorf("failed to obtain current working directory: %v", err) - return false - } - - return pwd == w.Path -} - -func (self *WorktreeHelper) IsWorktreePathMissing(w *models.Worktree) bool { - if _, err := os.Stat(w.Path); err != nil { - if errors.Is(err, fs.ErrNotExist) { - return true - } - self.c.Log.Errorf("failed to check if worktree path `%s` exists: %v", w.Path, err) - return false - } - return false -} - func (self *WorktreeHelper) NewWorktree() error { branch := self.refsHelper.GetCheckedOutRef() currentBranchName := branch.RefName() @@ -132,7 +108,8 @@ func (self *WorktreeHelper) NewWorktreeCheckout(base string, canCheckoutBase boo if err := self.c.Git().Worktree.New(opts); err != nil { return err } - return self.Switch(opts.Path, contextKey) + + return self.reposHelper.DispatchSwitchTo(opts.Path, self.c.Tr.ErrWorktreeMovedOrRemoved, contextKey) }) } @@ -175,14 +152,14 @@ func (self *WorktreeHelper) NewWorktreeCheckout(base string, canCheckoutBase boo }) } -func (self *WorktreeHelper) Switch(path string, contextKey types.ContextKey) error { - if self.c.Git().Worktree.IsCurrentWorktree(path) { +func (self *WorktreeHelper) Switch(worktree *models.Worktree, contextKey types.ContextKey) error { + if worktree.Current() { return self.c.ErrorMsg(self.c.Tr.AlreadyInWorktree) } self.c.LogAction(self.c.Tr.SwitchToWorktree) - return self.reposHelper.DispatchSwitchTo(path, self.c.Tr.ErrWorktreeMovedOrRemoved, contextKey) + return self.reposHelper.DispatchSwitchTo(worktree.Path, self.c.Tr.ErrWorktreeMovedOrRemoved, contextKey) } func (self *WorktreeHelper) Remove(worktree *models.Worktree, force bool) error { diff --git a/pkg/gui/controllers/worktrees_controller.go b/pkg/gui/controllers/worktrees_controller.go index 6b403f0ee92..78d53914d07 100644 --- a/pkg/gui/controllers/worktrees_controller.go +++ b/pkg/gui/controllers/worktrees_controller.go @@ -72,7 +72,7 @@ func (self *WorktreesController) GetOnRenderToMain() func() error { } missing := "" - if self.c.Git().Worktree.IsWorktreePathMissing(worktree.Path) { + if worktree.PathMissing() { missing = style.FgRed.Sprintf(" %s", self.c.Tr.MissingWorktree) } @@ -105,7 +105,7 @@ func (self *WorktreesController) remove(worktree *models.Worktree) error { return self.c.ErrorMsg(self.c.Tr.CantDeleteMainWorktree) } - if self.c.Git().Worktree.IsCurrentWorktree(worktree.Path) { + if worktree.Current() { return self.c.ErrorMsg(self.c.Tr.CantDeleteCurrentWorktree) } @@ -117,7 +117,7 @@ func (self *WorktreesController) GetOnClick() func() error { } func (self *WorktreesController) enter(worktree *models.Worktree) error { - return self.c.Helpers().Worktree.Switch(worktree.Path, context.WORKTREES_CONTEXT_KEY) + return self.c.Helpers().Worktree.Switch(worktree, context.WORKTREES_CONTEXT_KEY) } func (self *WorktreesController) open(worktree *models.Worktree) error { diff --git a/pkg/gui/presentation/worktrees.go b/pkg/gui/presentation/worktrees.go index 6c8acf97652..c16871c6912 100644 --- a/pkg/gui/presentation/worktrees.go +++ b/pkg/gui/presentation/worktrees.go @@ -9,28 +9,26 @@ import ( "github.com/samber/lo" ) -func GetWorktreeDisplayStrings(tr *i18n.TranslationSet, worktrees []*models.Worktree, isCurrent func(string) bool, isMissing func(string) bool) [][]string { +func GetWorktreeDisplayStrings(tr *i18n.TranslationSet, worktrees []*models.Worktree) [][]string { return lo.Map(worktrees, func(worktree *models.Worktree, _ int) []string { return GetWorktreeDisplayString( tr, - isCurrent(worktree.Path), - isMissing(worktree.Path), worktree) }) } -func GetWorktreeDisplayString(tr *i18n.TranslationSet, isCurrent bool, isPathMissing bool, worktree *models.Worktree) []string { +func GetWorktreeDisplayString(tr *i18n.TranslationSet, worktree *models.Worktree) []string { textStyle := theme.DefaultTextColor current := "" currentColor := style.FgCyan - if isCurrent { + if worktree.Current() { current = " *" currentColor = style.FgGreen } icon := icons.IconForWorktree(false) - if isPathMissing { + if worktree.PathMissing() { textStyle = style.FgRed icon = icons.IconForWorktree(true) } @@ -45,7 +43,7 @@ func GetWorktreeDisplayString(tr *i18n.TranslationSet, isCurrent bool, isPathMis if worktree.Main() { name += " " + tr.MainWorktree } - if isPathMissing && !icons.IsIconEnabled() { + if worktree.PathMissing() && !icons.IsIconEnabled() { name += " " + tr.MissingWorktree } res = append(res, textStyle.Sprint(name))