1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-23 00:39:13 +02:00

Properly render worktrees in files panel

This commit is contained in:
Jesse Duffield
2023-07-17 15:22:14 +10:00
parent b73efb2c22
commit 7b05dacb98
6 changed files with 79 additions and 27 deletions

View File

@ -2,6 +2,7 @@ package git_commands
import ( import (
"fmt" "fmt"
"path/filepath"
"strings" "strings"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
@ -58,13 +59,32 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
Name: status.Name, Name: status.Name,
PreviousName: status.PreviousName, PreviousName: status.PreviousName,
DisplayString: status.StatusString, DisplayString: status.StatusString,
Type: self.getFileType(status.Name),
} }
models.SetStatusFields(file, status.Change) models.SetStatusFields(file, status.Change)
files = append(files, file) files = append(files, file)
} }
// Go through the worktrees to see if any of these files are actually worktrees
// so that we can render them correctly
worktreePaths := linkedWortkreePaths()
for _, file := range files {
for _, worktreePath := range worktreePaths {
absFilePath, err := filepath.Abs(file.Name)
if err != nil {
self.Log.Error(err)
continue
}
if absFilePath == worktreePath {
file.IsWorktree = true
// `git status` renders this worktree as a folder with a trailing slash but we'll represent it as a singular worktree
// If we include the slash, it will be rendered as a folder with a null file inside.
file.Name = strings.TrimSuffix(file.Name, "/")
break
}
}
}
return files return files
} }

View File

@ -41,7 +41,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false, HasMergeConflicts: false,
HasInlineMergeConflicts: false, HasInlineMergeConflicts: false,
DisplayString: "MM file1.txt", DisplayString: "MM file1.txt",
Type: "file",
ShortStatus: "MM", ShortStatus: "MM",
}, },
{ {
@ -54,7 +53,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false, HasMergeConflicts: false,
HasInlineMergeConflicts: false, HasInlineMergeConflicts: false,
DisplayString: "A file3.txt", DisplayString: "A file3.txt",
Type: "file",
ShortStatus: "A ", ShortStatus: "A ",
}, },
{ {
@ -67,7 +65,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false, HasMergeConflicts: false,
HasInlineMergeConflicts: false, HasInlineMergeConflicts: false,
DisplayString: "AM file2.txt", DisplayString: "AM file2.txt",
Type: "file",
ShortStatus: "AM", ShortStatus: "AM",
}, },
{ {
@ -80,7 +77,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false, HasMergeConflicts: false,
HasInlineMergeConflicts: false, HasInlineMergeConflicts: false,
DisplayString: "?? file4.txt", DisplayString: "?? file4.txt",
Type: "file",
ShortStatus: "??", ShortStatus: "??",
}, },
{ {
@ -93,7 +89,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: true, HasMergeConflicts: true,
HasInlineMergeConflicts: true, HasInlineMergeConflicts: true,
DisplayString: "UU file5.txt", DisplayString: "UU file5.txt",
Type: "file",
ShortStatus: "UU", ShortStatus: "UU",
}, },
}, },
@ -113,7 +108,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false, HasMergeConflicts: false,
HasInlineMergeConflicts: false, HasInlineMergeConflicts: false,
DisplayString: "MM a\nb.txt", DisplayString: "MM a\nb.txt",
Type: "file",
ShortStatus: "MM", ShortStatus: "MM",
}, },
}, },
@ -137,7 +131,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false, HasMergeConflicts: false,
HasInlineMergeConflicts: false, HasInlineMergeConflicts: false,
DisplayString: "R before1.txt -> after1.txt", DisplayString: "R before1.txt -> after1.txt",
Type: "file",
ShortStatus: "R ", ShortStatus: "R ",
}, },
{ {
@ -151,7 +144,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false, HasMergeConflicts: false,
HasInlineMergeConflicts: false, HasInlineMergeConflicts: false,
DisplayString: "RM before2.txt -> after2.txt", DisplayString: "RM before2.txt -> after2.txt",
Type: "file",
ShortStatus: "RM", ShortStatus: "RM",
}, },
}, },
@ -174,7 +166,6 @@ func TestFileGetStatusFiles(t *testing.T) {
HasMergeConflicts: false, HasMergeConflicts: false,
HasInlineMergeConflicts: false, HasInlineMergeConflicts: false,
DisplayString: "?? a -> b.txt", DisplayString: "?? a -> b.txt",
Type: "file",
ShortStatus: "??", ShortStatus: "??",
}, },
}, },

View File

