Skip to content

Commit

Permalink
Remove IO logic from presentation code for worktrees
Browse files Browse the repository at this point in the history
We're doing all the IO in our workers loader method so that we don't need to do any
in our presentation code
  • Loading branch information
jesseduffield committed Jul 30, 2023
1 parent 2b24c15 commit de57cfd
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 86 deletions.
2 changes: 1 addition & 1 deletion pkg/commands/git_commands/file_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
33 changes: 1 addition & 32 deletions pkg/commands/git_commands/worktree.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package git_commands

import (
"errors"
"io/fs"
"log"
"os"
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down
35 changes: 26 additions & 9 deletions pkg/commands/git_commands/worktree_loader.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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]
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions pkg/commands/models/worktree.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
2 changes: 0 additions & 2 deletions pkg/gui/context/worktrees_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext {
return presentation.GetWorktreeDisplayStrings(
c.Tr,
viewModel.GetFilteredList(),
c.Git().Worktree.IsCurrentWorktree,
c.Git().Worktree.IsWorktreePathMissing,
)
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/gui/controllers/branches_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand All @@ -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)
},
})
}
Expand Down Expand Up @@ -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)
},
},
{
Expand Down Expand Up @@ -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
}

Expand Down
33 changes: 5 additions & 28 deletions pkg/gui/controllers/helpers/worktree_helper.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package helpers

import (
"errors"
"io/fs"
"os"
"strings"

"github.com/jesseduffield/gocui"
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
})
}

Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions pkg/gui/controllers/worktrees_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
}

Expand All @@ -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 {
Expand Down
12 changes: 5 additions & 7 deletions pkg/gui/presentation/worktrees.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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))
Expand Down

0 comments on commit de57cfd

Please sign in to comment.