Repository: JanDeDobbeleer/oh-my-posh
Pull request #3320 (2023-01-06 07:07:09)

git icons
### Prerequisites

- [x] I have read and understood the [contributing guide][CONTRIBUTING.md]
- [x] The commit message follows the [conventional commits][cc] guidelines
- [x] Tests for the changes have been added (for bug fixes/features)
- [x] Docs have been added/updated (for bug fixes/features)

### Description

- refactor(git): use map for upstream icons
- feat(git): allow adding own remote icons

<!---

Tips:

If you're not comfortable with working with Git, we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/src/segments/git.go b/src/segments/git.go
index 0808e86f..1149f523 100644
--- a/src/segments/git.go
+++ b/src/segments/git.go
@@ -72,6 +72,8 @@ const (
 	TagIcon properties.Property = "tag_icon"
 	// MergeIcon shows before the merge context
 	MergeIcon properties.Property = "merge_icon"
+	// UpstreamIcons allows to add custom upstream icons
+	UpstreamIcons properties.Property = "upstream_icons"
 	// GithubIcon shows√ when upstream is github
 	GithubIcon properties.Property = "github_icon"
 	// BitbucketIcon shows  when upstream is bitbucket
@@ -337,17 +339,29 @@ func (g *Git) getUpstreamIcon() string {
 	}
 	g.RawUpstreamURL = g.getRemoteURL()
 	g.UpstreamURL = cleanSSHURL(g.RawUpstreamURL)
-	if strings.Contains(g.UpstreamURL, "github") {
-		return g.props.GetString(GithubIcon, "\uF408 ")
-	}
-	if strings.Contains(g.UpstreamURL, "gitlab") {
-		return g.props.GetString(GitlabIcon, "\uF296 ")
+
+	// allow overrides first
+	custom := g.props.GetKeyValueMap(UpstreamIcons, map[string]string{})
+	for key, value := range custom {
+		if strings.Contains(g.UpstreamURL, key) {
+			return value
+		}
 	}
-	if strings.Contains(g.UpstreamURL, "bitbucket") {
-		return g.props.GetString(BitbucketIcon, "\uF171 ")
+
+	defaults := map[string]struct {
+		Icon    properties.Property
+		Default string
+	}{
+		"github":           {GithubIcon, "\uF408 "},
+		"gitlab":           {GitlabIcon, "\uF296 "},
+		"bitbucket":        {BitbucketIcon, "\uF171 "},
+		"dev.azure.com":    {AzureDevOpsIcon, "\uFD03 "},
+		"visualstudio.com": {AzureDevOpsIcon, "\uFD03 "},
 	}
-	if strings.Contains(g.UpstreamURL, "dev.azure.com") || strings.Contains(g.UpstreamURL, "visualstudio.com") {
-		return g.props.GetString(AzureDevOpsIcon, "\uFD03 ")
+	for key, value := range defaults {
+		if strings.Contains(g.UpstreamURL, key) {
+			return g.props.GetString(value.Icon, value.Default)
+		}
 	}
 	return g.props.GetString(GitIcon, "\uE5FB ")
 }
diff --git a/website/docs/segments/git.mdx b/website/docs/segments/git.mdx
index d368c721..85e4bf56 100644
--- a/website/docs/segments/git.mdx
+++ b/website/docs/segments/git.mdx
@@ -67,14 +67,14 @@ You can then use the `POSH_GIT_STRING` environment variable in a [text segment][
 As doing multiple git calls can slow down the prompt experience, we do not fetch information by default.
 You can set the following properties to `true` to enable fetching additional information (and populate the template).
 
-| Name                   | Type                | Description                                                                                                                                                                                                                                                                                                                           |
-| ---------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `fetch_status`         | `boolean`           | fetch the local changes - defaults to `false`                                                                                                                                                                                                                                                                                         |
-| `fetch_upstream_icon`  | `boolean`           | fetch upstream icon - defaults to `false`                                                                                                                                                                                                                                                                                             |
-| `fetch_bare_info`      | `boolean`           | fetch bare repo info - defaults to `false`                                                                                                                                                                                                                                                                                            |
-| `untracked_modes`      | `map[string]string` | map of repo's where to override the default [untracked files mode][untracked]:<ul><li>`no`</li><li>`normal`</li><li>`all`</li></ul>For example `"untracked_modes": { "/Users/me/repos/repo1": "no" }` - defaults to `normal` for all repo's. If you want to override for all repo's, use `*` to set the mode instead of the repo path |
-| `ignore_submodules`    | `map[string]string` | map of repo's where to change the [--ignore-submodules][submodules] flag (`none`, `untracked`, `dirty` or `all`). For example `"ignore_submodules": { "/Users/me/repos/repo1": "all" }`. If you want to override for all repo's, use `*` to set the mode instead of the repo path                                                     |
-| `native_fallback`      | `boolean`           | when set to `true` and `git.exe` is not available when inside a WSL2 shared Windows drive, we will fallback to the native git executable to fetch data. Not all information can be displayed in this case. Defaults to `false`                                                                                                        |
+| Name                  | Type                | Description                                                                                                                                                                                                                                                                                                                           |
+| --------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `fetch_status`        | `boolean`           | fetch the local changes - defaults to `false`                                                                                                                                                                                                                                                                                         |
+| `fetch_upstream_icon` | `boolean`           | fetch upstream icon - defaults to `false`                                                                                                                                                                                                                                                                                             |
+| `fetch_bare_info`     | `boolean`           | fetch bare repo info - defaults to `false`                                                                                                                                                                                                                                                                                            |
+| `untracked_modes`     | `map[string]string` | map of repo's where to override the default [untracked files mode][untracked]:<ul><li>`no`</li><li>`normal`</li><li>`all`</li></ul>For example `"untracked_modes": { "/Users/me/repos/repo1": "no" }` - defaults to `normal` for all repo's. If you want to override for all repo's, use `*` to set the mode instead of the repo path |
+| `ignore_submodules`   | `map[string]string` | map of repo's where to change the [--ignore-submodules][submodules] flag (`none`, `untracked`, `dirty` or `all`). For example `"ignore_submodules": { "/Users/me/repos/repo1": "all" }`. If you want to override for all repo's, use `*` to set the mode instead of the repo path                                                     |
+| `native_fallback`     | `boolean`           | when set to `true` and `git.exe` is not available when inside a WSL2 shared Windows drive, we will fallback to the native git executable to fetch data. Not all information can be displayed in this case. Defaults to `false`                                                                                                        |
 
 ### Icons
 
@@ -104,13 +104,14 @@ You can set the following properties to `true` to enable fetching additional inf
 
 #### Upstream
 
-| Name                | Type     | Description                                                                        |
-| ------------------- | -------- | ---------------------------------------------------------------------------------- |
-| `github_icon`       | `string` | icon/text to display when the upstream is Github - defaults to `\uF408 `           |
-| `gitlab_icon`       | `string` | icon/text to display when the upstream is Gitlab - defaults to `\uF296 `           |
-| `bitbucket_icon`    | `string` | icon/text to display when the upstream is Bitbucket - defaults to `\uF171 `        |
-| `azure_devops_icon` | `string` | icon/text to display when the upstream is Azure DevOps - defaults to `\uFD03 `     |
-| `git_icon`          | `string` | icon/text to display when the upstream is not known/mapped - defaults to `\uE5FB ` |
+| Name                | Type                | Description                                                                                                                                                                  |
+| ------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `github_icon`       | `string`            | icon/text to display when the upstream is Github - defaults to `\uF408 `                                                                                                     |
+| `gitlab_icon`       | `string`            | icon/text to display when the upstream is Gitlab - defaults to `\uF296 `                                                                                                     |
+| `bitbucket_icon`    | `string`            | icon/text to display when the upstream is Bitbucket - defaults to `\uF171 `                                                                                                  |
+| `azure_devops_icon` | `string`            | icon/text to display when the upstream is Azure DevOps - defaults to `\uFD03 `                                                                                               |
+| `git_icon`          | `string`            | icon/text to display when the upstream is not known/mapped - defaults to `\uE5FB `                                                                                           |
+| `upstream_icons`    | `map[string]string` | 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 |
 
 ## Template ([info][templates])
 


Test patch:
diff --git a/src/segments/git_test.go b/src/segments/git_test.go
index de755df9..ec048567 100644
--- a/src/segments/git_test.go
+++ b/src/segments/git_test.go
@@ -598,6 +598,7 @@ func TestGitUpstream(t *testing.T) {
 		{Case: "Azure DevOps", Expected: "AD", Upstream: "dev.azure.com/test"},
 		{Case: "Azure DevOps Dos", Expected: "AD", Upstream: "test.visualstudio.com"},
 		{Case: "Gitstash", Expected: "G", Upstream: "gitstash.com/test"},
+		{Case: "My custom server", Expected: "CU", Upstream: "mycustom.server/test"},
 	}
 	for _, tc := range cases {
 		env := &mock.MockedEnvironment{}
@@ -611,6 +612,9 @@ func TestGitUpstream(t *testing.T) {
 			BitbucketIcon:   "BB",
 			AzureDevOpsIcon: "AD",
 			GitIcon:         "G",
+			UpstreamIcons: map[string]string{
+				"mycustom.server": "CU",
+			},
 		}
 		g := &Git{
 			scm: scm{