Skip to content

Commit

Permalink
Style missing worktree as red and display better error when trying to…
Browse files Browse the repository at this point in the history
… switch to them

Use a broken link icon for missing worktrees
  • Loading branch information
Joel Baranick authored and jesseduffield committed Jul 30, 2023
1 parent 9a79154 commit c679fd1
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 85 deletions.
29 changes: 2 additions & 27 deletions pkg/commands/git_commands/worktree_loader.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package git_commands

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

"github.com/jesseduffield/lazygit/pkg/commands/models"
Expand All @@ -28,11 +24,6 @@ func NewWorktreeLoader(
}

func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
currentDir, err := os.Getwd()
if err != nil {
return nil, err
}

cmdArgs := NewGitCmd("worktree").Arg("list", "--porcelain", "-z").ToArgv()
worktreesOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
Expand All @@ -51,25 +42,9 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
}
if strings.HasPrefix(splitLine, "worktree ") {
path := strings.SplitN(splitLine, " ", 2)[1]

if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
// Ignore because the worktree is points to a non-existing filesystem location
continue
}

main := false
name := "main"
if len(worktrees) == 0 {
main = true
} else {
name = filepath.Base(path)
}

currentWorktree = &models.Worktree{
Name: name,
Path: path,
Main: main,
Current: path == currentDir,
Id: len(worktrees),
Path: path,
}
}
}
Expand Down
44 changes: 39 additions & 5 deletions pkg/commands/models/worktree.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package models

import (
"fmt"
"github.com/go-errors/errors"
"io/fs"
"log"
"os"
"path/filepath"
)

// Worktree : A git worktree
type Worktree struct {
Name string
Main bool
Current bool
Path string
Id int
Path string
}

func (w *Worktree) RefName() string {
return w.Name
return w.Name()
}

