From 7a6478269ca83ebf9dd41ce0d3b55aaaada4ae36 Mon Sep 17 00:00:00 2001 From: Jan De Dobbeleer Date: Fri, 26 Jul 2024 23:06:10 +0200 Subject: [PATCH] feat(git): mapped branches resolves #4979 --- src/go.mod | 2 +- src/segments/git.go | 32 +++--- src/segments/plastic.go | 6 +- src/segments/posh_git.go | 2 +- src/segments/scm.go | 30 ++++- src/segments/scm_test.go | 159 ++++++++++++++------------ src/terminal/writer.go | 8 +- themes/schema.json | 17 ++- website/docs/segments/scm/git.mdx | 47 ++++---- website/docs/segments/scm/plastic.mdx | 29 ++--- 10 files changed, 195 insertions(+), 137 deletions(-) diff --git a/src/go.mod b/src/go.mod index 58620295..06fdaf1e 100644 --- a/src/go.mod +++ b/src/go.mod @@ -35,7 +35,6 @@ require ( github.com/goccy/go-yaml v1.11.3 github.com/gookit/goutil v0.6.16 github.com/hashicorp/hcl/v2 v2.21.0 - github.com/mattn/go-runewidth v0.0.16 github.com/pelletier/go-toml/v2 v2.2.2 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 @@ -79,6 +78,7 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect diff --git a/src/segments/git.go b/src/segments/git.go index bb272ca6..51770e87 100644 --- a/src/segments/git.go +++ b/src/segments/git.go @@ -111,6 +111,8 @@ const ( UntrackedModes properties.Property = "untracked_modes" // IgnoreSubmodules list the optional ignore-submodules mode per repo IgnoreSubmodules properties.Property = "ignore_submodules" + // MappedBranches allows overriding certain branches with an icon/text + MappedBranches properties.Property = "mapped_branches" DETACHED = "(detached)" BRANCHPREFIX = "ref: refs/heads/" @@ -333,7 +335,7 @@ func (g *Git) getBareRepoInfo() { head := g.FileContents(g.workingDir, "HEAD") branchIcon := g.props.GetString(BranchIcon, "\uE0A0") g.Ref = strings.Replace(head, "ref: refs/heads/", "", 1) - g.HEAD = fmt.Sprintf("%s%s", branchIcon, g.Ref) + g.HEAD = fmt.Sprintf("%s%s", branchIcon, g.formatBranch(g.Ref)) if !g.props.GetBool(FetchUpstreamIcon, false) { return } @@ -550,23 +552,28 @@ func (g *Git) setGitStatus() { g.Working.add(workingCode) g.Staging.add(stagingCode) } + const ( HASH = "# branch.oid " REF = "# branch.head " UPSTREAM = "# branch.upstream " BRANCHSTATUS = "# branch.ab " ) + // firstly assume that upstream is gone g.UpstreamGone = true statusFormats := g.props.GetKeyValueMap(StatusFormats, map[string]string{}) + g.Working = &GitStatus{ScmStatus: ScmStatus{Formats: statusFormats}} g.Staging = &GitStatus{ScmStatus: ScmStatus{Formats: statusFormats}} + untrackedMode := g.getUntrackedFilesMode() args := []string{"status", untrackedMode, "--branch", "--porcelain=2"} ignoreSubmodulesMode := g.getIgnoreSubmodulesMode() if len(ignoreSubmodulesMode) > 0 { args = append(args, ignoreSubmodulesMode) } + output := g.getGitCommandOutput(args...) for _, line := range strings.Split(output, "\n") { if strings.HasPrefix(line, HASH) && len(line) >= len(HASH)+7 { @@ -574,16 +581,19 @@ func (g *Git) setGitStatus() { g.Hash = line[len(HASH):] continue } + if strings.HasPrefix(line, REF) && len(line) > len(REF) { g.Ref = line[len(REF):] continue } + if strings.HasPrefix(line, UPSTREAM) && len(line) > len(UPSTREAM) { // status reports upstream, but upstream may be gone (must check BRANCHSTATUS) g.Upstream = line[len(UPSTREAM):] g.UpstreamGone = true continue } + if strings.HasPrefix(line, BRANCHSTATUS) && len(line) > len(BRANCHSTATUS) { status := line[len(BRANCHSTATUS):] splitted := strings.Split(status, " ") @@ -596,6 +606,7 @@ func (g *Git) setGitStatus() { g.UpstreamGone = false continue } + addToStatus(line) } } @@ -615,7 +626,7 @@ func (g *Git) setGitHEADContext() { g.Detached = true g.setPrettyHEADName() } else { - head := g.formatHEAD(g.Ref) + head := g.formatBranch(g.Ref) g.HEAD = fmt.Sprintf("%s%s", branchIcon, head) } @@ -633,7 +644,7 @@ func (g *Git) setGitHEADContext() { origin = formatDetached() } else { head = strings.Replace(head, "refs/heads/", "", 1) - origin = branchIcon + g.formatHEAD(head) + origin = branchIcon + g.formatBranch(head) } return origin } @@ -642,7 +653,7 @@ func (g *Git) setGitHEADContext() { g.Rebase = true origin := getPrettyNameOrigin("rebase-merge/head-name") onto := g.getGitRefFileSymbolicName("rebase-merge/onto") - onto = g.formatHEAD(onto) + onto = g.formatBranch(onto) step := g.FileContents(g.workingDir, "rebase-merge/msgnum") total := g.FileContents(g.workingDir, "rebase-merge/end") icon := g.props.GetString(RebaseIcon, "\uE728 ") @@ -680,7 +691,7 @@ func (g *Git) setGitHEADContext() { theirs = g.formatSHA(matches["theirs"]) default: headIcon = branchIcon - theirs = g.formatHEAD(matches["theirs"]) + theirs = g.formatBranch(matches["theirs"]) } g.HEAD = fmt.Sprintf("%s%s%s into %s", icon, headIcon, theirs, formatDetached()) return @@ -732,15 +743,6 @@ func (g *Git) setGitHEADContext() { g.HEAD = formatDetached() } -func (g *Git) formatHEAD(head string) string { - maxLength := g.props.GetInt(BranchMaxLength, 0) - if maxLength == 0 || len(head) < maxLength { - return head - } - symbol := g.props.GetString(TruncateSymbol, "") - return head[0:maxLength] + symbol -} - func (g *Git) formatSHA(sha string) string { if len(sha) <= 7 { return sha @@ -765,7 +767,7 @@ func (g *Git) setPrettyHEADName() { if strings.HasPrefix(HEADRef, BRANCHPREFIX) { branchName := strings.TrimPrefix(HEADRef, BRANCHPREFIX) g.Ref = branchName - g.HEAD = fmt.Sprintf("%s%s", g.props.GetString(BranchIcon, "\uE0A0"), g.formatHEAD(branchName)) + g.HEAD = fmt.Sprintf("%s%s", g.props.GetString(BranchIcon, "\uE0A0"), g.formatBranch(branchName)) return } // no branch, points to commit diff --git a/src/segments/plastic.go b/src/segments/plastic.go index 07c0faee..0cfac4b1 100644 --- a/src/segments/plastic.go +++ b/src/segments/plastic.go @@ -140,23 +140,27 @@ func (p *Plastic) getHeadChangeset() int { func (p *Plastic) setSelector() { var ref string selector := p.FileContents(p.plasticWorkspaceFolder+"/.plastic/", "plastic.selector") + // changeset ref = p.parseChangesetSelector(selector) if len(ref) > 0 { p.Selector = fmt.Sprintf("%s%s", p.props.GetString(CommitIcon, "\uF417"), ref) return } + // fallback to label ref = p.parseLabelSelector(selector) if len(ref) > 0 { p.Selector = fmt.Sprintf("%s%s", p.props.GetString(TagIcon, "\uF412"), ref) return } + // fallback to branch/smartbranch ref = p.parseBranchSelector(selector) if len(ref) > 0 { - ref = p.truncateBranch(ref) + ref = p.formatBranch(ref) } + p.Selector = fmt.Sprintf("%s%s", p.props.GetString(BranchIcon, "\uE0A0"), ref) } diff --git a/src/segments/posh_git.go b/src/segments/posh_git.go index 8a7c6e6e..41a7b59e 100644 --- a/src/segments/posh_git.go +++ b/src/segments/posh_git.go @@ -93,5 +93,5 @@ func (g *Git) parsePoshGitHEAD(head string) string { return fmt.Sprintf("%s%s", g.props.GetString(TagIcon, "\uF412"), head) } // regular branch - return fmt.Sprintf("%s%s", g.props.GetString(BranchIcon, "\uE0A0"), g.formatHEAD(head)) + return fmt.Sprintf("%s%s", g.props.GetString(BranchIcon, "\uE0A0"), g.formatBranch(head)) } diff --git a/src/segments/scm.go b/src/segments/scm.go index 33260c93..07eb09bb 100644 --- a/src/segments/scm.go +++ b/src/segments/scm.go @@ -3,6 +3,7 @@ package segments import ( "fmt" "strings" + "unicode/utf8" "github.com/jandedobbeleer/oh-my-posh/src/properties" "github.com/jandedobbeleer/oh-my-posh/src/runtime" @@ -109,21 +110,40 @@ func (s *scm) Init(props properties.Properties, env runtime.Environment) { s.env = env } -func (s *scm) truncateBranch(branch string) string { - fullBranchPath := s.props.GetBool(FullBranchPath, true) - maxLength := s.props.GetInt(BranchMaxLength, 0) +func (s *scm) formatBranch(branch string) string { + mappedBranches := s.props.GetKeyValueMap(MappedBranches, make(map[string]string)) + for key, value := range mappedBranches { + matchSubFolders := strings.HasSuffix(key, "*") + if matchSubFolders && len(key) > 1 { + key = key[0 : len(key)-1] // remove trailing /* or \* + } + + if !strings.HasPrefix(branch, key) { + continue + } + + branch = strings.Replace(branch, key, value, 1) + break + } + + fullBranchPath := s.props.GetBool(FullBranchPath, true) if !fullBranchPath && strings.Contains(branch, "/") { index := strings.LastIndex(branch, "/") branch = branch[index+1:] } + maxLength := s.props.GetInt(BranchMaxLength, 0) if maxLength == 0 || len(branch) <= maxLength { return branch } - symbol := s.props.GetString(TruncateSymbol, "") - return branch[0:maxLength] + symbol + truncateSymbol := s.props.GetString(TruncateSymbol, "") + lenTruncateSymbol := utf8.RuneCountInString(truncateSymbol) + maxLength -= lenTruncateSymbol + + runes := []rune(branch) + return string(runes[0:maxLength]) + truncateSymbol } func (s *scm) shouldIgnoreRootRepository(rootDir string) bool { diff --git a/src/segments/scm_test.go b/src/segments/scm_test.go index 895ce86e..f7ff2859 100644 --- a/src/segments/scm_test.go +++ b/src/segments/scm_test.go @@ -105,78 +105,6 @@ func TestScmStatusString(t *testing.T) { } } -func TestTruncateBranch(t *testing.T) { - cases := []struct { - Case string - Expected string - Branch string - FullBranch bool - MaxLength any - }{ - {Case: "No limit", Expected: "are-belong-to-us", Branch: "/all-your-base/are-belong-to-us", FullBranch: false}, - {Case: "No limit - larger", Expected: "are-belong", Branch: "/all-your-base/are-belong-to-us", FullBranch: false, MaxLength: 10.0}, - {Case: "No limit - smaller", Expected: "all-your-base", Branch: "/all-your-base", FullBranch: false, MaxLength: 13.0}, - {Case: "Invalid setting", Expected: "all-your-base", Branch: "/all-your-base", FullBranch: false, MaxLength: "burp"}, - {Case: "Lower than limit", Expected: "all-your-base", Branch: "/all-your-base", FullBranch: false, MaxLength: 20.0}, - - {Case: "No limit - full branch", Expected: "/all-your-base/are-belong-to-us", Branch: "/all-your-base/are-belong-to-us", FullBranch: true}, - {Case: "No limit - larger - full branch", Expected: "/all-your-base", Branch: "/all-your-base/are-belong-to-us", FullBranch: true, MaxLength: 14.0}, - {Case: "No limit - smaller - full branch ", Expected: "/all-your-base", Branch: "/all-your-base", FullBranch: true, MaxLength: 14.0}, - {Case: "Invalid setting - full branch", Expected: "/all-your-base", Branch: "/all-your-base", FullBranch: true, MaxLength: "burp"}, - {Case: "Lower than limit - full branch", Expected: "/all-your-base", Branch: "/all-your-base", FullBranch: true, MaxLength: 20.0}, - } - - for _, tc := range cases { - props := properties.Map{ - BranchMaxLength: tc.MaxLength, - FullBranchPath: tc.FullBranch, - } - p := &Plastic{ - scm: scm{ - props: props, - }, - } - assert.Equal(t, tc.Expected, p.truncateBranch(tc.Branch), tc.Case) - } -} - -func TestTruncateBranchWithSymbol(t *testing.T) { - cases := []struct { - Case string - Expected string - Branch string - FullBranch bool - MaxLength any - TruncateSymbol any - }{ - {Case: "No limit", Expected: "are-belong-to-us", Branch: "/all-your-base/are-belong-to-us", FullBranch: false, TruncateSymbol: "..."}, - {Case: "No limit - larger", Expected: "are-belong...", Branch: "/all-your-base/are-belong-to-us", FullBranch: false, MaxLength: 10.0, TruncateSymbol: "..."}, - {Case: "No limit - smaller", Expected: "all-your-base", Branch: "/all-your-base", FullBranch: false, MaxLength: 13.0, TruncateSymbol: "..."}, - {Case: "Invalid setting", Expected: "all-your-base", Branch: "/all-your-base", FullBranch: false, MaxLength: "burp", TruncateSymbol: "..."}, - {Case: "Lower than limit", Expected: "all-your-base", Branch: "/all-your-base", FullBranch: false, MaxLength: 20.0, TruncateSymbol: "..."}, - - {Case: "No limit - full branch", Expected: "/all-your-base/are-belong-to-us", Branch: "/all-your-base/are-belong-to-us", FullBranch: true, TruncateSymbol: "..."}, - {Case: "No limit - larger - full branch", Expected: "/all-your-base...", Branch: "/all-your-base/are-belong-to-us", FullBranch: true, MaxLength: 14.0, TruncateSymbol: "..."}, - {Case: "No limit - smaller - full branch ", Expected: "/all-your-base", Branch: "/all-your-base", FullBranch: true, MaxLength: 14.0, TruncateSymbol: "..."}, - {Case: "Invalid setting - full branch", Expected: "/all-your-base", Branch: "/all-your-base", FullBranch: true, MaxLength: "burp", TruncateSymbol: "..."}, - {Case: "Lower than limit - full branch", Expected: "/all-your-base", Branch: "/all-your-base", FullBranch: true, MaxLength: 20.0, TruncateSymbol: "..."}, - } - - for _, tc := range cases { - props := properties.Map{ - BranchMaxLength: tc.MaxLength, - TruncateSymbol: tc.TruncateSymbol, - FullBranchPath: tc.FullBranch, - } - p := &Plastic{ - scm: scm{ - props: props, - }, - } - assert.Equal(t, tc.Expected, p.truncateBranch(tc.Branch), tc.Case) - } -} - func TestHasCommand(t *testing.T) { cases := []struct { Case string @@ -212,3 +140,90 @@ func TestHasCommand(t *testing.T) { assert.Equal(t, tc.ExpectedCommand, s.command, tc.Case) } } + +func TestFormatBranch(t *testing.T) { + cases := []struct { + Case string + Expected string + Input string + MappedBranches map[string]string + BranchMaxLength int + TruncateSymbol string + NoFullBranchPath bool + }{ + { + Case: "No settings", + Input: "main", + Expected: "main", + }, + { + Case: "BranchMaxLength higher than branch name", + Input: "main", + Expected: "main", + BranchMaxLength: 10, + }, + { + Case: "BranchMaxLength lower than branch name", + Input: "feature/test-this-branch", + Expected: "featu", + BranchMaxLength: 5, + }, + { + Case: "BranchMaxLength lower than branch name, with truncate symbol", + Input: "feature/test-this-branch", + Expected: "feat…", + BranchMaxLength: 5, + TruncateSymbol: "…", + }, + { + Case: "BranchMaxLength lower than branch name, with truncate symbol and no FullBranchPath", + Input: "feature/test-this-branch", + Expected: "test…", + BranchMaxLength: 5, + TruncateSymbol: "…", + NoFullBranchPath: true, + }, + { + Case: "BranchMaxLength lower to branch name, with truncate symbol", + Input: "feat", + Expected: "feat", + BranchMaxLength: 5, + TruncateSymbol: "…", + }, + { + Case: "Branch mapping, no BranchMaxLength", + Input: "feat/my-new-feature", + Expected: "🚀 my-new-feature", + MappedBranches: map[string]string{ + "feat/*": "🚀 ", + "bug/*": "🐛 ", + }, + }, + { + Case: "Branch mapping, with BranchMaxLength", + Input: "feat/my-new-feature", + Expected: "🚀 my-", + BranchMaxLength: 5, + MappedBranches: map[string]string{ + "feat/*": "🚀 ", + "bug/*": "🐛 ", + }, + }, + } + + for _, tc := range cases { + g := &Git{ + scm: scm{ + props: properties.Map{ + MappedBranches: tc.MappedBranches, + BranchMaxLength: tc.BranchMaxLength, + TruncateSymbol: tc.TruncateSymbol, + FullBranchPath: !tc.NoFullBranchPath, + }, + }, + } + + got := g.formatBranch(tc.Input) + assert.Equal(t, tc.Expected, got, tc.Case) + } +} diff --git a/src/terminal/writer.go b/src/terminal/writer.go index 617f167a..7bafc1f6 100644 --- a/src/terminal/writer.go +++ b/src/terminal/writer.go @@ -4,18 +4,14 @@ import ( "fmt" "os" "strings" + "unicode/utf8" "github.com/jandedobbeleer/oh-my-posh/src/color" "github.com/jandedobbeleer/oh-my-posh/src/log" "github.com/jandedobbeleer/oh-my-posh/src/regex" "github.com/jandedobbeleer/oh-my-posh/src/shell" - "github.com/mattn/go-runewidth" ) -func init() { - runewidth.DefaultCondition.EastAsianWidth = false -} - type style struct { AnchorStart string AnchorEnd string @@ -400,7 +396,7 @@ func write(s rune) { } } - length += runewidth.RuneWidth(s) + length += utf8.RuneCountInString(string(s)) lastRune = s builder.WriteRune(s) } diff --git a/themes/schema.json b/themes/schema.json index ce2a7c37..dac6b650 100644 --- a/themes/schema.json +++ b/themes/schema.json @@ -144,9 +144,15 @@ "full_branch_path": { "type": "boolean", "title": "Full branch path", - "description": "display the full branch path instead of only the branch name", + "description": "display the full branch path instead of only the last part (e.g. feature/branch instead of branch)", "default": true }, + "mapped_branches": { + "type": "object", + "title": "Mapped Branches", + "description": "Custom glyph/text for specific branches", + "default": {} + }, "extra_prompt": { "type": "object", "default": {}, @@ -1467,6 +1473,12 @@ "title": "Status string formats", "description": "a key, value map representing the remote URL (or a part of that URL) and icon to use in case the upstream URL contains the key. These get precedence over the standard icons", "default": {} + }, + "mapped_branches": { + "$ref": "#/definitions/mapped_branches" + }, + "full_branch_path": { + "$ref": "#/definitions/full_branch_path" } } } @@ -3655,6 +3667,9 @@ }, "native_fallback": { "$ref": "#/definitions/native_fallback" + }, + "mapped_branches": { + "$ref": "#/definitions/mapped_branches" } } } diff --git a/website/docs/segments/scm/git.mdx b/website/docs/segments/scm/git.mdx index 4709e5b2..2857950f 100644 --- a/website/docs/segments/scm/git.mdx +++ b/website/docs/segments/scm/git.mdx @@ -9,26 +9,6 @@ sidebar_label: Git Display git information when in a git repository. Also works for subfolders. For maximum compatibility, make sure your `git` executable is up-to-date (when branch or status information is incorrect for example). -:::tip -If you want to display the default [posh-git][poshgit] output, **do not** use this segment -but add the following snippet after initializing Oh My Posh in your `$PROFILE`: - -```powershell -function Set-PoshGitStatus { - $global:GitStatus = Get-GitStatus - $env:POSH_GIT_STRING = Write-GitStatus -Status $global:GitStatus -} -New-Alias -Name 'Set-PoshContext' -Value 'Set-PoshGitStatus' -Scope Global -Force -``` - -You can then use the `POSH_GIT_STRING` environment variable in a [text segment][text]: - -```json -"template": "{{ if .Env.POSH_GIT_STRING }} {{ .Env.POSH_GIT_STRING }} {{ end }}" -``` - -::: - ## Sample Configuration import Config from "@site/src/components/Config.js"; @@ -55,6 +35,10 @@ import Config from "@site/src/components/Config.js"; "/Users/user/Projects/oh-my-posh/": "no", }, source: "cli", + mapped_branches: { + "feat/*": "🚀 ", + "bug/*": "🐛 ", + }, }, }} /> @@ -78,6 +62,8 @@ You can set the following properties to `true` to enable fetching additional inf | `fetch_user` | [`User`](#user) | `false` | fetch the current configured user for the repository | | `status_formats` | `map[string]string` | | a key, value map allowing to override how individual status items are displayed. For example, `"status_formats": { "Added": "Added: %d" }` will display the added count as `Added: 1` instead of `+1`. See the [Status](#status) section for available overrides. | | `source` | `string` | `cli` | | +| `mapped_branches` | `object` | | custom glyph/text for specific branches. You can use `*` at the end as a wildcard character for matching | +| `full_branch_path` | `bool` | `true` | display the full branch path instead of only the last part (e.g. `feature/branch` instead of `branch`) | ### Icons @@ -197,11 +183,30 @@ Local changes use the following syntax: | `.Name` | `string` | the user's name | | `.Email` | `string` | the user's email | +## posh-git + +If you want to display the default [posh-git][poshgit] output, **do not** use this segment +but add the following snippet after initializing Oh My Posh in your `$PROFILE`: + +```powershell +function Set-PoshGitStatus { + $global:GitStatus = Get-GitStatus + $env:POSH_GIT_STRING = Write-GitStatus -Status $global:GitStatus +} +New-Alias -Name 'Set-PoshContext' -Value 'Set-PoshGitStatus' -Scope Global -Force +``` + +You can then use the `POSH_GIT_STRING` environment variable in a [text segment][text]: + +```json +"template": "{{ if .Env.POSH_GIT_STRING }} {{ .Env.POSH_GIT_STRING }} {{ end }}" +``` + [poshgit]: https://github.com/dahlbyk/posh-git [templates]: /docs/configuration/templates [hyperlinks]: /docs/configuration/templates#custom [untracked]: https://git-scm.com/docs/git-status#Documentation/git-status.txt---untracked-filesltmodegt [submodules]: https://git-scm.com/docs/git-status#Documentation/git-status.txt---ignore-submodulesltwhengt [kraken-ref]: https://www.gitkraken.com/invite/nQmDPR9D -[text]: text.mdx +[text]: /docs/segments/system/text [exclude_folders]: /docs/configuration/segment#include--exclude-folders diff --git a/website/docs/segments/scm/plastic.mdx b/website/docs/segments/scm/plastic.mdx index 751bf68a..3eec026d 100644 --- a/website/docs/segments/scm/plastic.mdx +++ b/website/docs/segments/scm/plastic.mdx @@ -50,25 +50,26 @@ by leaving a like! As doing multiple `cm` calls can slow down the prompt experience, we do not fetch information by default. You can set the following property to `true` to enable fetching additional information (and populate the template). -| Name | Type | Default | Description | +| Name | Type | Default | Description | | ---------------- | :-----------------: | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `fetch_status` | `boolean` | `false` | fetch the local changes | +| `fetch_status` | `boolean` | `false` | fetch the local changes | | `status_formats` | `map[string]string` | | a key, value map allowing to override how individual status items are displayed. For example, `"status_formats": { "Added": "Added: %d" }` will display the added count as `Added: 1` instead of `+1`. See the [Status](#status) section for available overrides | ### Icons #### Branch -| Name | Type | Default | Description | -| ------------------- | :------: | :------: | -------------------------------------------------------------------------- | -| `branch_icon` | `string` | `\uE0A0` | the icon to use in front of the git branch name | -| `full_branch_path` | `bool` | `true` | display the full branch path: `_/main/fix-001_` instead of `_fix-001_` | -| `branch_max_length` | `int` | `0` | the max length for the displayed branch name where `0` implies full length | -| `truncate_symbol` | `string` | | the icon to display when a branch name is truncated | +| Name | Type | Default | Description | +| ------------------- | :------: | :------: | -------------------------------------------------------------------------------------------------------- | +| `branch_icon` | `string` | `\uE0A0` | the icon to use in front of the git branch name | +| `full_branch_path` | `bool` | `true` | display the full branch path instead of only the last part (e.g. `feature/branch` instead of `branch`) | +| `branch_max_length` | `int` | `0` | the max length for the displayed branch name where `0` implies full length | +| `truncate_symbol` | `string` | | the icon to display when a branch name is truncated | +| `mapped_branches` | `object` | | custom glyph/text for specific branches. You can use `*` at the end as a wildcard character for matching | #### Selector -| Name | Type | Default | Description | +| Name | Type | Default | Description | | ------------- | :------: | :------: | -------------------------------------------------------------- | | `commit_icon` | `string` | `\uF417` | icon/text to display before the commit context (detached HEAD) | | `tag_icon` | `string` | `\uF412` | icon/text to display before the tag context | @@ -85,11 +86,11 @@ You can set the following property to `true` to enable fetching additional infor ### Properties -| Name | Type | Description | -| --------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `.Selector` | `string` | the current selector context (branch/changeset/label) | -| `.Behind` | `bool` | the current workspace is behind and changes are incoming | -| `.Status` | `Status` | changes in the workspace (see below) | +| Name | Type | Description | +| --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `.Selector` | `string` | the current selector context (branch/changeset/label) | +| `.Behind` | `bool` | the current workspace is behind and changes are incoming | +| `.Status` | `Status` | changes in the workspace (see below) | | `.MergePending` | `bool` | if a merge is pending and needs to be committed (known issue: when no file is left after a _Change/Delete conflict_ merge, the `MergePending` property is not set) | ### Status