mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-03-05 15:15:49 +02:00
Style missing worktree as red and display better error when trying to switch to them
Use a broken link icon for missing worktrees
This commit is contained in:
parent
9a79154d05
commit
c679fd1924
@ -1,10 +1,6 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
@ -28,11 +24,6 @@ func NewWorktreeLoader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
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()
|
cmdArgs := NewGitCmd("worktree").Arg("list", "--porcelain", "-z").ToArgv()
|
||||||
worktreesOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
worktreesOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -51,25 +42,9 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
|||||||
}
|
}
|
||||||
if strings.HasPrefix(splitLine, "worktree ") {
|
if strings.HasPrefix(splitLine, "worktree ") {
|
||||||
path := strings.SplitN(splitLine, " ", 2)[1]
|
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{
|
currentWorktree = &models.Worktree{
|
||||||
Name: name,
|
Id: len(worktrees),
|
||||||
Path: path,
|
Path: path,
|
||||||
Main: main,
|
|
||||||
Current: path == currentDir,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-errors/errors"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
// Worktree : A git worktree
|
// Worktree : A git worktree
|
||||||
type Worktree struct {
|
type Worktree struct {
|
||||||
Name string
|
Id int
|
||||||
Main bool
|
|
||||||
Current bool
|
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Worktree) RefName() string {
|
func (w *Worktree) RefName() string {
|
||||||
return w.Name
|
return w.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Worktree) ID() string {
|
func (w *Worktree) ID() string {
|
||||||
@ -19,3 +26,30 @@ func (w *Worktree) ID() string {
|
|||||||
func (w *Worktree) Description() string {
|
func (w *Worktree) Description() string {
|
||||||
return w.RefName()
|
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
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext {
|
|||||||
viewModel := NewFilteredListViewModel(
|
viewModel := NewFilteredListViewModel(
|
||||||
func() []*models.Worktree { return c.Model().Worktrees },
|
func() []*models.Worktree { return c.Model().Worktrees },
|
||||||
func(Worktree *models.Worktree) []string {
|
func(Worktree *models.Worktree) []string {
|
||||||
return []string{Worktree.Name}
|
return []string{Worktree.Name()}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -638,16 +638,13 @@ func (self *RefreshHelper) refreshStatus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
name := presentation.GetBranchTextStyle(currentBranch.Name).Sprint(currentBranch.Name)
|
name := presentation.GetBranchTextStyle(currentBranch.Name).Sprint(currentBranch.Name)
|
||||||
|
|
||||||
var repoName string
|
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)
|
self.c.SetViewContent(self.c.Views().Status, status)
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,10 @@ func (self *ReposHelper) CreateRecentReposMenu() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *ReposHelper) DispatchSwitchToRepo(path string, reuse bool) 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()
|
env.UnsetGitDirEnvs()
|
||||||
originalPath, err := os.Getwd()
|
originalPath, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -146,7 +150,7 @@ func (self *ReposHelper) DispatchSwitchToRepo(path string, reuse bool) error {
|
|||||||
|
|
||||||
if err := os.Chdir(path); err != nil {
|
if err := os.Chdir(path); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return self.c.ErrorMsg(self.c.Tr.ErrRepositoryMovedOrDeleted)
|
return self.c.ErrorMsg(errMsg)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IWorktreeHelper interface {
|
type IWorktreeHelper interface {
|
||||||
GetMainWorktreeName() string
|
GetMainWorktreeName() string
|
||||||
GetCurrentWorktreeName() string
|
GetCurrentWorktreeName() string
|
||||||
@ -21,23 +17,23 @@ func NewWorktreeHelper(c *HelperCommon) *WorktreeHelper {
|
|||||||
|
|
||||||
func (self *WorktreeHelper) GetMainWorktreeName() string {
|
func (self *WorktreeHelper) GetMainWorktreeName() string {
|
||||||
for _, worktree := range self.c.Model().Worktrees {
|
for _, worktree := range self.c.Model().Worktrees {
|
||||||
if worktree.Main {
|
if worktree.Main() {
|
||||||
return filepath.Base(worktree.Path)
|
return worktree.Name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorktreeHelper) GetCurrentWorktreeName() string {
|
//func (self *WorktreeHelper) GetCurrentWorktreeName() string {
|
||||||
for _, worktree := range self.c.Model().Worktrees {
|
// for _, worktree := range self.c.Model().Worktrees {
|
||||||
if worktree.Current {
|
// if worktree.Current() {
|
||||||
if worktree.Main {
|
// if worktree.Main() {
|
||||||
return ""
|
// return ""
|
||||||
}
|
// }
|
||||||
return worktree.Name
|
// return worktree.Name()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return ""
|
// return ""
|
||||||
}
|
//}
|
||||||
|
@ -2,7 +2,6 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
@ -56,7 +55,11 @@ func (self *WorktreesController) GetOnRenderToMain() func() error {
|
|||||||
if worktree == nil {
|
if worktree == nil {
|
||||||
task = types.NewRenderStringTask("No worktrees")
|
task = types.NewRenderStringTask("No worktrees")
|
||||||
} else {
|
} 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{
|
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||||
@ -86,11 +89,11 @@ func (self *WorktreesController) GetOnRenderToMain() func() error {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
func (self *WorktreesController) delete(worktree *models.Worktree) error {
|
func (self *WorktreesController) delete(worktree *models.Worktree) error {
|
||||||
if worktree.Main {
|
if worktree.Main() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.CantDeleteMainWorktree)
|
return self.c.ErrorMsg(self.c.Tr.CantDeleteMainWorktree)
|
||||||
}
|
}
|
||||||
|
|
||||||
if worktree.Current {
|
if worktree.Current() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.CantDeleteCurrentWorktree)
|
return self.c.ErrorMsg(self.c.Tr.CantDeleteCurrentWorktree)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +111,7 @@ func (self *WorktreesController) deleteWithForce(worktree *models.Worktree, forc
|
|||||||
message := utils.ResolvePlaceholderString(
|
message := utils.ResolvePlaceholderString(
|
||||||
templateStr,
|
templateStr,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"worktreeName": worktree.Name,
|
"worktreeName": worktree.Name(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -170,14 +173,11 @@ func (self *WorktreesController) GetOnClick() func() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorktreesController) enter(worktree *models.Worktree) error {
|
func (self *WorktreesController) enter(worktree *models.Worktree) error {
|
||||||
wd, err := os.Getwd()
|
// if we were in a submodule, we want to forget about that stack of repos
|
||||||
if err != nil {
|
// so that hitting escape in the new repo does nothing
|
||||||
return err
|
self.c.State().GetRepoPathStack().Clear()
|
||||||
}
|
|
||||||
|
|
||||||
self.c.State().GetRepoPathStack().Push(wd)
|
return self.c.Helpers().Repos.DispatchSwitchTo(worktree.Path, true, self.c.Tr.ErrWorktreeMovedOrDeleted)
|
||||||
|
|
||||||
return self.c.Helpers().Repos.DispatchSwitchToRepo(worktree.Path, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *WorktreesController) checkSelected(callback func(worktree *models.Worktree) error) func() error {
|
func (self *WorktreesController) checkSelected(callback func(worktree *models.Worktree) error) func() error {
|
||||||
|
@ -15,6 +15,7 @@ var (
|
|||||||
DEFAULT_REMOTE_ICON = "\uf02a2" //
|
DEFAULT_REMOTE_ICON = "\uf02a2" //
|
||||||
STASH_ICON = "\uf01c" //
|
STASH_ICON = "\uf01c" //
|
||||||
LINKED_WORKTREE_ICON = "\uf838" //
|
LINKED_WORKTREE_ICON = "\uf838" //
|
||||||
|
MISSING_LINKED_WORKTREE_ICON = "\uf839" //
|
||||||
)
|
)
|
||||||
|
|
||||||
var remoteIcons = map[string]string{
|
var remoteIcons = map[string]string{
|
||||||
@ -70,9 +71,12 @@ func IconForStash(stash *models.StashEntry) string {
|
|||||||
return STASH_ICON
|
return STASH_ICON
|
||||||
}
|
}
|
||||||
|
|
||||||
func IconForWorktree(worktree *models.Worktree) string {
|
func IconForWorktree(worktree *models.Worktree, missing bool) string {
|
||||||
if worktree.Main {
|
if worktree.Main() {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
if missing {
|
||||||
|
return MISSING_LINKED_WORKTREE_ICON
|
||||||
|
}
|
||||||
return LINKED_WORKTREE_ICON
|
return LINKED_WORKTREE_ICON
|
||||||
}
|
}
|
||||||
|
@ -20,16 +20,22 @@ func getWorktreeDisplayStrings(w *models.Worktree) []string {
|
|||||||
|
|
||||||
current := ""
|
current := ""
|
||||||
currentColor := style.FgCyan
|
currentColor := style.FgCyan
|
||||||
if w.Current {
|
if w.Current() {
|
||||||
current = " *"
|
current = " *"
|
||||||
currentColor = style.FgGreen
|
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 := make([]string, 0, 3)
|
||||||
res = append(res, currentColor.Sprint(current))
|
res = append(res, currentColor.Sprint(current))
|
||||||
if icons.IsIconEnabled() {
|
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
|
return res
|
||||||
}
|
}
|
||||||
|
@ -482,6 +482,7 @@ type TranslationSet struct {
|
|||||||
ErrCannotEditDirectory string
|
ErrCannotEditDirectory string
|
||||||
ErrStageDirWithInlineMergeConflicts string
|
ErrStageDirWithInlineMergeConflicts string
|
||||||
ErrRepositoryMovedOrDeleted string
|
ErrRepositoryMovedOrDeleted string
|
||||||
|
ErrWorktreeMovedOrDeleted string
|
||||||
CommandLog string
|
CommandLog string
|
||||||
ToggleShowCommandLog string
|
ToggleShowCommandLog string
|
||||||
FocusCommandLog string
|
FocusCommandLog string
|
||||||
@ -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",
|
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 ¯\\_(ツ)_/¯",
|
ErrRepositoryMovedOrDeleted: "Cannot find repo. It might have been moved or deleted ¯\\_(ツ)_/¯",
|
||||||
CommandLog: "Command log",
|
CommandLog: "Command log",
|
||||||
|
ErrWorktreeMovedOrDeleted: "Cannot find worktree. It might have been moved or deleted ¯\\_(ツ)_/¯",
|
||||||
ToggleShowCommandLog: "Toggle show/hide command log",
|
ToggleShowCommandLog: "Toggle show/hide command log",
|
||||||
FocusCommandLog: "Focus command log",
|
FocusCommandLog: "Focus command log",
|
||||||
CommandLogHeader: "You can hide/focus this panel by pressing '%s'\n",
|
CommandLogHeader: "You can hide/focus this panel by pressing '%s'\n",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user