func (w *Worktree) ID() string {
Expand All @@ -19,3 +26,30 @@ func (w *Worktree) ID() string {
func (w *Worktree) Description() string {
return w.RefName()
}

func (w *Worktree) Name() string {
return filepath.Base(w.Path)
}

func (w *Worktree) Main() bool {
return w.Id == 0
}

func (w *Worktree) Current() bool {
pwd, err := os.Getwd()
if err != nil {
log.Fatalln(err.Error())
}

return pwd == w.Path
}

func (w *Worktree) Missing() bool {
if _, err := os.Stat(w.Path); err != nil {
if errors.Is(err, fs.ErrNotExist) {
return true
}
log.Fatalln(fmt.Errorf("failed to check if worktree path `%s` exists\n%w", w.Path, err).Error())
}
return false
}
2 changes: 1 addition & 1 deletion pkg/gui/context/worktrees_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext {
viewModel := NewFilteredListViewModel(
func() []*models.Worktree { return c.Model().Worktrees },
func(Worktree *models.Worktree) []string {
return []string{Worktree.Name}
return []string{Worktree.Name()}
},
)

Expand Down
13 changes: 5 additions & 8 deletions pkg/gui/controllers/helpers/refresh_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,16 +638,13 @@ func (self *RefreshHelper) refreshStatus() {
}

name := presentation.GetBranchTextStyle(currentBranch.Name).Sprint(currentBranch.Name)

var repoName string
worktreeName := self.worktreeHelper.GetCurrentWorktreeName()
if len(worktreeName) > 0 {
worktreeName = fmt.Sprintf("[%s]", worktreeName)
repoName = self.worktreeHelper.GetMainWorktreeName()
} else {
repoName = utils.GetCurrentRepoName()
repoName = utils.GetCurrentRepoName()
mainWorktreeName := self.worktreeHelper.GetMainWorktreeName()
if repoName != mainWorktreeName {
repoName = fmt.Sprintf("%s(%s)", mainWorktreeName, style.FgBlue.Sprint(repoName))
}
status += fmt.Sprintf("%s%s → %s ", repoName, worktreeName, name)
status += fmt.Sprintf("%s → %s ", repoName, name)

self.c.SetViewContent(self.c.Views().Status, status)
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/gui/controllers/helpers/repos_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ func (self *ReposHelper) CreateRecentReposMenu() error {
}

func (self *ReposHelper) DispatchSwitchToRepo(path string, reuse bool) error {
return self.DispatchSwitchTo(path, reuse, self.c.Tr.ErrRepositoryMovedOrDeleted)
}

func (self *ReposHelper) DispatchSwitchTo(path string, reuse bool, errMsg string) error {
env.UnsetGitDirEnvs()
originalPath, err := os.Getwd()
if err != nil {
Expand All @@ -146,7 +150,7 @@ func (self *ReposHelper) DispatchSwitchToRepo(path string, reuse bool) error {

if err := os.Chdir(path); err != nil {
if os.IsNotExist(err) {
return self.c.ErrorMsg(self.c.Tr.ErrRepositoryMovedOrDeleted)
return self.c.ErrorMsg(errMsg)
}
return err
}
Expand Down
32 changes: 14 additions & 18 deletions pkg/gui/controllers/helpers/worktree_helper.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package helpers

import (
"path/filepath"
)

type IWorktreeHelper interface {
GetMainWorktreeName() string
GetCurrentWorktreeName() string
Expand All @@ -21,23 +17,23 @@ func NewWorktreeHelper(c *HelperCommon) *WorktreeHelper {

func (self *WorktreeHelper) GetMainWorktreeName() string {
for _, worktree := range self.c.Model().Worktrees {
if worktree.Main {
return filepath.Base(worktree.Path)
if worktree.Main() {
return worktree.Name()
}
}

return ""
}

func (self *WorktreeHelper) GetCurrentWorktreeName() string {
for _, worktree := range self.c.Model().Worktrees {
if worktree.Current {
if worktree.Main {
return ""
}
return worktree.Name
}
}

return ""
}
//func (self *WorktreeHelper) GetCurrentWorktreeName() string {
// for _, worktree := range self.c.Model().Worktrees {
// if worktree.Current() {
// if worktree.Main() {
// return ""
// }
// return worktree.Name()
// }
// }
//
// return ""
//}
24 changes: 12 additions & 12 deletions pkg/gui/controllers/worktrees_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package controllers

import (
"fmt"
"os"

"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
Expand Down Expand Up @@ -56,7 +55,11 @@ func (self *WorktreesController) GetOnRenderToMain() func() error {
if worktree == nil {
task = types.NewRenderStringTask("No worktrees")
} else {
task = types.NewRenderStringTask(fmt.Sprintf("%s\nPath: %s", style.FgGreen.Sprint(worktree.Name), worktree.Path))
missing := ""
if worktree.Missing() {
missing = style.FgRed.Sprint(" (missing)")
}
task = types.NewRenderStringTask(fmt.Sprintf("%s\nPath: %s%s", style.FgGreen.Sprint(worktree.Name()), worktree.Path, missing))
}

return self.c.RenderToMainViews(types.RefreshMainOpts{
Expand Down Expand Up @@ -86,11 +89,11 @@ func (self *WorktreesController) GetOnRenderToMain() func() error {
//}

func (self *WorktreesController) delete(worktree *models.Worktree) error {
if worktree.Main {
if worktree.Main() {
return self.c.ErrorMsg(self.c.Tr.CantDeleteMainWorktree)
}

if worktree.Current {
if worktree.Current() {
return self.c.ErrorMsg(self.c.Tr.CantDeleteCurrentWorktree)
}

Expand All @@ -108,7 +111,7 @@ func (self *WorktreesController) deleteWithForce(worktree *models.Worktree, forc
message := utils.ResolvePlaceholderString(
templateStr,
map[string]string{
"worktreeName": worktree.Name,
"worktreeName": worktree.Name(),
},
)

Expand Down Expand Up @@ -170,14 +173,11 @@ func (self *WorktreesController) GetOnClick() func() error {
}

func (self *WorktreesController) enter(worktree *models.Worktree) error {
wd, err := os.Getwd()
if err != nil {
return err
}

self.c.State().GetRepoPathStack().Push(wd)
// if we were in a submodule, we want to forget about that stack of repos
// so that hitting escape in the new repo does nothing
self.c.State().GetRepoPathStack().Clear()

return self.c.Helpers().Repos.DispatchSwitchToRepo(worktree.Path, true)
return self.c.Helpers().Repos.DispatchSwitchTo(worktree.Path, true, self.c.Tr.ErrWorktreeMovedOrDeleted)
}

func (self *WorktreesController) checkSelected(callback func(worktree *models.Worktree) error) func() error {
Expand Down
24 changes: 14 additions & 10 deletions pkg/gui/presentation/icons/git_icons.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import (
)

var (
BRANCH_ICON = "\U000f062c" // 󰘬
DETACHED_HEAD_ICON = "\ue729" // 
TAG_ICON = "\uf02b" // 
COMMIT_ICON = "\U000f0718" // 󰜘
MERGE_COMMIT_ICON = "\U000f062d" // 󰘭
DEFAULT_REMOTE_ICON = "\uf02a2" // 󰊢
STASH_ICON = "\uf01c" // 
LINKED_WORKTREE_ICON = "\uf838" // 
BRANCH_ICON = "\U000f062c" // 󰘬
DETACHED_HEAD_ICON = "\ue729" // 
TAG_ICON = "\uf02b" // 
COMMIT_ICON = "\U000f0718" // 󰜘
MERGE_COMMIT_ICON = "\U000f062d" // 󰘭
DEFAULT_REMOTE_ICON = "\uf02a2" // 󰊢
STASH_ICON = "\uf01c" // 
LINKED_WORKTREE_ICON = "\uf838" // 
MISSING_LINKED_WORKTREE_ICON = "\uf839" // 
)

var remoteIcons = map[string]string{
Expand Down Expand Up @@ -70,9 +71,12 @@ func IconForStash(stash *models.StashEntry) string {
return STASH_ICON
}

func IconForWorktree(worktree *models.Worktree) string {
if worktree.Main {
func IconForWorktree(worktree *models.Worktree, missing bool) string {
if worktree.Main() {
return ""
}
if missing {
return MISSING_LINKED_WORKTREE_ICON
}
return LINKED_WORKTREE_ICON
}
12 changes: 9 additions & 3 deletions pkg/gui/presentation/worktrees.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,22 @@ func getWorktreeDisplayStrings(w *models.Worktree) []string {

current := ""
currentColor := style.FgCyan
if w.Current {
if w.Current() {
current = " *"
currentColor = style.FgGreen
}

icon := icons.IconForWorktree(w, false)
if w.Missing() {
textStyle = style.FgRed
icon = icons.IconForWorktree(w, true)
}

res := make([]string, 0, 3)
res = append(res, currentColor.Sprint(current))
if icons.IsIconEnabled() {
res = append(res, textStyle.Sprint(icons.IconForWorktree(w)))
res = append(res, textStyle.Sprint(icon))
}
res = append(res, textStyle.Sprint(w.Name))
res = append(res, textStyle.Sprint(w.Name()))
return res
}
2 changes: 2 additions & 0 deletions pkg/i18n/english.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ type TranslationSet struct {
ErrCannotEditDirectory string
ErrStageDirWithInlineMergeConflicts string
ErrRepositoryMovedOrDeleted string
ErrWorktreeMovedOrDeleted string
CommandLog string
ToggleShowCommandLog string
FocusCommandLog string
Expand Down Expand Up @@ -1191,6 +1192,7 @@ func EnglishTranslationSet() TranslationSet {
ErrStageDirWithInlineMergeConflicts: "Cannot stage/unstage directory containing files with inline merge conflicts. Please fix up the merge conflicts first",
ErrRepositoryMovedOrDeleted: "Cannot find repo. It might have been moved or deleted ¯\\_(ツ)_/¯",
CommandLog: "Command log",
ErrWorktreeMovedOrDeleted: "Cannot find worktree. It might have been moved or deleted ¯\\_(ツ)_/¯",
ToggleShowCommandLog: "Toggle show/hide command log",
FocusCommandLog: "Focus command log",
CommandLogHeader: "You can hide/focus this panel by pressing '%s'\n",
Expand Down

0 comments on commit c679fd1

Please sign in to comment.