Remove IO logic from presentation code for worktrees

We're doing all the IO in our workers loader method so that we don't need to do any
in our presentation code
This commit is contained in:
Jesse Duffield 2023-07-28 16:17:15 +10:00
parent 2b24c15938
commit de57cfd6ff
9 changed files with 57 additions and 86 deletions

View file

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

View file

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

View file

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

View file

@ -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 <repo_path>/.git/worktrees/<name>
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
}

View file

@ -25,8 +25,6 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext {
return presentation.GetWorktreeDisplayStrings(
c.Tr,
viewModel.GetFilteredList(),
c.Git().Worktree.IsCurrentWorktree,
c.Git().Worktree.IsWorktreePathMissing,
)
}

View file

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

View file

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

View file

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

View file

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