feat(git): display worktree count

resolves #913
This commit is contained in:
Laurent Nullens 2021-09-04 20:32:55 +02:00 committed by GitHub
parent e422e87384
commit bb246377ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 26 deletions

View file

@ -61,10 +61,12 @@ An alternative is to use the [Posh-Git segment][poshgit]
- display_status: `boolean` - display the local changes or not - defaults to `false`
- display_status_detail: `boolean` - display the local changes in detail or not - defaults to `true`
- display_stash_count: `boolean` show stash count or not - defaults to `false`
- display_worktree_count: `boolean` show worktree count or not - defaults to `false`
- status_separator_icon: `string` icon/text to display between staging and working area changes - defaults to ` |`
- local_working_icon: `string` - the icon to display in front of the working area changes - defaults to `\uF044`
- local_staged_icon: `string` - the icon to display in front of the staged area changes - defaults to `\uF046`
- stash_count_icon: `string` icon/text to display before the stash context - defaults to `\uF692`
- worktree_count_icon: `string` icon/text to display before the worktree context - defaults to `\uF1BB`
### HEAD context

View file

@ -57,6 +57,7 @@ type environmentInfo interface {
hasFilesInDir(dir, pattern string) bool
hasFolder(folder string) bool
getFileContent(file string) string
getFoldersList(path string) []string
getPathSeperator() string
getCurrentUser() string
isRunningAsRoot() bool
@ -219,6 +220,21 @@ func (env *environment) getFileContent(file string) string {
return string(content)
}
func (env *environment) getFoldersList(path string) []string {
defer env.tracer.trace(time.Now(), "getFoldersList", path)
content, err := os.ReadDir(path)
if err != nil {
return nil
}
var folderNames []string
for _, s := range content {
if s.IsDir() {
folderNames = append(folderNames, s.Name())
}
}
return folderNames
}
func (env *environment) getPathSeperator() string {
defer env.tracer.trace(time.Now(), "getPathSeperator")
return string(os.PathSeparator)

View file

@ -20,6 +20,7 @@ type gitRepo struct {
gitWorkingFolder string // .git working folder, can be different of root if using worktree
isWorkTree bool
gitRootFolder string // .git root folder
worktreeCount int
}
type gitStatus struct {
@ -120,6 +121,10 @@ const (
AheadColor Property = "ahead_color"
// BranchMaxLength truncates the length of the branch name
BranchMaxLength Property = "branch_max_length"
// DisplayWorktreeCount show worktree count or not
DisplayWorktreeCount Property = "display_worktree_count"
// WorktreeCountIcon shows before the worktree context
WorktreeCountIcon Property = "worktree_count_icon"
)
func (g *git) enabled() bool {
@ -189,6 +194,9 @@ func (g *git) string() string {
if g.repo.stashCount != 0 {
fmt.Fprintf(buffer, " %s%d", g.props.getString(StashCountIcon, "\uF692 "), g.repo.stashCount)
}
if g.repo.worktreeCount != 0 {
fmt.Fprintf(buffer, " %s%d", g.props.getString(WorktreeCountIcon, "\uf1bb "), g.repo.worktreeCount)
}
return buffer.String()
}
@ -270,6 +278,9 @@ func (g *git) setGitStatus() {
if g.props.getBool(DisplayStashCount, false) {
g.repo.stashCount = g.getStashContext()
}
if g.props.getBool(DisplayWorktreeCount, false) {
g.repo.worktreeCount = g.getWorktreeContext()
}
}
func (g *git) SetStatusColor() {
@ -315,30 +326,30 @@ func (g *git) getGitHEADContext(ref string) string {
ref = fmt.Sprintf("%s%s", branchIcon, ref)
}
// rebase
if g.hasGitFolder("rebase-merge") {
head := g.getGitFileContents("rebase-merge/head-name")
if g.env.hasFolder(g.repo.gitWorkingFolder + "/rebase-merge") {
head := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-merge/head-name")
origin := strings.Replace(head, "refs/heads/", "", 1)
origin = g.truncateBranch(origin)
onto := g.getGitRefFileSymbolicName("rebase-merge/onto")
onto = g.truncateBranch(onto)
step := g.getGitFileContents("rebase-merge/msgnum")
total := g.getGitFileContents("rebase-merge/end")
step := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-merge/msgnum")
total := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-merge/end")
icon := g.props.getString(RebaseIcon, "\uE728 ")
return fmt.Sprintf("%s%s%s onto %s%s (%s/%s) at %s", icon, branchIcon, origin, branchIcon, onto, step, total, ref)
}
if g.hasGitFolder("rebase-apply") {
head := g.getGitFileContents("rebase-apply/head-name")
if g.env.hasFolder(g.repo.gitWorkingFolder + "/rebase-apply") {
head := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-apply/head-name")
origin := strings.Replace(head, "refs/heads/", "", 1)
origin = g.truncateBranch(origin)
step := g.getGitFileContents("rebase-apply/next")
total := g.getGitFileContents("rebase-apply/last")
step := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-apply/next")
total := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-apply/last")
icon := g.props.getString(RebaseIcon, "\uE728 ")
return fmt.Sprintf("%s%s%s (%s/%s) at %s", icon, branchIcon, origin, step, total, ref)
}
// merge
if g.hasGitFile("MERGE_MSG") && g.hasGitFile("MERGE_HEAD") {
icon := g.props.getString(MergeIcon, "\uE727 ")
mergeContext := g.getGitFileContents("MERGE_MSG")
mergeContext := g.getGitFileContents(g.repo.gitWorkingFolder, "MERGE_MSG")
matches := findNamedRegexMatch(`Merge branch '(?P<head>.*)' into`, mergeContext)
if matches != nil && matches["head"] != "" {
branch := g.truncateBranch(matches["head"])
@ -351,15 +362,15 @@ func (g *git) getGitHEADContext(ref string) string {
// reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read
// the todo file.
if g.hasGitFile("CHERRY_PICK_HEAD") {
sha := g.getGitFileContents("CHERRY_PICK_HEAD")
sha := g.getGitFileContents(g.repo.gitWorkingFolder, "CHERRY_PICK_HEAD")
icon := g.props.getString(CherryPickIcon, "\uE29B ")
return fmt.Sprintf("%s%s onto %s", icon, sha[0:6], ref)
} else if g.hasGitFile("REVERT_HEAD") {
sha := g.getGitFileContents("REVERT_HEAD")
sha := g.getGitFileContents(g.repo.gitWorkingFolder, "REVERT_HEAD")
icon := g.props.getString(RevertIcon, "\uF0E2 ")
return fmt.Sprintf("%s%s onto %s", icon, sha[0:6], ref)
} else if g.hasGitFile("sequencer/todo") {
todo := g.getGitFileContents("sequencer/todo")
todo := g.getGitFileContents(g.repo.gitWorkingFolder, "sequencer/todo")
matches := findNamedRegexMatch(`^(?P<action>p|pick|revert)\s+(?P<sha>\S+)`, todo)
if matches != nil && matches["sha"] != "" {
action := matches["action"]
@ -389,25 +400,18 @@ func (g *git) hasGitFile(file string) bool {
return g.env.hasFilesInDir(g.repo.gitWorkingFolder, file)
}
func (g *git) hasGitFolder(folder string) bool {
path := g.repo.gitWorkingFolder + "/" + folder
return g.env.hasFolder(path)
}
func (g *git) getGitFileContents(file string) string {
path := g.repo.gitWorkingFolder + "/" + file
content := g.env.getFileContent(path)
return strings.Trim(content, " \r\n")
func (g *git) getGitFileContents(folder, file string) string {
return strings.Trim(g.env.getFileContent(folder+"/"+file), " \r\n")
}
func (g *git) getGitRefFileSymbolicName(refFile string) string {
ref := g.getGitFileContents(refFile)
ref := g.getGitFileContents(g.repo.gitWorkingFolder, refFile)
return g.getGitCommandOutput("name-rev", "--name-only", "--exclude=tags/*", ref)
}
func (g *git) getPrettyHEADName() string {
var ref string
HEAD := g.getGitFileContents("HEAD")
HEAD := g.getGitFileContents(g.repo.gitWorkingFolder, "HEAD")
branchPrefix := "ref: refs/heads/"
if strings.HasPrefix(HEAD, branchPrefix) {
ref = strings.TrimPrefix(HEAD, branchPrefix)
@ -462,7 +466,7 @@ func (g *git) parseGitStats(output []string, working bool) *gitStatus {
}
func (g *git) getStashContext() int {
stashContent := g.getGitFileContents("logs/refs/stash")
stashContent := g.getGitFileContents(g.repo.gitRootFolder, "logs/refs/stash")
if stashContent == "" {
return 0
}
@ -470,6 +474,14 @@ func (g *git) getStashContext() int {
return len(lines)
}
func (g *git) getWorktreeContext() int {
if !g.env.hasFolder(g.repo.gitRootFolder + "/worktrees") {
return 0
}
worktreeFolders := g.env.getFoldersList(g.repo.gitRootFolder + "/worktrees")
return len(worktreeFolders)
}
func (g *git) parseGitStatusInfo(branchInfo string) map[string]string {
var branchRegex = `^## (?P<local>\S+?)(\.{3}(?P<upstream>\S+?)( \[(?P<upstream_status>(ahead (?P<ahead>\d+)(, )?)?(behind (?P<behind>\d+))?(gone)?)])?)?$`
return findNamedRegexMatch(branchRegex, branchInfo)

View file

@ -45,7 +45,7 @@ const (
MappedLocationsEnabled Property = "mapped_locations_enabled"
// StackCountEnabled enables the stack count display
StackCountEnabled Property = "stack_count_enabled"
// Maximum path depth to display whithout shortening
// MaxDepth Maximum path depth to display whithout shortening
MaxDepth Property = "max_depth"
)

View file

@ -49,6 +49,11 @@ func (env *MockedEnvironment) getFileContent(file string) string {
return args.String(0)
}
func (env *MockedEnvironment) getFoldersList(path string) []string {
args := env.Called(path)
return args.Get(0).([]string)
}
func (env *MockedEnvironment) getPathSeperator() string {
args := env.Called(nil)
return args.String(0)

View file

@ -570,6 +570,12 @@
"description": "Display the stash count or not",
"default": false
},
"display_worktree_count": {
"type": "boolean",
"title": "Display Worktree Count",
"description": "Display the worktree count or not",
"default": false
},
"status_separator_icon": {
"type": "string",
"title": "Status Separator Icon",
@ -592,7 +598,13 @@
"type": "string",
"title": "Stash Count Icon",
"description": "The icon to display before the stash context",
"default": "\uF692"
"default": "\uF692 "
},
"worktree_count_icon": {
"type": "string",
"title": "Worktree Count Icon",
"description": "The icon to display before the worktree context",
"default": "\uF1bb "
},
"commit_icon": {
"type": "string",