@ -7,6 +7,7 @@ import (
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
) )
@ -107,7 +108,7 @@ func CheckedOutByOtherWorktree(branch *models.Branch, worktrees []*models.Worktr
return !IsCurrentWorktree(worktree.Path) return !IsCurrentWorktree(worktree.Path)
} }
func GetCurrentRepoName() string { func GetCurrentRepoPath() string {
pwd, err := os.Getwd() pwd, err := os.Getwd()
if err != nil { if err != nil {
log.Fatalln(err.Error()) log.Fatalln(err.Error())
@ -120,24 +121,58 @@ func GetCurrentRepoName() string {
log.Fatalln(err.Error()) log.Fatalln(err.Error())
} }
if gitFileInfo.IsDir() {
// must be in the main worktree
return currentPath()
}
// must be a worktree or bare repo // must be a worktree or bare repo
if !gitFileInfo.IsDir() {
worktreeGitPath, ok := WorktreeGitPath(pwd) worktreeGitPath, ok := WorktreeGitPath(pwd)
if !ok { if !ok {
return basePath() // fallback
return currentPath()
} }
// now we just jump up three directories to get the repo name // now we just jump up three directories to get the repo name
return filepath.Base(filepath.Dir(filepath.Dir(filepath.Dir(worktreeGitPath)))) return filepath.Dir(filepath.Dir(filepath.Dir(worktreeGitPath)))
} }
return basePath() func GetCurrentRepoName() string {
return filepath.Base(GetCurrentRepoPath())
} }
func basePath() string { func currentPath() string {
pwd, err := os.Getwd() pwd, err := os.Getwd()
if err != nil { if err != nil {
log.Fatalln(err.Error()) log.Fatalln(err.Error())
} }
return filepath.Base(pwd) return pwd
}
func linkedWortkreePaths() []string {
// first we need to get the repo dir
repoPath := GetCurrentRepoPath()
result := []string{}
worktreePath := filepath.Join(repoPath, ".git", "worktrees")
// for each directory in this path we're going to cat the `gitdir` file and append its contents to our result
err := filepath.Walk(worktreePath, func(path string, info fs.FileInfo, err error) error {
if info.IsDir() {
gitDirPath := filepath.Join(path, "gitdir")
gitDirBytes, err := os.ReadFile(gitDirPath)
if err != nil {
// ignoring error
return nil
}
trimmedGitDir := strings.TrimSpace(string(gitDirBytes))
// removing the .git part
worktreeDir := filepath.Dir(trimmedGitDir)
result = append(result, worktreeDir)
}
return nil
})
if err != nil {
log.Fatalln(err.Error())
}
return result
} }

View File

@ -18,8 +18,10 @@ type File struct {
HasMergeConflicts bool HasMergeConflicts bool
HasInlineMergeConflicts bool HasInlineMergeConflicts bool
DisplayString string DisplayString string
Type string // one of 'file', 'directory', and 'other'
ShortStatus string // e.g. 'AD', ' A', 'M ', '??' ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
// If true, this must be a worktree folder
IsWorktree bool
} }
// sometimes we need to deal with either a node (which contains a file) or an actual file // sometimes we need to deal with either a node (which contains a file) or an actual file

View File

@ -155,10 +155,11 @@ func getFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, di
} }
isSubmodule := file != nil && file.IsSubmodule(submoduleConfigs) isSubmodule := file != nil && file.IsSubmodule(submoduleConfigs)
isLinkedWorktree := file != nil && file.IsWorktree
isDirectory := file == nil isDirectory := file == nil
if icons.IsIconEnabled() { if icons.IsIconEnabled() {
output += restColor.Sprintf("%s ", icons.IconForFile(name, isSubmodule, isDirectory)) output += restColor.Sprintf("%s ", icons.IconForFile(name, isSubmodule, isLinkedWorktree, isDirectory))
} }
output += restColor.Sprint(utils.EscapeSpecialChars(name)) output += restColor.Sprint(utils.EscapeSpecialChars(name))
@ -193,10 +194,11 @@ func getCommitFileLine(name string, diffName string, commitFile *models.CommitFi
} }
isSubmodule := false isSubmodule := false
isLinkedWorktree := false
isDirectory := commitFile == nil isDirectory := commitFile == nil
if icons.IsIconEnabled() { if icons.IsIconEnabled() {
output += colour.Sprintf("%s ", icons.IconForFile(name, isSubmodule, isDirectory)) output += colour.Sprintf("%s ", icons.IconForFile(name, isSubmodule, isLinkedWorktree, isDirectory))
} }
output += colour.Sprint(name) output += colour.Sprint(name)

View File

@ -323,7 +323,7 @@ func patchFileIconsForNerdFontsV2() {
extIconMap[".vue"] = "\ufd42" // ﵂ extIconMap[".vue"] = "\ufd42" // ﵂
} }
func IconForFile(name string, isSubmodule bool, isDirectory bool) string { func IconForFile(name string, isSubmodule bool, isLinkedWorktree bool, isDirectory bool) string {
base := filepath.Base(name) base := filepath.Base(name)
if icon, ok := nameIconMap[base]; ok { if icon, ok := nameIconMap[base]; ok {
return icon return icon
@ -336,6 +336,8 @@ func IconForFile(name string, isSubmodule bool, isDirectory bool) string {
if isSubmodule { if isSubmodule {
return DEFAULT_SUBMODULE_ICON return DEFAULT_SUBMODULE_ICON
} else if isLinkedWorktree {
return LINKED_WORKTREE_ICON
} else if isDirectory { } else if isDirectory {
return DEFAULT_DIRECTORY_ICON return DEFAULT_DIRECTORY_ICON
} }