mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-15 00:15:32 +02:00
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:
@ -65,7 +65,7 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
|||||||
files = append(files, 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
|
// so that we can render them correctly
|
||||||
worktreePaths := linkedWortkreePaths()
|
worktreePaths := linkedWortkreePaths()
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@ -59,36 +58,6 @@ func (self *WorktreeCommands) Detach(worktreePath string) error {
|
|||||||
return self.cmd.New(cmdArgs).Run()
|
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) {
|
func WorktreeForBranch(branch *models.Branch, worktrees []*models.Worktree) (*models.Worktree, bool) {
|
||||||
for _, worktree := range worktrees {
|
for _, worktree := range worktrees {
|
||||||
if worktree.Branch == branch.Name {
|
if worktree.Branch == branch.Name {
|
||||||
@ -105,7 +74,7 @@ func CheckedOutByOtherWorktree(branch *models.Branch, worktrees []*models.Worktr
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return !IsCurrentWorktree(worktree.Path)
|
return !worktree.IsCurrent
|
||||||
}
|
}
|
||||||
|
|
||||||
// If in a non-bare repo, this returns the path of the main worktree
|
// If in a non-bare repo, this returns the path of the main worktree
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package git_commands
|
package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-errors/errors"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/common"
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
@ -30,6 +32,11 @@ func NewWorktreeLoader(
|
|||||||
func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
||||||
currentRepoPath := GetCurrentRepoPath()
|
currentRepoPath := GetCurrentRepoPath()
|
||||||
|
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
cmdArgs := NewGitCmd("worktree").Arg("list", "--porcelain").ToArgv()
|
cmdArgs := NewGitCmd("worktree").Arg("list", "--porcelain").ToArgv()
|
||||||
worktreesOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
worktreesOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,6 +61,8 @@ 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]
|
||||||
isMain := path == currentRepoPath
|
isMain := path == currentRepoPath
|
||||||
|
isCurrent := path == pwd
|
||||||
|
isPathMissing := self.pathExists(path)
|
||||||
|
|
||||||
var gitDir string
|
var gitDir string
|
||||||
if isMain {
|
if isMain {
|
||||||
@ -67,7 +76,9 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
current = &models.Worktree{
|
current = &models.Worktree{
|
||||||
IsMain: path == currentRepoPath,
|
IsMain: isMain,
|
||||||
|
IsCurrent: isCurrent,
|
||||||
|
IsPathMissing: isPathMissing,
|
||||||
Path: path,
|
Path: path,
|
||||||
GitDir: gitDir,
|
GitDir: gitDir,
|
||||||
}
|
}
|
||||||
@ -85,14 +96,9 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
|||||||
worktree.NameField = names[index]
|
worktree.NameField = names[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
pwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// move current worktree to the top
|
// move current worktree to the top
|
||||||
for i, worktree := range worktrees {
|
for i, worktree := range worktrees {
|
||||||
if EqualPath(worktree.Path, pwd) {
|
if worktree.IsCurrent {
|
||||||
worktrees = append(worktrees[:i], worktrees[i+1:]...)
|
worktrees = append(worktrees[:i], worktrees[i+1:]...)
|
||||||
worktrees = append([]*models.Worktree{worktree}, worktrees...)
|
worktrees = append([]*models.Worktree{worktree}, worktrees...)
|
||||||
break
|
break
|
||||||
@ -130,6 +136,17 @@ func (self *WorktreeLoader) GetWorktrees() ([]*models.Worktree, error) {
|
|||||||
return worktrees, nil
|
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) {
|
func rebaseBranch(worktree *models.Worktree) (string, bool) {
|
||||||
for _, dir := range []string{"rebase-merge", "rebase-apply"} {
|
for _, dir := range []string{"rebase-merge", "rebase-apply"} {
|
||||||
if bytesContent, err := os.ReadFile(filepath.Join(worktree.GitDir, dir, "head-name")); err == nil {
|
if bytesContent, err := os.ReadFile(filepath.Join(worktree.GitDir, dir, "head-name")); err == nil {
|
||||||
|
@ -4,8 +4,12 @@ package models
|
|||||||
type Worktree struct {
|
type Worktree struct {
|
||||||
// if false, this is a linked worktree
|
// if false, this is a linked worktree
|
||||||
IsMain bool
|
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 to the directory of the worktree i.e. the directory that contains all the user's files
|
||||||
Path string
|
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
|
// 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>
|
// in the main worktree. For linked worktrees this would be <repo_path>/.git/worktrees/<name>
|
||||||
GitDir string
|
GitDir string
|
||||||
@ -39,3 +43,11 @@ func (w *Worktree) Name() string {
|
|||||||
func (w *Worktree) Main() bool {
|
func (w *Worktree) Main() bool {
|
||||||
return w.IsMain
|
return w.IsMain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Worktree) Current() bool {
|
||||||
|
return w.IsCurrent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Worktree) PathMissing() bool {
|
||||||
|
return w.IsPathMissing
|
||||||
|
}
|
||||||
|
@ -25,8 +25,6 @@ func NewWorktreesContext(c *ContextCommon) *WorktreesContext {
|
|||||||
return presentation.GetWorktreeDisplayStrings(
|
return presentation.GetWorktreeDisplayStrings(
|
||||||
c.Tr,
|
c.Tr,
|
||||||
viewModel.GetFilteredList(),
|
viewModel.GetFilteredList(),
|
||||||
c.Git().Worktree.IsCurrentWorktree,
|
|
||||||
c.Git().Worktree.IsWorktreePathMissing,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ func (self *BranchesController) press(selectedBranch *models.Branch) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
worktreeForRef, ok := self.worktreeForBranch(selectedBranch)
|
worktreeForRef, ok := self.worktreeForBranch(selectedBranch)
|
||||||
if ok && !self.c.Git().Worktree.IsCurrentWorktree(worktreeForRef.Path) {
|
if ok && !worktreeForRef.Current() {
|
||||||
return self.promptToCheckoutWorktree(worktreeForRef)
|
return self.promptToCheckoutWorktree(worktreeForRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ func (self *BranchesController) promptToCheckoutWorktree(worktree *models.Worktr
|
|||||||
Title: "Switch to worktree",
|
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()),
|
Prompt: fmt.Sprintf("This branch is checked out by worktree %s. Do you want to switch to that worktree?", worktree.Name()),
|
||||||
HandleConfirm: func() error {
|
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",
|
Label: "Switch to worktree",
|
||||||
OnPress: func() error {
|
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 := ""
|
worktreeGitDir := ""
|
||||||
// if it is the current worktree path, no need to specify the path
|
// 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
|
worktreeGitDir = worktree.GitDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
@ -61,27 +58,6 @@ func (self *WorktreeHelper) GetLinkedWorktreeName() string {
|
|||||||
return currentWorktree.Name()
|
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 {
|
func (self *WorktreeHelper) NewWorktree() error {
|
||||||
branch := self.refsHelper.GetCheckedOutRef()
|
branch := self.refsHelper.GetCheckedOutRef()
|
||||||
currentBranchName := branch.RefName()
|
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 {
|
if err := self.c.Git().Worktree.New(opts); err != nil {
|
||||||
return err
|
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 {
|
func (self *WorktreeHelper) Switch(worktree *models.Worktree, contextKey types.ContextKey) error {
|
||||||
if self.c.Git().Worktree.IsCurrentWorktree(path) {
|
if worktree.Current() {
|
||||||
return self.c.ErrorMsg(self.c.Tr.AlreadyInWorktree)
|
return self.c.ErrorMsg(self.c.Tr.AlreadyInWorktree)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.SwitchToWorktree)
|
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 {
|
func (self *WorktreeHelper) Remove(worktree *models.Worktree, force bool) error {
|
||||||
|
@ -72,7 +72,7 @@ func (self *WorktreesController) GetOnRenderToMain() func() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
missing := ""
|
missing := ""
|
||||||
if self.c.Git().Worktree.IsWorktreePathMissing(worktree.Path) {
|
if worktree.PathMissing() {
|
||||||
missing = style.FgRed.Sprintf(" %s", self.c.Tr.MissingWorktree)
|
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)
|
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)
|
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 {
|
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 {
|
func (self *WorktreesController) open(worktree *models.Worktree) error {
|
||||||
|
@ -9,28 +9,26 @@ import (
|
|||||||
"github.com/samber/lo"
|
"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 lo.Map(worktrees, func(worktree *models.Worktree, _ int) []string {
|
||||||
return GetWorktreeDisplayString(
|
return GetWorktreeDisplayString(
|
||||||
tr,
|
tr,
|
||||||
isCurrent(worktree.Path),
|
|
||||||
isMissing(worktree.Path),
|
|
||||||
worktree)
|
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
|
textStyle := theme.DefaultTextColor
|
||||||
|
|
||||||
current := ""
|
current := ""
|
||||||
currentColor := style.FgCyan
|
currentColor := style.FgCyan
|
||||||
if isCurrent {
|
if worktree.Current() {
|
||||||
current = " *"
|
current = " *"
|
||||||
currentColor = style.FgGreen
|
currentColor = style.FgGreen
|
||||||
}
|
}
|
||||||
|
|
||||||
icon := icons.IconForWorktree(false)
|
icon := icons.IconForWorktree(false)
|
||||||
if isPathMissing {
|
if worktree.PathMissing() {
|
||||||
textStyle = style.FgRed
|
textStyle = style.FgRed
|
||||||
icon = icons.IconForWorktree(true)
|
icon = icons.IconForWorktree(true)
|
||||||
}
|
}
|
||||||
@ -45,7 +43,7 @@ func GetWorktreeDisplayString(tr *i18n.TranslationSet, isCurrent bool, isPathMis
|
|||||||
if worktree.Main() {
|
if worktree.Main() {
|
||||||
name += " " + tr.MainWorktree
|
name += " " + tr.MainWorktree
|
||||||
}
|
}
|
||||||
if isPathMissing && !icons.IsIconEnabled() {
|
if worktree.PathMissing() && !icons.IsIconEnabled() {
|
||||||
name += " " + tr.MissingWorktree
|
name += " " + tr.MissingWorktree
|
||||||
}
|
}
|
||||||
res = append(res, textStyle.Sprint(name))
|
res = append(res, textStyle.Sprint(name))
|
||||||
|
Reference in New Issue
Block a user