Repository: git-town/git-town

Issue:
"display branch types" config option
The [switch](https://www.git-town.com/commands/switch.html#-d--display-types) command has an option `--display-types`. Some people might want to enable this always, in all dialogs. So making this a proper config option seems appropriate.

In config file:

```toml
[branches]
display-types = "all"
```

Git config:

```sh
git config git-town.display-types no
```

CLI

```sh
git-town switch --display-types
```


Hints from issue comments:
@kevgo: Alternative form: "show-types". Advantages are:

- its shorter
- "display-types" could be confused with "types that are for display only"

Config file:

```toml
[branches]
show-types = true
```

Git config:

```
git config git-town.show-types true
```


CLI

```
git town switch --show-types
```@kevgo: A downside of `show-types` is that the current CLI flag is already `--display-types`. Changing it would require changes from users and a major version bump.@kevgo: Possible values for `display-types`:
- `all` always displays branch types
- `no` never displays any branch types
- `no feature prototype` displays all branch types except the `feature` and `prototype` branch types
- `perennial observed` displays only the `perennial` and `observed` branch types
- CLI set to no value (`git town branch --display-types`) ==> `all`
- Git metadata set to no value (`git config git-town.display-types ""`) ==> error
- config file set to no value (`display-types = ""`) ==> error
- not set ==> `no feature main`@kevgo: This is now available in [v22.2](https://github.com/git-town/git-town/releases/tag/v22.2.0).

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c55fda6e4..46e616e6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Git Town Changelog
 
+## 22.2.0 (unreleased)
+
+#### New Features
+
+- the new display-types configuration setting allows configuring which branch types Git Town shows in lists of branches. This affects the [branch](https://www.git-town.com/commands/branch.html), [switch](https://www.git-town.com/commands/switch.html), [config](https://www.git-town.com/commands/config.html), [down](https://www.git-town.com/commands/down.html), [up](https://www.git-town.com/commands/up.html) commands and all internal dialogs that ask to select a branch or parent branch.
+
 ## 22.1.0 (2025-10-13)
 
 #### New Features
diff --git a/features/branch/display_types/cli.feature b/features/branch/display_types/cli.feature
new file mode 100644
index 000000000..48693c463
--- /dev/null
+++ b/features/branch/display_types/cli.feature
@@ -0,0 +1,98 @@
+Feature: specify which branch types are displayed via the CLI
+
+  Background:
+    Given a Git repo with origin
+    And the branches
+      | NAME         | TYPE         | PARENT | LOCATIONS     |
+      | alpha        | feature      | main   | local, origin |
+      | beta         | feature      | alpha  | local, origin |
+      | gamma        | feature      | beta   | local, origin |
+      | observed     | observed     |        | local, origin |
+      | contribution | contribution |        | local, origin |
+      | prototype    | prototype    | main   | local         |
+      | parked       | parked       | main   | local         |
+      | perennial    | perennial    |        | local, origin |
+    And the current branch is "beta"
+
+  Scenario Outline: show all types
+    When I run "git-town branch <FLAG>"
+    Then Git Town prints:
+      """
+        main  (main)
+          alpha  (feature)
+      *     beta  (feature)
+              gamma  (feature)
+          parked  (parked)
+          prototype  (prototype)
+        contribution  (contribution)
+        observed  (observed)
+        perennial  (perennial)
+      """
+
+    Examples:
+      | FLAG                |
+      | --display-types     |
+      | --display-types=all |
+      | --display-types=ALL |
+      | -d                  |
+      | -d=all              |
+
+  Scenario Outline: show only the given branch types
+    When I run "git-town branch --display-types=<FLAG>"
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked
+          prototype  (prototype)
+        contribution
+        observed  (observed)
+        perennial
+      """
+
+    Examples:
+      | FLAG               |
+      | prototype+observed |
+      | prototype-observed |
+      | prototype_observed |
+      | prototype&observed |
+
+  Scenario: show no types
+    When I run "git-town branch --display-types=no"
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked
+          prototype
+        contribution
+        observed
+        perennial
+      """
+
+  Scenario Outline: show all except the given branch types
+    When I run "git-town branch --display-types=<FLAG>"
+    Then Git Town prints:
+      """
+        main  (main)
+          alpha  (feature)
+      *     beta  (feature)
+              gamma  (feature)
+          parked  (parked)
+          prototype
+        contribution  (contribution)
+        observed
+        perennial  (perennial)
+      """
+
+    Examples:
+      | FLAG                  |
+      | no+prototype+observed |
+      | no-prototype-observed |
+      | no&prototype&observed |
+      | no_prototype_observed |
+      | no-prototype&observed |
diff --git a/features/branch/display_types/config_file.feature b/features/branch/display_types/config_file.feature
new file mode 100644
index 000000000..294f7713d
--- /dev/null
+++ b/features/branch/display_types/config_file.feature
@@ -0,0 +1,112 @@
+Feature: specify which branch types are displayed via the config file
+
+  Background:
+    Given a Git repo with origin
+    And the branches
+      | NAME         | TYPE         | PARENT | LOCATIONS     |
+      | alpha        | feature      | main   | local, origin |
+      | beta         | feature      | alpha  | local, origin |
+      | gamma        | feature      | beta   | local, origin |
+      | observed     | observed     |        | local, origin |
+      | contribution | contribution |        | local, origin |
+      | prototype    | prototype    | main   | local         |
+      | parked       | parked       | main   | local         |
+      | perennial    | perennial    |        | local, origin |
+    And the current branch is "beta"
+
+  Scenario: show all types
+    Given the configuration file:
+      """
+      [branches]
+      display-types = "all"
+      """
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main  (main)
+          alpha  (feature)
+      *     beta  (feature)
+              gamma  (feature)
+          parked  (parked)
+          prototype  (prototype)
+        contribution  (contribution)
+        observed  (observed)
+        perennial  (perennial)
+      """
+
+  Scenario Outline: show only the given branch types
+    Given the configuration file:
+      """
+      [branches]
+      display-types = "<VALUE>"
+      """
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked
+          prototype  (prototype)
+        contribution
+        observed  (observed)
+        perennial
+      """
+
+    Examples:
+      | VALUE              |
+      | prototype observed |
+      | prototype+observed |
+      | prototype-observed |
+      | prototype_observed |
+      | prototype&observed |
+
+  Scenario: show no types
+    Given the configuration file:
+      """
+      [branches]
+      display-types = "no"
+      """
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked
+          prototype
+        contribution
+        observed
+        perennial
+      """
+
+  Scenario Outline: show all except the given branch types
+    Given the configuration file:
+      """
+      [branches]
+      display-types = "<VALUE>"
+      """
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main  (main)
+          alpha  (feature)
+      *     beta  (feature)
+              gamma  (feature)
+          parked  (parked)
+          prototype
+        contribution  (contribution)
+        observed
+        perennial  (perennial)
+      """
+
+    Examples:
+      | VALUE                 |
+      | no prototype observed |
+      | no+prototype+observed |
+      | no-prototype-observed |
+      | no&prototype&observed |
+      | no_prototype_observed |
+      | no prototype-observed |
diff --git a/features/branch/display_types/default.feature b/features/branch/display_types/default.feature
new file mode 100644
index 000000000..f54d573c8
--- /dev/null
+++ b/features/branch/display_types/default.feature
@@ -0,0 +1,28 @@
+Feature: default behavior
+
+  Scenario: default behavior
+    Given a Git repo with origin
+    And the branches
+      | NAME         | TYPE         | PARENT | LOCATIONS     |
+      | alpha        | feature      | main   | local, origin |
+      | beta         | feature      | alpha  | local, origin |
+      | gamma        | feature      | beta   | local, origin |
+      | observed     | observed     |        | local, origin |
+      | contribution | contribution |        | local, origin |
+      | prototype    | prototype    | main   | local         |
+      | parked       | parked       | main   | local         |
+      | perennial    | perennial    |        | local, origin |
+    And the current branch is "beta"
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked  (parked)
+          prototype  (prototype)
+        contribution  (contribution)
+        observed  (observed)
+        perennial  (perennial)
+      """
diff --git a/features/branch/display_types/env.feature b/features/branch/display_types/env.feature
new file mode 100644
index 000000000..1e98d9048
--- /dev/null
+++ b/features/branch/display_types/env.feature
@@ -0,0 +1,101 @@
+Feature: specify which branch types are displayed via environment variables
+
+  Background:
+    Given a Git repo with origin
+    And the branches
+      | NAME         | TYPE         | PARENT | LOCATIONS     |
+      | alpha        | feature      | main   | local, origin |
+      | beta         | feature      | alpha  | local, origin |
+      | gamma        | feature      | beta   | local, origin |
+      | observed     | observed     |        | local, origin |
+      | contribution | contribution |        | local, origin |
+      | prototype    | prototype    | main   | local         |
+      | parked       | parked       | main   | local         |
+      | perennial    | perennial    |        | local, origin |
+    And the current branch is "beta"
+
+  Scenario Outline: show all types
+    When I run "git-town branch" with these environment variables
+      | GIT_TOWN_DISPLAY_TYPES | <VALUE> |
+    Then Git Town prints:
+      """
+        main  (main)
+          alpha  (feature)
+      *     beta  (feature)
+              gamma  (feature)
+          parked  (parked)
+          prototype  (prototype)
+        contribution  (contribution)
+        observed  (observed)
+        perennial  (perennial)
+      """
+
+    Examples:
+      | VALUE |
+      | all   |
+      | ALL   |
+
+  Scenario Outline: show only the given branch types
+    When I run "git-town branch" with these environment variables
+      | GIT_TOWN_DISPLAY_TYPES | <VALUE> |
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked
+          prototype  (prototype)
+        contribution
+        observed  (observed)
+        perennial
+      """
+
+    Examples:
+      | VALUE              |
+      | prototype observed |
+      | prototype+observed |
+      | prototype-observed |
+      | prototype_observed |
+      | prototype&observed |
+
+  Scenario: show no types
+    When I run "git-town branch" with these environment variables
+      | GIT_TOWN_DISPLAY_TYPES | no |
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked
+          prototype
+        contribution
+        observed
+        perennial
+      """
+
+  Scenario Outline: show all except the given branch types
+    When I run "git-town branch" with these environment variables
+      | GIT_TOWN_DISPLAY_TYPES | <VALUE> |
+    Then Git Town prints:
+      """
+        main  (main)
+          alpha  (feature)
+      *     beta  (feature)
+              gamma  (feature)
+          parked  (parked)
+          prototype
+        contribution  (contribution)
+        observed
+        perennial  (perennial)
+      """
+
+    Examples:
+      | VALUE                 |
+      | no prototype observed |
+      | no+prototype+observed |
+      | no-prototype-observed |
+      | no&prototype&observed |
+      | no_prototype_observed |
+      | no prototype-observed |
diff --git a/features/branch/display_types/git.feature b/features/branch/display_types/git.feature
new file mode 100644
index 000000000..425aa5241
--- /dev/null
+++ b/features/branch/display_types/git.feature
@@ -0,0 +1,96 @@
+Feature: specify which branch types are displayed via Git metadata
+
+  Background:
+    Given a Git repo with origin
+    And the branches
+      | NAME         | TYPE         | PARENT | LOCATIONS     |
+      | alpha        | feature      | main   | local, origin |
+      | beta         | feature      | alpha  | local, origin |
+      | gamma        | feature      | beta   | local, origin |
+      | observed     | observed     |        | local, origin |
+      | contribution | contribution |        | local, origin |
+      | prototype    | prototype    | main   | local         |
+      | parked       | parked       | main   | local         |
+      | perennial    | perennial    |        | local, origin |
+    And the current branch is "beta"
+
+  Scenario: show all types
+    Given Git setting "git-town.display-types" is "all"
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main  (main)
+          alpha  (feature)
+      *     beta  (feature)
+              gamma  (feature)
+          parked  (parked)
+          prototype  (prototype)
+        contribution  (contribution)
+        observed  (observed)
+        perennial  (perennial)
+      """
+
+  Scenario Outline: show only the given branch types
+    Given Git setting "git-town.display-types" is "<VALUE>"
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked
+          prototype  (prototype)
+        contribution
+        observed  (observed)
+        perennial
+      """
+
+    Examples:
+      | VALUE              |
+      | prototype observed |
+      | prototype+observed |
+      | prototype-observed |
+      | prototype_observed |
+      | prototype&observed |
+
+  Scenario: show no types
+    Given Git setting "git-town.display-types" is "no"
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main
+          alpha
+      *     beta
+              gamma
+          parked
+          prototype
+        contribution
+        observed
+        perennial
+      """
+
+  Scenario Outline: show all except the given branch types
+    Given Git setting "git-town.display-types" is "<VALUE>"
+    When I run "git-town branch"
+    Then Git Town prints:
+      """
+        main  (main)
+          alpha  (feature)
+      *     beta  (feature)
+              gamma  (feature)
+          parked  (parked)
+          prototype
+        contribution  (contribution)
+        observed
+        perennial  (perennial)
+      """
+
+    Examples:
+      | VALUE                 |
+      | no prototype observed |
+      | no+prototype+observed |
+      | no-prototype-observed |
+      | no&prototype&observed |
+      | no_prototype_observed |
+      | no prototype-observed |
diff --git a/features/config/color.ui/always.feature b/features/config/color.ui/always.feature
index bf6c74c4a..68467e47d 100644
--- a/features/config/color.ui/always.feature
+++ b/features/config/color.ui/always.feature
@@ -22,6 +22,7 @@ Feature: show the configuration
     And Git setting "git-town.perennial-regex" is "^release-"
     And Git setting "git-town.auto-sync" is "false"
     And Git setting "git-town.contribution-regex" is "^renovate/"
+    And Git setting "git-town.display-types" is "all"
     And Git setting "git-town.observed-regex" is "^dependabot/"
     And Git setting "git-town.unknown-branch-type" is "observed"
     And Git setting "git-town.feature-regex" is "^user-.*$"
@@ -46,6 +47,7 @@ Feature: show the configuration
         prototype branches: prototype-1, prototype-2
         unknown branch type: observed
         order: asc
+        display types: all branch types
 
       Configuration:
         offline: no
@@ -99,6 +101,7 @@ Feature: show the configuration
       contribution-regex = "^renovate/"
       observed-regex = "^dependabot/"
       unknown-type = "observed"
+      display-types = "all"
 
       [create]
       share-new-branches = "push"
@@ -138,6 +141,7 @@ Feature: show the configuration
         prototype branches: prototype-1, prototype-2
         unknown branch type: observed
         order: asc
+        display types: all branch types
 
       Configuration:
         offline: no
@@ -185,6 +189,7 @@ Feature: show the configuration
     And Git setting "git-town.perennial-branches" is "git-perennial-1 git-perennial-2"
     And Git setting "git-town.auto-sync" is "false"
     And Git setting "git-town.contribution-regex" is "^git-contribution-regex"
+    And Git setting "git-town.display-types" is "all"
     And Git setting "git-town.observed-regex" is "^git-observed-regex"
     And Git setting "git-town.perennial-regex" is "^git-perennial-"
     And Git setting "git-town.feature-regex" is "git-feature-.*"
@@ -212,6 +217,7 @@ Feature: show the configuration
       observed-regex = "^config-observed-regex"
       unknown-type = "contribution"
       order = "asc"
+      display-types = "no"
 
       [create]
       share-new-branches = "push"
@@ -251,6 +257,7 @@ Feature: show the configuration
         prototype branches: prototype-1, prototype-2
         unknown branch type: observed
         order: desc
+        display types: all branch types
 
       Configuration:
         offline: no
@@ -318,6 +325,7 @@ Feature: show the configuration
         prototype branches: prototype-1, prototype-2
         unknown branch type: feature
         order: asc
+        display types: all branch types except "feature" and "main"
 
       Configuration:
         offline: no
@@ -391,6 +399,7 @@ Feature: show the configuration
         prototype branches: prototype-1, prototype-2
         unknown branch type: feature
         order: asc
+        display types: all branch types except "feature" and "main"
 
       Configuration:
         offline: no
diff --git a/features/config/view/alternative_config_file.feature b/features/config/view/alternative_config_file.feature
index e44320177..2cae91274 100644
--- a/features/config/view/alternative_config_file.feature
+++ b/features/config/view/alternative_config_file.feature
@@ -24,6 +24,7 @@ Feature: show the configuration when using an alternative config file
         prototype branches: (none)
         unknown branch type: feature
         order: asc
+        display types: all branch types except "feature" and "main"
 
       Configuration:
         offline: no
diff --git a/features/config/view/config_file.feature b/features/config/view/config_file.feature
index ce13e0c8b..2d3a5cb9f 100644
--- a/features/config/view/config_file.feature
+++ b/features/config/view/config_file.feature
@@ -27,6 +27,7 @@ Feature: display information from config file
       observed-regex = "^dependabot/"
       unknown-type = "observed"
       order = "desc"
+      display-types = "all"
 
       [create]
       share-new-branches = "push"
@@ -68,6 +69,7 @@ Feature: display information from config file
         prototype branches: prototype-1, prototype-2
         unknown branch type: observed
         order: desc
+        display types: all branch types
 
       Configuration:
         offline: no
diff --git a/features/config/view/detached_head.feature b/features/config/view/detached_head.feature
index 39bea60b9..dc1d32cc1 100644
--- a/features/config/view/detached_head.feature
+++ b/features/config/view/detached_head.feature
@@ -14,6 +14,7 @@ Feature: display configuration from Git metadata in detached head state
       |        | local    | commit 2 |
     And Git setting "git-town.perennial-branches" is "qa staging"
     And Git setting "git-town.auto-sync" is "false"
+    And Git setting "git-town.display-types" is "all"
     And Git setting "git-town.perennial-regex" is "^release-"
     And Git setting "git-town.contribution-regex" is "^renovate/"
     And Git setting "git-town.observed-regex" is "^dependabot/"
@@ -43,6 +44,7 @@ Feature: display configuration from Git metadata in detached head state
         prototype branches: (none)
         unknown branch type: observed
         order: asc
+        display types: all branch types
 
       Configuration:
         offline: no
diff --git a/features/config/view/environment_variables.feature b/features/config/view/environment_variables.feature
index 9b9511c88..b7c9ba0f3 100644
--- a/features/config/view/environment_variables.feature
+++ b/features/config/view/environment_variables.feature
@@ -20,6 +20,7 @@ Feature: display configuration defined in environment variables
       | GIT_TOWN_CONTRIBUTION_REGEX          | ^renovate/         |
       | GIT_TOWN_DETACHED                    | true               |
       | GIT_TOWN_DEV_REMOTE                  | my-fork            |
+      | GIT_TOWN_DISPLAY_TYPES               | all                |
       | GIT_TOWN_FEATURE_REGEX               | ^user-.*$          |
       | GIT_TOWN_FORGE_TYPE                  | gitlab             |
       | GIT_TOWN_GITEA_TOKEN                 | gitea-token        |
@@ -62,6 +63,7 @@ Feature: display configuration defined in environment variables
         prototype branches: (none)
         unknown branch type: observed
         order: desc
+        display types: all branch types
 
       Configuration:
         offline: yes
diff --git a/features/config/view/git_metadata.feature b/features/config/view/git_metadata.feature
index 921e4344b..850bb275b 100644
--- a/features/config/view/git_metadata.feature
+++ b/features/config/view/git_metadata.feature
@@ -20,6 +20,7 @@ Feature: display configuration from Git metadata
     And Git setting "git-town.perennial-regex" is "^release-"
     And Git setting "git-town.auto-sync" is "false"
     And Git setting "git-town.contribution-regex" is "^renovate/"
+    And Git setting "git-town.display-types" is "all"
     And Git setting "git-town.observed-regex" is "^dependabot/"
     And Git setting "git-town.feature-regex" is "^user-.*$"
     And Git setting "git-town.ship-strategy" is "squash-merge"
@@ -43,6 +44,7 @@ Feature: display configuration from Git metadata
         prototype branches: prototype-1, prototype-2
         unknown branch type: observed
         order: asc
+        display types: all branch types
 
       Configuration:
         offline: no
@@ -109,6 +111,7 @@ Feature: display configuration from Git metadata
         prototype branches: prototype-1, prototype-2
         unknown branch type: feature
         order: asc
+        display types: all branch types except "feature" and "main"
 
       Configuration:
         offline: no
diff --git a/features/config/view/in_subfolder.feature b/features/config/view/in_subfolder.feature
index cd302ecda..08568990d 100644
--- a/features/config/view/in_subfolder.feature
+++ b/features/config/view/in_subfolder.feature
@@ -25,6 +25,7 @@ Feature: show the configuration from a subfolder
         prototype branches: (none)
         unknown branch type: feature
         order: asc
+        display types: all branch types except "feature" and "main"
 
       Configuration:
         offline: no
diff --git a/features/config/view/lang/es.feature b/features/config/view/lang/es.feature
index 7b6e0ef0d..ee629d416 100644
--- a/features/config/view/lang/es.feature
+++ b/features/config/view/lang/es.feature
@@ -40,6 +40,7 @@ Feature: show the configuration in Spanish
         prototype branches: prototype-1, prototype-2
         unknown branch type: observed
         order: asc
+        display types: all branch types except "feature" and "main"
 
       Configuration:
         offline: no
diff --git a/features/config/view/multiple_config_sources.feature b/features/config/view/multiple_config_sources.feature
index f387f2de9..64767f6e1 100644
--- a/features/config/view/multiple_config_sources.feature
+++ b/features/config/view/multiple_config_sources.feature
@@ -22,6 +22,7 @@ Feature: show the configuration
     And Git setting "git-town.auto-sync" is "false"
     And Git setting "git-town.contribution-regex" is "^git-contribution-regex"
     And Git setting "git-town.detached" is "true"
+    And Git setting "git-town.display-types" is "no"
     And Git setting "git-town.observed-regex" is "^git-observed-regex"
     And Git setting "git-town.perennial-regex" is "^git-perennial-"
     And Git setting "git-town.feature-regex" is "git-feature-.*"
@@ -48,6 +49,7 @@ Feature: show the configuration
       observed-regex = "^config-observed-regex"
       unknown-type = "contribution"
       order = "asc"
+      display-types = "all"
 
       [create]
       share-new-branches = "push"
@@ -87,6 +89,7 @@ Feature: show the configuration
         prototype branches: prototype-1, prototype-2
         unknown branch type: observed
         order: desc
+        display types: no branch types
 
       Configuration:
         offline: no
diff --git a/features/config/view/no_config.feature b/features/config/view/no_config.feature
index fadae21ab..33bf09037 100644
--- a/features/config/view/no_config.feature
+++ b/features/config/view/no_config.feature
@@ -27,6 +27,7 @@ Feature: no configuration data
         prototype branches: (none)
         unknown branch type: feature
         order: asc
+        display types: all branch types except "feature" and "main"
 
       Configuration:
         offline: no
diff --git a/internal/cli/dialog/lineage.go b/internal/cli/dialog/lineage.go
index b971d5a0d..60955027f 100644
--- a/internal/cli/dialog/lineage.go
+++ b/internal/cli/dialog/lineage.go
@@ -82,7 +82,7 @@ func Lineage(args LineageArgs) (additionalLineage configdomain.Lineage, addition
 		newParent, exit, err := SwitchBranch(SwitchBranchArgs{
 			CurrentBranch:      None[gitdomain.LocalBranchName](),
 			Cursor:             1, // select the "main branch" entry, below the "make perennial" entry
-			DisplayBranchTypes: false,
+			DisplayBranchTypes: args.Config.NormalConfig.DisplayTypes,
 			EntryData: EntryData{
 				EntriesAll:      entriesAll,
 				EntriesLocal:    entriesLocal,
diff --git a/internal/cli/dialog/switch_branch.go b/internal/cli/dialog/switch_branch.go
index a71a1cfe3..c4593ec53 100644
--- a/internal/cli/dialog/switch_branch.go
+++ b/internal/cli/dialog/switch_branch.go
@@ -154,7 +154,7 @@ func (self SwitchModel) View() string {
 			}
 			s.WriteString(color.Styled("  " + entry.Text))
 		}
-		if self.DisplayBranchTypes.ShouldDisplayTypes() && ShouldDisplayBranchType(entry.Data.Type) {
+		if self.DisplayBranchTypes.ShouldDisplayType(entry.Data.Type) {
 			s.WriteString("  ")
 			s.WriteString(colors.Faint().Styled("(" + entry.Data.Type.String() + ")"))
 		}
@@ -276,23 +276,6 @@ type NewSwitchBranchEntriesArgs struct {
 	UnknownBranchType configdomain.UnknownBranchType
 }
 
-func ShouldDisplayBranchType(branchType configdomain.BranchType) bool {
-	switch branchType {
-	case
-		configdomain.BranchTypeMainBranch,
-		configdomain.BranchTypeFeatureBranch:
-		return false
-	case
-		configdomain.BranchTypeContributionBranch,
-		configdomain.BranchTypeObservedBranch,
-		configdomain.BranchTypeParkedBranch,
-		configdomain.BranchTypePerennialBranch,
-		configdomain.BranchTypePrototypeBranch:
-		return true
-	}
-	panic("unhandled branch type:" + branchType.String())
-}
-
 func SwitchBranch(args SwitchBranchArgs) (gitdomain.LocalBranchName, dialogdomain.Exit, error) {
 	entries := args.EntryData.entries()
 	initialBranchPos := None[int]()
diff --git a/internal/cli/flags/display_types.go b/internal/cli/flags/display_types.go
index 420f95281..ea72502a2 100644
--- a/internal/cli/flags/display_types.go
+++ b/internal/cli/flags/display_types.go
@@ -2,6 +2,7 @@ package flags
 
 import (
 	"github.com/git-town/git-town/v22/internal/config/configdomain"
+	. "github.com/git-town/git-town/v22/pkg/prelude"
 	"github.com/spf13/cobra"
 )
 
@@ -10,13 +11,22 @@ const displayTypesLong = "display-types"
 // type-safe access to the CLI arguments of type configdomain.Displaytypes
 func Displaytypes() (AddFunc, ReadDisplayTypesFlagFunc) {
 	addFlag := func(cmd *cobra.Command) {
-		cmd.Flags().BoolP(displayTypesLong, "d", false, "display the branch types")
+		cmd.Flags().StringP(displayTypesLong, "d", "", "display the branch types")
+		cmd.Flags().Lookup(displayTypesLong).NoOptDefVal = "all"
 	}
-	readFlag := func(cmd *cobra.Command) (configdomain.DisplayTypes, error) {
-		return readBoolFlag[configdomain.DisplayTypes](cmd.Flags(), displayTypesLong)
+	readFlag := func(cmd *cobra.Command) (Option[configdomain.DisplayTypes], error) {
+		// Check if the flag was actually provided by the user
+		if !cmd.Flags().Changed(displayTypesLong) {
+			return None[configdomain.DisplayTypes](), nil
+		}
+		text, err := cmd.Flags().GetString(displayTypesLong)
+		if err != nil {
+			return None[configdomain.DisplayTypes](), err
+		}
+		return configdomain.ParseDisplayTypes(text, "CLI flag "+displayTypesLong)
 	}
 	return addFlag, readFlag
 }
 
 // ReadDisplayTypesFlagFunc is the type signature for the function that reads the "display-types" flag from the args to the given Cobra command.
-type ReadDisplayTypesFlagFunc func(*cobra.Command) (configdomain.DisplayTypes, error)
+type ReadDisplayTypesFlagFunc func(*cobra.Command) (Option[configdomain.DisplayTypes], error)
diff --git a/internal/cmd/append.go b/internal/cmd/append.go
index 508b61b1e..40abfbf51 100644
--- a/internal/cmd/append.go
+++ b/internal/cmd/append.go
@@ -104,6 +104,7 @@ func appendCmd() *cobra.Command {
 				AutoResolve:  autoResolve,
 				AutoSync:     sync,
 				Detached:     detached,
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: push,
diff --git a/internal/cmd/branch.go b/internal/cmd/branch.go
index 14082248c..8e40dc29e 100644
--- a/internal/cmd/branch.go
+++ b/internal/cmd/branch.go
@@ -29,6 +29,7 @@ Git Town's equivalent of the "git branch" command.`
 )
 
 func branchCmd() *cobra.Command {
+	addDisplayTypesFlag, readDisplayTypesFlag := flags.Displaytypes()
 	addOrderFlag, readOrderFlag := flags.Order()
 	addVerboseFlag, readVerboseFlag := flags.Verbose()
 	cmd := cobra.Command{
@@ -37,15 +38,17 @@ func branchCmd() *cobra.Command {
 		Short: branchDesc,
 		Long:  cmdhelpers.Long(branchDesc, branchHelp),
 		RunE: func(cmd *cobra.Command, _ []string) error {
+			displayTypes, errDisplayTypes := readDisplayTypesFlag(cmd)
 			order, errOrder := readOrderFlag(cmd)
 			verbose, errVerbose := readVerboseFlag(cmd)
-			if err := cmp.Or(errOrder, errVerbose); err != nil {
+			if err := cmp.Or(errDisplayTypes, errOrder, errVerbose); err != nil {
 				return err
 			}
 			cliConfig := cliconfig.New(cliconfig.NewArgs{
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: displayTypes,
 				DryRun:       None[configdomain.DryRun](),
 				Order:        order,
 				PushBranches: None[configdomain.PushBranches](),
@@ -55,6 +58,7 @@ func branchCmd() *cobra.Command {
 			return executeBranch(cliConfig)
 		},
 	}
+	addDisplayTypesFlag(&cmd)
 	addOrderFlag(&cmd)
 	addVerboseFlag(&cmd)
 	return &cmd
@@ -95,7 +99,7 @@ Start:
 		ShowAllBranches:   false,
 		UnknownBranchType: repo.UnvalidatedConfig.NormalConfig.UnknownBranchType,
 	})
-	fmt.Print(branchLayout(entries, data))
+	fmt.Print(branchLayout(entries, data, repo.UnvalidatedConfig.NormalConfig.DisplayTypes))
 	return nil
 }
 
@@ -159,7 +163,7 @@ type branchData struct {
 	initialBranchOpt Option[gitdomain.LocalBranchName]
 }
 
-func branchLayout(entries dialog.SwitchBranchEntries, data branchData) string {
+func branchLayout(entries dialog.SwitchBranchEntries, data branchData, displayTypes configdomain.DisplayTypes) string {
 	s := strings.Builder{}
 	initialBranch, hasInitialBranch := data.initialBranchOpt.Get()
 	for _, entry := range entries {
@@ -174,7 +178,7 @@ func branchLayout(entries dialog.SwitchBranchEntries, data branchData) string {
 			s.WriteString("  ")
 			s.WriteString(entry.String())
 		}
-		if dialog.ShouldDisplayBranchType(entry.Type) {
+		if displayTypes.ShouldDisplayType(entry.Type) {
 			s.WriteString("  ")
 			s.WriteString(colors.Faint().Styled("(" + entry.Type.String() + ")"))
 		}
diff --git a/internal/cmd/compress.go b/internal/cmd/compress.go
index a05e67f9f..958b9a0f9 100644
--- a/internal/cmd/compress.go
+++ b/internal/cmd/compress.go
@@ -84,6 +84,7 @@ func compressCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/config/get_parent.go b/internal/cmd/config/get_parent.go
index b91365f41..f5836bdff 100644
--- a/internal/cmd/config/get_parent.go
+++ b/internal/cmd/config/get_parent.go
@@ -32,6 +32,7 @@ func getParentCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/config/remove.go b/internal/cmd/config/remove.go
index fa0f8c01e..95621fb16 100644
--- a/internal/cmd/config/remove.go
+++ b/internal/cmd/config/remove.go
@@ -34,6 +34,7 @@ func removeConfigCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/config/root.go b/internal/cmd/config/root.go
index d575681d0..005b93a6e 100644
--- a/internal/cmd/config/root.go
+++ b/internal/cmd/config/root.go
@@ -2,6 +2,7 @@
 package config
 
 import (
+	"cmp"
 	"fmt"
 
 	"github.com/git-town/git-town/v22/internal/cli/flags"
@@ -19,6 +20,7 @@ import (
 const configDesc = "Display your Git Town configuration"
 
 func RootCmd() *cobra.Command {
+	addDisplayTypesFlag, readDisplayTypesFlag := flags.Displaytypes()
 	addVerboseFlag, readVerboseFlag := flags.Verbose()
 	configCmd := cobra.Command{
 		Use:     "config",
@@ -27,14 +29,16 @@ func RootCmd() *cobra.Command {
 		Short:   configDesc,
 		Long:    cmdhelpers.Long(configDesc),
 		RunE: func(cmd *cobra.Command, _ []string) error {
-			verbose, err := readVerboseFlag(cmd)
-			if err != nil {
+			displayTypes, errDisplayTypes := readDisplayTypesFlag(cmd)
+			verbose, errVerbose := readVerboseFlag(cmd)
+			if err := cmp.Or(errDisplayTypes, errVerbose); err != nil {
 				return err
 			}
 			cliConfig := cliconfig.New(cliconfig.NewArgs{
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: displayTypes,
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
@@ -44,6 +48,7 @@ func RootCmd() *cobra.Command {
 			return executeDisplayConfig(cliConfig)
 		},
 	}
+	addDisplayTypesFlag(&configCmd)
 	addVerboseFlag(&configCmd)
 	configCmd.AddCommand(getParentCommand())
 	configCmd.AddCommand(removeConfigCommand())
@@ -80,6 +85,7 @@ func printConfig(config config.UnvalidatedConfig) {
 	print.Entry("prototype branches", format.BranchNames(config.NormalConfig.PartialBranchesOfType(configdomain.BranchTypePrototypeBranch)))
 	print.Entry("unknown branch type", config.NormalConfig.UnknownBranchType.String())
 	print.Entry("order", config.NormalConfig.Order.String())
+	print.Entry("display types", config.NormalConfig.DisplayTypes.String())
 	fmt.Println()
 	print.Header("Configuration")
 	print.Entry("offline", format.Bool(config.NormalConfig.Offline.IsOffline()))
diff --git a/internal/cmd/continue.go b/internal/cmd/continue.go
index eaa2429e7..e2a137bde 100644
--- a/internal/cmd/continue.go
+++ b/internal/cmd/continue.go
@@ -45,6 +45,7 @@ func continueCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/contribute.go b/internal/cmd/contribute.go
index 7aac339fd..cb59b16d8 100644
--- a/internal/cmd/contribute.go
+++ b/internal/cmd/contribute.go
@@ -54,6 +54,7 @@ func contributeCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/delete.go b/internal/cmd/delete.go
index 9ad597bb9..b9396ae98 100644
--- a/internal/cmd/delete.go
+++ b/internal/cmd/delete.go
@@ -81,6 +81,7 @@ func deleteCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/detach.go b/internal/cmd/detach.go
index b92240fbf..53e99e220 100644
--- a/internal/cmd/detach.go
+++ b/internal/cmd/detach.go
@@ -85,6 +85,7 @@ func detachCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/diff_parent.go b/internal/cmd/diff_parent.go
index 390a9c47b..a26742847 100644
--- a/internal/cmd/diff_parent.go
+++ b/internal/cmd/diff_parent.go
@@ -46,6 +46,7 @@ func diffParentCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/down.go b/internal/cmd/down.go
index 9b34eb13b..97cab9a6a 100644
--- a/internal/cmd/down.go
+++ b/internal/cmd/down.go
@@ -24,6 +24,7 @@ const (
 )
 
 func downCmd() *cobra.Command {
+	addDisplayTypesFlag, readDisplayTypesFlag := flags.Displaytypes()
 	addMergeFlag, readMergeFlag := flags.Merge()
 	addVerboseFlag, readVerboseFlag := flags.Verbose()
 	cmd := cobra.Command{
@@ -33,15 +34,17 @@ func downCmd() *cobra.Command {
 		Short:   downShort,
 		Long:    cmdhelpers.Long(downShort, downLong),
 		RunE: func(cmd *cobra.Command, _ []string) error {
+			displayTypes, errDisplayTypes := readDisplayTypesFlag(cmd)
 			merge, errMerge := readMergeFlag(cmd)
 			verbose, errVerbose := readVerboseFlag(cmd)
-			if err := cmp.Or(errMerge, errVerbose); err != nil {
+			if err := cmp.Or(errDisplayTypes, errMerge, errVerbose); err != nil {
 				return err
 			}
 			cliConfig := cliconfig.New(cliconfig.NewArgs{
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: displayTypes,
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
@@ -54,6 +57,7 @@ func downCmd() *cobra.Command {
 			})
 		},
 	}
+	addDisplayTypesFlag(&cmd)
 	addMergeFlag(&cmd)
 	addVerboseFlag(&cmd)
 	return &cmd
@@ -124,7 +128,7 @@ Start:
 		UnknownBranchType: repo.UnvalidatedConfig.NormalConfig.UnknownBranchType,
 	})
 	fmt.Println()
-	fmt.Print(branchLayout(entries, data))
+	fmt.Print(branchLayout(entries, data, repo.UnvalidatedConfig.NormalConfig.DisplayTypes))
 
 	return nil
 }
diff --git a/internal/cmd/feature.go b/internal/cmd/feature.go
index fd24246dd..04199a166 100644
--- a/internal/cmd/feature.go
+++ b/internal/cmd/feature.go
@@ -46,6 +46,7 @@ func featureCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/hack.go b/internal/cmd/hack.go
index cd8e3c6b6..db0b9fe24 100644
--- a/internal/cmd/hack.go
+++ b/internal/cmd/hack.go
@@ -97,6 +97,7 @@ func hackCmd() *cobra.Command {
 				AutoResolve:  autoResolve,
 				AutoSync:     sync,
 				Detached:     detached,
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
@@ -347,7 +348,7 @@ func determineHackData(args hackArgs, repo execute.OpenRepoResult) (data appendF
 		newParent, exit, err := dialog.SwitchBranch(dialog.SwitchBranchArgs{
 			CurrentBranch:      None[gitdomain.LocalBranchName](),
 			Cursor:             1, // select the "main branch" entry, below the "make perennial" entry
-			DisplayBranchTypes: false,
+			DisplayBranchTypes: validatedConfig.NormalConfig.DisplayTypes,
 			EntryData: dialog.EntryData{
 				EntriesAll:      entriesAll,
 				EntriesLocal:    entriesLocal,
diff --git a/internal/cmd/init.go b/internal/cmd/init.go
index c0e6b3d10..616ea3b68 100644
--- a/internal/cmd/init.go
+++ b/internal/cmd/init.go
@@ -40,6 +40,7 @@ func initCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/merge.go b/internal/cmd/merge.go
index 0db41fe20..cc9bb2ba2 100644
--- a/internal/cmd/merge.go
+++ b/internal/cmd/merge.go
@@ -81,6 +81,7 @@ func mergeCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/observe.go b/internal/cmd/observe.go
index cbf7fe849..68ad27ba4 100644
--- a/internal/cmd/observe.go
+++ b/internal/cmd/observe.go
@@ -53,6 +53,7 @@ func observeCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/offline.go b/internal/cmd/offline.go
index b4ecb7edf..debba2356 100644
--- a/internal/cmd/offline.go
+++ b/internal/cmd/offline.go
@@ -43,6 +43,7 @@ func offlineCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/park.go b/internal/cmd/park.go
index 385e654be..33d799641 100644
--- a/internal/cmd/park.go
+++ b/internal/cmd/park.go
@@ -47,6 +47,7 @@ func parkCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/prepend.go b/internal/cmd/prepend.go
index 8710251e0..2b19aaec3 100644
--- a/internal/cmd/prepend.go
+++ b/internal/cmd/prepend.go
@@ -113,6 +113,7 @@ func prependCommand() *cobra.Command {
 				AutoResolve:  autoResolve,
 				AutoSync:     sync,
 				Detached:     detached,
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: push,
diff --git a/internal/cmd/propose.go b/internal/cmd/propose.go
index 357f34ac8..5a41e9eaa 100644
--- a/internal/cmd/propose.go
+++ b/internal/cmd/propose.go
@@ -82,6 +82,7 @@ func proposeCommand() *cobra.Command {
 				AutoResolve:  autoResolve,
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/prototype.go b/internal/cmd/prototype.go
index fbeb75bf7..964829dd3 100644
--- a/internal/cmd/prototype.go
+++ b/internal/cmd/prototype.go
@@ -50,6 +50,7 @@ func prototypeCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/rename.go b/internal/cmd/rename.go
index 6cfcc0766..c8583fad2 100644
--- a/internal/cmd/rename.go
+++ b/internal/cmd/rename.go
@@ -59,6 +59,7 @@ func renameCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/repo.go b/internal/cmd/repo.go
index 0bf300550..b96fe601d 100644
--- a/internal/cmd/repo.go
+++ b/internal/cmd/repo.go
@@ -47,6 +47,7 @@ func repoCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/runlog.go b/internal/cmd/runlog.go
index 44634da07..5ca2f889f 100644
--- a/internal/cmd/runlog.go
+++ b/internal/cmd/runlog.go
@@ -47,6 +47,7 @@ func runLogCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/set_parent.go b/internal/cmd/set_parent.go
index a97322f3c..ef35506dd 100644
--- a/internal/cmd/set_parent.go
+++ b/internal/cmd/set_parent.go
@@ -82,6 +82,7 @@ func setParentCommand() *cobra.Command {
 				AutoResolve:  autoResolve,
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
@@ -159,7 +160,7 @@ Start:
 			selectedParent, exit, err = dialog.SwitchBranch(dialog.SwitchBranchArgs{
 				CurrentBranch:      None[gitdomain.LocalBranchName](),
 				Cursor:             entriesLocal.IndexOf(data.defaultChoice),
-				DisplayBranchTypes: true,
+				DisplayBranchTypes: data.config.NormalConfig.DisplayTypes,
 				EntryData: dialog.EntryData{
 					EntriesAll:      entriesAll,
 					EntriesLocal:    entriesLocal,
diff --git a/internal/cmd/ship/cmd.go b/internal/cmd/ship/cmd.go
index ff2fa4eef..06817b12a 100644
--- a/internal/cmd/ship/cmd.go
+++ b/internal/cmd/ship/cmd.go
@@ -73,6 +73,7 @@ func Cmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/skip.go b/internal/cmd/skip.go
index 1595d0f01..7aa084932 100644
--- a/internal/cmd/skip.go
+++ b/internal/cmd/skip.go
@@ -40,6 +40,7 @@ func skipCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/status/reset.go b/internal/cmd/status/reset.go
index 9065420c6..7cd6dcd55 100644
--- a/internal/cmd/status/reset.go
+++ b/internal/cmd/status/reset.go
@@ -37,6 +37,7 @@ func resetRunstateCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/status/root.go b/internal/cmd/status/root.go
index aa11d4e8a..e7f063be8 100644
--- a/internal/cmd/status/root.go
+++ b/internal/cmd/status/root.go
@@ -40,6 +40,7 @@ func RootCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/status/show.go b/internal/cmd/status/show.go
index 259c40cec..cab22e831 100644
--- a/internal/cmd/status/show.go
+++ b/internal/cmd/status/show.go
@@ -31,6 +31,7 @@ func showRunstateCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/swap/cmd.go b/internal/cmd/swap/cmd.go
index 158e0b57e..0d5e867e0 100644
--- a/internal/cmd/swap/cmd.go
+++ b/internal/cmd/swap/cmd.go
@@ -81,6 +81,7 @@ func Cmd() *cobra.Command {
 				AutoResolve:  autoResolve,
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/switch.go b/internal/cmd/switch.go
index 8c5e0f3a1..5f8a89002 100644
--- a/internal/cmd/switch.go
+++ b/internal/cmd/switch.go
@@ -52,6 +52,7 @@ func switchCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: displayTypes,
 				DryRun:       None[configdomain.DryRun](),
 				Order:        order,
 				PushBranches: None[configdomain.PushBranches](),
@@ -59,12 +60,11 @@ func switchCmd() *cobra.Command {
 				Verbose:      verbose,
 			})
 			return executeSwitch(executeSwitchArgs{
-				allBranches:  allBranches,
-				argv:         args,
-				branchTypes:  branchTypes,
-				cliConfig:    cliConfig,
-				displayTypes: displayTypes,
-				merge:        merge,
+				allBranches: allBranches,
+				argv:        args,
+				branchTypes: branchTypes,
+				cliConfig:   cliConfig,
+				merge:       merge,
 			})
 		},
 	}
@@ -78,12 +78,11 @@ func switchCmd() *cobra.Command {
 }
 
 type executeSwitchArgs struct {
-	allBranches  configdomain.AllBranches
-	argv         []string
-	branchTypes  []configdomain.BranchType
-	cliConfig    configdomain.PartialConfig
-	displayTypes configdomain.DisplayTypes
-	merge        configdomain.SwitchUsingMerge
+	allBranches configdomain.AllBranches
+	argv        []string
+	branchTypes []configdomain.BranchType
+	cliConfig   configdomain.PartialConfig
+	merge       configdomain.SwitchUsingMerge
 }
 
 func executeSwitch(args executeSwitchArgs) error {
@@ -136,7 +135,7 @@ Start:
 	branchToCheckout, exit, err := dialog.SwitchBranch(dialog.SwitchBranchArgs{
 		CurrentBranch:      Some(data.initialBranch),
 		Cursor:             cursor,
-		DisplayBranchTypes: args.displayTypes,
+		DisplayBranchTypes: repo.UnvalidatedConfig.NormalConfig.DisplayTypes,
 		EntryData: dialog.EntryData{
 			EntriesAll:      entriesAll,
 			EntriesLocal:    entriesLocal,
diff --git a/internal/cmd/sync/cmd.go b/internal/cmd/sync/cmd.go
index dd8c3ceef..068b97391 100644
--- a/internal/cmd/sync/cmd.go
+++ b/internal/cmd/sync/cmd.go
@@ -81,6 +81,7 @@ func Cmd() *cobra.Command {
 				AutoResolve:  autoResolve,
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     detached,
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: pushBranches,
diff --git a/internal/cmd/undo.go b/internal/cmd/undo.go
index 90f911868..42e631703 100644
--- a/internal/cmd/undo.go
+++ b/internal/cmd/undo.go
@@ -42,6 +42,7 @@ func undoCmd() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/cmd/up.go b/internal/cmd/up.go
index 916bbab79..9c4779482 100644
--- a/internal/cmd/up.go
+++ b/internal/cmd/up.go
@@ -26,6 +26,7 @@ const (
 )
 
 func upCmd() *cobra.Command {
+	addDisplayTypesFlag, readDisplayTypesFlag := flags.Displaytypes()
 	addMergeFlag, readMergeFlag := flags.Merge()
 	addVerboseFlag, readVerboseFlag := flags.Verbose()
 	cmd := cobra.Command{
@@ -35,15 +36,17 @@ func upCmd() *cobra.Command {
 		Short:   upShort,
 		Long:    cmdhelpers.Long(upShort, upLong),
 		RunE: func(cmd *cobra.Command, _ []string) error {
+			displayTypes, errDisplayTypes := readDisplayTypesFlag(cmd)
 			merge, errMerge := readMergeFlag(cmd)
 			verbose, errVerbose := readVerboseFlag(cmd)
-			if err := cmp.Or(errMerge, errVerbose); err != nil {
+			if err := cmp.Or(errDisplayTypes, errMerge, errVerbose); err != nil {
 				return err
 			}
 			cliConfig := cliconfig.New(cliconfig.NewArgs{
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     None[configdomain.Detached](),
+				DisplayTypes: displayTypes,
 				DryRun:       None[configdomain.DryRun](),
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
@@ -56,6 +59,7 @@ func upCmd() *cobra.Command {
 			})
 		},
 	}
+	addDisplayTypesFlag(&cmd)
 	addMergeFlag(&cmd)
 	addVerboseFlag(&cmd)
 	return &cmd
@@ -141,7 +145,7 @@ Start:
 		UnknownBranchType: repo.UnvalidatedConfig.NormalConfig.UnknownBranchType,
 	})
 	fmt.Println()
-	fmt.Print(branchLayout(entries, data))
+	fmt.Print(branchLayout(entries, data, repo.UnvalidatedConfig.NormalConfig.DisplayTypes))
 
 	return nil
 }
diff --git a/internal/cmd/walk.go b/internal/cmd/walk.go
index 1e7cf1e37..81915195d 100644
--- a/internal/cmd/walk.go
+++ b/internal/cmd/walk.go
@@ -90,6 +90,7 @@ func walkCommand() *cobra.Command {
 				AutoResolve:  None[configdomain.AutoResolve](),
 				AutoSync:     None[configdomain.AutoSync](),
 				Detached:     Some(configdomain.Detached(true)),
+				DisplayTypes: None[configdomain.DisplayTypes](),
 				DryRun:       dryRun,
 				Order:        None[configdomain.Order](),
 				PushBranches: None[configdomain.PushBranches](),
diff --git a/internal/config/cliconfig/cli_config.go b/internal/config/cliconfig/cli_config.go
index e678120d2..72fb3c3e7 100644
--- a/internal/config/cliconfig/cli_config.go
+++ b/internal/config/cliconfig/cli_config.go
@@ -11,6 +11,7 @@ type NewArgs struct {
 	AutoResolve  Option[configdomain.AutoResolve]
 	AutoSync     Option[configdomain.AutoSync]
 	Detached     Option[configdomain.Detached]
+	DisplayTypes Option[configdomain.DisplayTypes]
 	DryRun       Option[configdomain.DryRun]
 	Order        Option[configdomain.Order]
 	PushBranches Option[configdomain.PushBranches]
@@ -30,6 +31,7 @@ func New(args NewArgs) configdomain.PartialConfig {
 		ContributionRegex:        None[configdomain.ContributionRegex](),
 		Detached:                 args.Detached,
 		DevRemote:                None[gitdomain.Remote](),
+		DisplayTypes:             args.DisplayTypes,
 		DryRun:                   args.DryRun,
 		FeatureRegex:             None[configdomain.FeatureRegex](),
 		ForgeType:                None[forgedomain.ForgeType](),
diff --git a/internal/config/configdomain/display_types.go b/internal/config/configdomain/display_types.go
index 88c48aa2f..a029c10cf 100644
--- a/internal/config/configdomain/display_types.go
+++ b/internal/config/configdomain/display_types.go
@@ -1,8 +1,110 @@
 package configdomain
 
+import (
+	"fmt"
+	"regexp"
+	"slices"
+	"strings"
+
+	"github.com/git-town/git-town/v22/internal/gohacks/slice"
+	. "github.com/git-town/git-town/v22/pkg/prelude"
+)
+
 // whether to display branch types in the CLI output
-type DisplayTypes bool
+type DisplayTypes struct {
+	BranchTypes []BranchType // the branch types for which the user has specified exceptions
+	Quantifier  Quantifier   // whether to include or exclude the listed branches
+}
+
+// Quantifier specifies whether to display or not display branches
+type Quantifier string
+
+const (
+	QuantifierAll  = "all" // display all branches
+	QuantifierNo   = "no"  // display no or all except the specified branches
+	QuantifierOnly = ""    // display only the specified branches
+)
+
+// provides the serialized version to be used in configuration data
+func (self DisplayTypes) Serialize(delimiter string) string {
+	switch self.Quantifier {
+	case QuantifierAll:
+		return "all"
+	case QuantifierNo:
+		if len(self.BranchTypes) == 0 {
+			return "no"
+		}
+		return "no " + strings.Join(slice.Stringify(self.BranchTypes), delimiter)
+	case QuantifierOnly:
+		return strings.Join(slice.Stringify(self.BranchTypes), delimiter)
+	}
+	panic("unhandled DisplayType quantifier: " + self.Quantifier)
+}
+
+// indicates whether Git Town should display the given branch type
+func (self DisplayTypes) ShouldDisplayType(branchType BranchType) bool {
+	switch self.Quantifier {
+	case QuantifierAll:
+		return true
+	case QuantifierNo:
+		if len(self.BranchTypes) == 0 {
+			return false
+		}
+		return !slices.Contains(self.BranchTypes, branchType)
+	case QuantifierOnly:
+		return slices.Contains(self.BranchTypes, branchType)
+	}
+	panic("unhandled DisplayType state: " + self.String())
+}
+
+// provides a human-readable version of this DisplayTypes
+func (self DisplayTypes) String() string {
+	switch self.Quantifier {
+	case QuantifierAll:
+		return "all branch types"
+	case QuantifierNo:
+		if len(self.BranchTypes) == 0 {
+			return "no branch types"
+		}
+		return "all branch types except " + slice.JoinSentenceQuotes(self.BranchTypes)
+	case QuantifierOnly:
+		return "only the branch types " + slice.JoinSentenceQuotes(self.BranchTypes)
+	}
+	panic("unhandled DisplayType quantifier: " + self.Quantifier)
+}
 
-func (self DisplayTypes) ShouldDisplayTypes() bool {
-	return bool(self)
+func ParseDisplayTypes(text, source string) (Option[DisplayTypes], error) {
+	if len(text) == 0 {
+		return None[DisplayTypes](), nil
+	}
+	re := regexp.MustCompile(`[ +\-&_]`)
+	parts := re.Split(strings.ToLower(text), -1)
+	var quantifier Quantifier
+	switch parts[0] {
+	case QuantifierAll:
+		quantifier = QuantifierAll
+		parts = parts[1:]
+		if len(parts) > 0 {
+			return None[DisplayTypes](), fmt.Errorf(`the "all" quantifier for DisplayTypes does not accept branch types, in %q you gave: %s`, source, parts)
+		}
+	case QuantifierNo:
+		quantifier = QuantifierNo
+		parts = parts[1:]
+	default:
+		quantifier = QuantifierOnly
+	}
+	branchTypes := make([]BranchType, len(parts))
+	for p, part := range parts {
+		branchTypeOpt, err := ParseBranchType(part)
+		if err != nil {
+			return None[DisplayTypes](), err
+		}
+		if branchType, hasBranchType := branchTypeOpt.Get(); hasBranchType {
+			branchTypes[p] = branchType
+		}
+	}
+	return Some(DisplayTypes{
+		BranchTypes: branchTypes,
+		Quantifier:  quantifier,
+	}), nil
 }
diff --git a/internal/config/configdomain/key.go b/internal/config/configdomain/key.go
index cd233e1a7..9d0d5b6b8 100644
--- a/internal/config/configdomain/key.go
+++ b/internal/config/configdomain/key.go
@@ -82,6 +82,7 @@ const (
 	KeyDeprecatedSyncStrategy              = Key("git-town.sync-strategy")
 	KeyDetached                            = Key("git-town.detached")
 	KeyDevRemote                           = Key("git-town.dev-remote")
+	KeyDisplayTypes                        = Key("git-town.display-types")
 	KeyFeatureRegex                        = Key("git-town.feature-regex")
 	KeyForgejoToken                        = Key("git-town.forgejo-token")
 	KeyForgeType                           = Key("git-town.forge-type")
@@ -145,6 +146,7 @@ var keys = []Key{
 	KeyDeprecatedSyncStrategy,
 	KeyDetached,
 	KeyDevRemote,
+	KeyDisplayTypes,
 	KeyFeatureRegex,
 	KeyForgejoToken,
 	KeyForgeType,
diff --git a/internal/config/configdomain/partial_config.go b/internal/config/configdomain/partial_config.go
index e550811e2..fac2e4636 100644
--- a/internal/config/configdomain/partial_config.go
+++ b/internal/config/configdomain/partial_config.go
@@ -25,6 +25,7 @@ type PartialConfig struct {
 	ContributionRegex        Option[ContributionRegex]
 	Detached                 Option[Detached]
 	DevRemote                Option[gitdomain.Remote]
+	DisplayTypes             Option[DisplayTypes]
 	DryRun                   Option[DryRun]
 	FeatureRegex             Option[FeatureRegex]
 	ForgeType                Option[forgedomain.ForgeType]
@@ -80,6 +81,7 @@ func (self PartialConfig) Merge(other PartialConfig) PartialConfig {
 		ContributionRegex:        other.ContributionRegex.Or(self.ContributionRegex),
 		Detached:                 other.Detached.Or(self.Detached),
 		DevRemote:                other.DevRemote.Or(self.DevRemote),
+		DisplayTypes:             other.DisplayTypes.Or(self.DisplayTypes),
 		DryRun:                   other.DryRun.Or(self.DryRun),
 		FeatureRegex:             other.FeatureRegex.Or(self.FeatureRegex),
 		ForgeType:                other.ForgeType.Or(self.ForgeType),
diff --git a/internal/config/configfile/data.go b/internal/config/configfile/data.go
index 0cab8be4d..30d3d9a60 100644
--- a/internal/config/configfile/data.go
+++ b/internal/config/configfile/data.go
@@ -21,6 +21,7 @@ type Data struct {
 type Branches struct {
 	ContributionRegex *string  `toml:"contribution-regex"`
 	DefaultType       *string  `toml:"default-type"`
+	DisplayTypes      *string  `toml:"display-types"`
 	FeatureRegex      *string  `toml:"feature-regex"`
 	Main              *string  `toml:"main"`
 	ObservedRegex     *string  `toml:"observed-regex"`
diff --git a/internal/config/configfile/load.go b/internal/config/configfile/load.go
index 4883cc409..e895881b5 100644
--- a/internal/config/configfile/load.go
+++ b/internal/config/configfile/load.go
@@ -50,6 +50,7 @@ func Validate(data Data, finalMessages stringslice.Collector) (configdomain.Part
 	var contributionRegex Option[configdomain.ContributionRegex]
 	var detached Option[configdomain.Detached]
 	var devRemote Option[gitdomain.Remote]
+	var displayTypes Option[configdomain.DisplayTypes]
 	var featureRegex Option[configdomain.FeatureRegex]
 	var forgeType Option[forgedomain.ForgeType]
 	var githubConnectorType Option[forgedomain.GitHubConnectorType]
@@ -116,6 +117,10 @@ func Validate(data Data, finalMessages stringslice.Collector) (configdomain.Part
 			ec.Check(err)
 			unknownBranchType = configdomain.UnknownBranchTypeOpt(branchType)
 		}
+		if data.Branches.DisplayTypes != nil {
+			displayTypes, err = configdomain.ParseDisplayTypes(*data.Branches.DisplayTypes, "config file")
+			ec.Check(err)
+		}
 		if data.Branches.FeatureRegex != nil {
 			verifiedRegexOpt, err := configdomain.ParseRegex(*data.Branches.FeatureRegex)
 			ec.Check(err)
@@ -261,6 +266,7 @@ func Validate(data Data, finalMessages stringslice.Collector) (configdomain.Part
 		ForgejoToken:             None[forgedomain.ForgejoToken](),
 		ContributionRegex:        contributionRegex,
 		Detached:                 detached,
+		DisplayTypes:             displayTypes,
 		DryRun:                   None[configdomain.DryRun](),
 		UnknownBranchType:        unknownBranchType,
 		DevRemote:                devRemote,
diff --git a/internal/config/configfile/save.go b/internal/config/configfile/save.go
index ec99dc0db..d2b646738 100644
--- a/internal/config/configfile/save.go
+++ b/internal/config/configfile/save.go
@@ -21,6 +21,7 @@ func RenderTOML(data configdomain.PartialConfig) string {
 	result.WriteString("# See https://www.git-town.com/configuration-file for details\n")
 
 	main, hasMain := data.MainBranch.Get()
+	displayTypes, hasDisplayTypes := data.DisplayTypes.Get()
 	hasPerennialBranches := len(data.PerennialBranches) > 0
 	order, hasOrder := data.Order.Get()
 	perennialRegex, hasPerennialRegex := data.PerennialRegex.Get()
@@ -38,6 +39,9 @@ func RenderTOML(data configdomain.PartialConfig) string {
 		if hasPerennialRegex {
 			result.WriteString(fmt.Sprintf("perennial-regex = %q\n", perennialRegex))
 		}
+		if hasDisplayTypes {
+			result.WriteString(fmt.Sprintf("display-types = %q\n", displayTypes.Serialize(" ")))
+		}
 	}
 
 	newBranchType, hasNewBranchType := data.NewBranchType.Get()
diff --git a/internal/config/envconfig/load.go b/internal/config/envconfig/load.go
index 0dd3b0d54..4727ca157 100644
--- a/internal/config/envconfig/load.go
+++ b/internal/config/envconfig/load.go
@@ -19,6 +19,7 @@ const (
 	contributionRegex        = "GIT_TOWN_CONTRIBUTION_REGEX"
 	detached                 = "GIT_TOWN_DETACHED"
 	devRemote                = "GIT_TOWN_DEV_REMOTE"
+	displayTypes             = "GIT_TOWN_DISPLAY_TYPES"
 	dryRun                   = "GIT_TOWN_DRY_RUN"
 	featureRegex             = "GIT_TOWN_FEATURE_REGEX"
 	forgeType                = "GIT_TOWN_FORGE_TYPE"
@@ -60,6 +61,7 @@ func Load(env EnvVars) (configdomain.PartialConfig, error) {
 	autoSync, errAutoSync := gohacks.ParseBoolOpt[configdomain.AutoSync](env.Get(autoSync), autoSync)
 	contributionRegex, errContribRegex := configdomain.ParseContributionRegex(env.Get(contributionRegex))
 	detached, errDetached := gohacks.ParseBoolOpt[configdomain.Detached](env.Get(detached), detached)
+	displayTypesOpt, errDisplayTypes := configdomain.ParseDisplayTypes(env.Get(displayTypes), displayTypes)
 	dryRun, errDryRun := gohacks.ParseBoolOpt[configdomain.DryRun](env.Get(dryRun), dryRun)
 	featureRegex, errFeatureRegex := configdomain.ParseFeatureRegex(env.Get(featureRegex))
 	forgeType, errForgeType := forgedomain.ParseForgeType(env.Get(forgeType))
@@ -95,6 +97,7 @@ func Load(env EnvVars) (configdomain.PartialConfig, error) {
 		errAutoSync,
 		errContribRegex,
 		errDetached,
+		errDisplayTypes,
 		errDryRun,
 		errFeatureRegex,
 		errForgeType,
@@ -131,6 +134,7 @@ func Load(env EnvVars) (configdomain.PartialConfig, error) {
 		ContributionRegex:        contributionRegex,
 		Detached:                 detached,
 		DevRemote:                gitdomain.NewRemote(env.Get(devRemote)),
+		DisplayTypes:             displayTypesOpt,
 		DryRun:                   dryRun,
 		FeatureRegex:             featureRegex,
 		ForgeType:                forgeType,
diff --git a/internal/config/normal_config.go b/internal/config/normal_config.go
index cb11495dd..ed55d2e9a 100644
--- a/internal/config/normal_config.go
+++ b/internal/config/normal_config.go
@@ -38,6 +38,7 @@ type NormalConfig struct {
 	ContributionRegex        Option[configdomain.ContributionRegex]
 	Detached                 configdomain.Detached
 	DevRemote                gitdomain.Remote
+	DisplayTypes             configdomain.DisplayTypes
 	DryRun                   configdomain.DryRun // whether to only print the Git commands but not execute them
 	FeatureRegex             Option[configdomain.FeatureRegex]
 	ForgeType                Option[forgedomain.ForgeType] // None = auto-detect
@@ -103,6 +104,7 @@ func (self *NormalConfig) OverwriteWith(other configdomain.PartialConfig) Normal
 		ContributionRegex:        other.ContributionRegex.Or(self.ContributionRegex),
 		Detached:                 other.Detached.GetOr(self.Detached),
 		DevRemote:                other.DevRemote.GetOr(self.DevRemote),
+		DisplayTypes:             other.DisplayTypes.GetOr(self.DisplayTypes),
 		DryRun:                   other.DryRun.GetOr(self.DryRun),
 		FeatureRegex:             other.FeatureRegex.Or(self.FeatureRegex),
 		ForgeType:                other.ForgeType.Or(self.ForgeType),
@@ -242,15 +244,19 @@ func (self *NormalConfig) SetPerennialBranches(runner subshelldomain.Runner, bra
 
 func DefaultNormalConfig() NormalConfig {
 	return NormalConfig{
-		Aliases:                  configdomain.Aliases{},
-		AutoResolve:              true,
-		AutoSync:                 true,
-		BitbucketAppPassword:     None[forgedomain.BitbucketAppPassword](),
-		BitbucketUsername:        None[forgedomain.BitbucketUsername](),
-		BranchTypeOverrides:      configdomain.BranchTypeOverrides{},
-		ContributionRegex:        None[configdomain.ContributionRegex](),
-		Detached:                 false,
-		DevRemote:                gitdomain.RemoteOrigin,
+		Aliases:              configdomain.Aliases{},
+		AutoResolve:          true,
+		AutoSync:             true,
+		BitbucketAppPassword: None[forgedomain.BitbucketAppPassword](),
+		BitbucketUsername:    None[forgedomain.BitbucketUsername](),
+		BranchTypeOverrides:  configdomain.BranchTypeOverrides{},
+		ContributionRegex:    None[configdomain.ContributionRegex](),
+		Detached:             false,
+		DevRemote:            gitdomain.RemoteOrigin,
+		DisplayTypes: configdomain.DisplayTypes{
+			Quantifier:  configdomain.QuantifierNo,
+			BranchTypes: []configdomain.BranchType{configdomain.BranchTypeFeatureBranch, configdomain.BranchTypeMainBranch},
+		},
 		DryRun:                   false,
 		FeatureRegex:             None[configdomain.FeatureRegex](),
 		ForgeType:                None[forgedomain.ForgeType](),
@@ -299,6 +305,7 @@ func NewNormalConfigFromPartial(partial configdomain.PartialConfig, defaults Nor
 		ContributionRegex:        partial.ContributionRegex,
 		Detached:                 partial.Detached.GetOr(defaults.Detached),
 		DevRemote:                partial.DevRemote.GetOr(defaults.DevRemote),
+		DisplayTypes:             partial.DisplayTypes.GetOr(defaults.DisplayTypes),
 		DryRun:                   partial.DryRun.GetOr(defaults.DryRun),
 		FeatureRegex:             partial.FeatureRegex,
 		ForgeType:                partial.ForgeType,
diff --git a/internal/config/parse_snapshot.go b/internal/config/parse_snapshot.go
index 952bf5698..581262d5f 100644
--- a/internal/config/parse_snapshot.go
+++ b/internal/config/parse_snapshot.go
@@ -83,6 +83,7 @@ func NewPartialConfigFromSnapshot(snapshot configdomain.SingleSnapshot, updateOu
 	branchTypeOverrides, errBranchTypeOverride := NewBranchTypeOverridesInSnapshot(snapshot, runner)
 	contributionRegex, errContributionRegex := configdomain.ParseContributionRegex(snapshot[configdomain.KeyContributionRegex])
 	detached, errDetached := gohacks.ParseBoolOpt[configdomain.Detached](snapshot[configdomain.KeyDetached], configdomain.KeyDetached.String())
+	displayTypes, errDisplayTypes := configdomain.ParseDisplayTypes(snapshot[configdomain.KeyDisplayTypes], configdomain.KeyDisplayTypes.String())
 	featureRegex, errFeatureRegex := configdomain.ParseFeatureRegex(snapshot[configdomain.KeyFeatureRegex])
 	forgeType, errForgeType := forgedomain.ParseForgeType(snapshot[configdomain.KeyForgeType])
 	githubConnectorType, errGitHubConnectorType := forgedomain.ParseGitHubConnectorType(snapshot[configdomain.KeyGitHubConnectorType])
@@ -114,6 +115,7 @@ func NewPartialConfigFromSnapshot(snapshot configdomain.SingleSnapshot, updateOu
 		errBranchTypeOverride,
 		errContributionRegex,
 		errDetached,
+		errDisplayTypes,
 		errFeatureRegex,
 		errForgeType,
 		errGitHubConnectorType,
@@ -149,6 +151,7 @@ func NewPartialConfigFromSnapshot(snapshot configdomain.SingleSnapshot, updateOu
 		ContributionRegex:        contributionRegex,
 		Detached:                 detached,
 		DevRemote:                gitdomain.NewRemote(snapshot[configdomain.KeyDevRemote]),
+		DisplayTypes:             displayTypes,
 		DryRun:                   None[configdomain.DryRun](),
 		FeatureRegex:             featureRegex,
 		ForgeType:                forgeType,
diff --git a/internal/gohacks/slice/join_sentence.go b/internal/gohacks/slice/join_sentence.go
new file mode 100644
index 000000000..d447a1ec3
--- /dev/null
+++ b/internal/gohacks/slice/join_sentence.go
@@ -0,0 +1,30 @@
+package slice
+
+import (
+	"fmt"
+	"strings"
+)
+
+// JoinSentenceQuotes joins the given elements into a natural sentence
+// while surrounding each element with double-quotes.
+func JoinSentenceQuotes[T fmt.Stringer](elements []T) string {
+	if len(elements) == 0 {
+		return ""
+	}
+	if len(elements) == 1 {
+		return `"` + elements[0].String() + `"`
+	}
+	if len(elements) == 2 {
+		return `"` + elements[0].String() + `" and "` + elements[1].String() + `"`
+	}
+	// 3+ items: use Oxford comma style
+	var parts []string
+	for i, element := range elements {
+		if i == len(elements)-1 {
+			parts = append(parts, `and "`+element.String()+`"`)
+		} else {
+			parts = append(parts, `"`+element.String()+`"`)
+		}
+	}
+	return strings.Join(parts, ", ")
+}
diff --git a/internal/gohacks/slice/stringify.go b/internal/gohacks/slice/stringify.go
new file mode 100644
index 000000000..4257484ee
--- /dev/null
+++ b/internal/gohacks/slice/stringify.go
@@ -0,0 +1,12 @@
+package slice
+
+import "fmt"
+
+// provides the string version of the given stringers
+func Stringify[T fmt.Stringer](elements []T) []string {
+	result := make([]string, len(elements))
+	for e, element := range elements {
+		result[e] = element.String()
+	}
+	return result
+}
diff --git a/internal/setup/enter.go b/internal/setup/enter.go
index 712f62d60..38be0f6ee 100644
--- a/internal/setup/enter.go
+++ b/internal/setup/enter.go
@@ -261,6 +261,7 @@ EnterForgeData:
 		ContributionRegex:        contributionRegex,
 		Detached:                 detached,
 		DevRemote:                devRemote,
+		DisplayTypes:             None[configdomain.DisplayTypes](),
 		DryRun:                   None[configdomain.DryRun](), // the setup assistant doesn't ask for this
 		FeatureRegex:             featureRegex,
 		ForgeType:                enteredForgeType,
diff --git a/website/src/SUMMARY.md b/website/src/SUMMARY.md
index 6998b8e72..c34154aa0 100644
--- a/website/src/SUMMARY.md
+++ b/website/src/SUMMARY.md
@@ -60,6 +60,7 @@
 - [Preferences](preferences.md)
   - [Branches](preferences/branches.md)
     - [Contribution regex](preferences/contribution-regex.md)
+    - [Display types](preferences/display-types.md)
     - [Feature regex](preferences/feature-regex.md)
     - [Main branch](preferences/main-branch.md)
     - [Observed regex](preferences/observed-regex.md)
diff --git a/website/src/preferences/display-types.md b/website/src/preferences/display-types.md
new file mode 100644
index 000000000..19e72ea95
--- /dev/null
+++ b/website/src/preferences/display-types.md
@@ -0,0 +1,54 @@
+# Display types
+
+This setting allows you to change whether Git Town also displays the
+[branch type](../branch-types.md) in addition to the branch name when showing a
+list of branches.
+
+## Allowed values
+
+- **all** - display the type for all branches
+- **no** - never display the branch type
+- **no &lt;branch types&gt;** - display the type of all branches unless the
+  branch has one of the listed types
+- **&lt;branch types&gt;** - display the type of the branch only if it matches
+  one of the listed branch types
+
+## Examples
+
+- `no feature main` displays the type for all branches except for feature and
+  main branches. _(This is the default setting.)_
+- `prototype observed contribution parked` displays the type only for these four
+  branch types
+
+## Config file
+
+```toml
+[branches]
+display-types = "<value>"
+```
+
+## Git metadata
+
+```wrap
+git config [--global] git-town.display-types <push|propose>
+```
+
+The optional `--global` flag applies this setting to all Git repositories on
+your machine. Without it, this setting applies only to the current Git repo.
+
+## Environment variable
+
+You can configure the branch types display via the `GIT_TOWN_SHARE_NEW_BRANCHES`
+environment variable.
+
+## CLI flags
+
+You can override this setting per command using:
+
+- `--display-types` / `-d` / `--display-types=all`: display the type for all
+  branches
+- `--display-types=no`: never display the branch type
+- `--display-types=contribution+observed`: display the type only for
+  contribution and observed branches
+- `--display-types=no-main-feature` displays the type for all branches except
+  main and feature branches



Test patch:
diff --git a/internal/cli/dialog/switch_branch_test.go b/internal/cli/dialog/switch_branch_test.go
index 9063739a6..ac3901b77 100644
--- a/internal/cli/dialog/switch_branch_test.go
+++ b/internal/cli/dialog/switch_branch_test.go
@@ -473,7 +473,10 @@ func TestSwitchBranch(t *testing.T) {
 				},
 				InitialBranchPos:   Some(0),
 				UncommittedChanges: false,
-				DisplayBranchTypes: false,
+				DisplayBranchTypes: configdomain.DisplayTypes{
+					Quantifier:  configdomain.QuantifierNo,
+					BranchTypes: []configdomain.BranchType{},
+				},
 			}
 			have := model.View()
 			want := `
@@ -499,7 +502,10 @@ func TestSwitchBranch(t *testing.T) {
 				},
 				InitialBranchPos:   Some(0),
 				UncommittedChanges: false,
-				DisplayBranchTypes: false,
+				DisplayBranchTypes: configdomain.DisplayTypes{
+					Quantifier:  configdomain.QuantifierNo,
+					BranchTypes: []configdomain.BranchType{},
+				},
 			}
 			have := model.View()
 			dim := "\x1b[2m"
@@ -534,7 +540,10 @@ func TestSwitchBranch(t *testing.T) {
 				},
 				InitialBranchPos:   Some(0),
 				UncommittedChanges: false,
-				DisplayBranchTypes: false,
+				DisplayBranchTypes: configdomain.DisplayTypes{
+					Quantifier:  configdomain.QuantifierNo,
+					BranchTypes: []configdomain.BranchType{},
+				},
 			}
 			have := model.View()
 			dim := "\x1b[2m"
@@ -573,16 +582,19 @@ func TestSwitchBranch(t *testing.T) {
 				},
 				InitialBranchPos:   Some(0),
 				UncommittedChanges: false,
-				DisplayBranchTypes: true,
+				DisplayBranchTypes: configdomain.DisplayTypes{
+					Quantifier:  configdomain.QuantifierAll,
+					BranchTypes: []configdomain.BranchType{configdomain.BranchTypeFeatureBranch, configdomain.BranchTypeMainBranch},
+				},
 			}
 			have := model.View()
 			dim := "\x1b[2m"
 			reset := "\x1b[0m"
 			want := `
-> main
-    alpha
-      alpha1
-` + dim + `+     alpha2` + reset + `
+> main  ` + dim + `(main)` + reset + `
+    alpha  ` + dim + `(feature)` + reset + `
+      alpha1  ` + dim + `(feature)` + reset + `
+` + dim + `+     alpha2` + reset + `  ` + dim + `(feature)` + reset + `
     beta  ` + dim + `(observed)` + reset + `
       beta1  ` + dim + `(observed)` + reset + `
   other  ` + dim + `(parked)` + reset + `
@@ -606,7 +618,10 @@ func TestSwitchBranch(t *testing.T) {
 				},
 				InitialBranchPos:   Some(0),
 				UncommittedChanges: true,
-				DisplayBranchTypes: false,
+				DisplayBranchTypes: configdomain.DisplayTypes{
+					Quantifier:  configdomain.QuantifierNo,
+					BranchTypes: []configdomain.BranchType{},
+				},
 			}
 			have := model.View()
 			cyanBold := "\x1b[36;1m"
diff --git a/internal/config/configdomain/display_types_test.go b/internal/config/configdomain/display_types_test.go
new file mode 100644
index 000000000..c8d82ae47
--- /dev/null
+++ b/internal/config/configdomain/display_types_test.go
@@ -0,0 +1,191 @@
+package configdomain_test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/git-town/git-town/v22/internal/config/configdomain"
+	"github.com/shoenig/test/must"
+)
+
+func TestDisplayTypes(t *testing.T) {
+	t.Parallel()
+
+	t.Run("ParseDisplayType", func(t *testing.T) {
+		t.Parallel()
+
+		t.Run("all", func(t *testing.T) {
+			t.Parallel()
+			have, err := configdomain.ParseDisplayTypes("all", "unit test")
+			must.NoError(t, err)
+			want := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierAll,
+				BranchTypes: []configdomain.BranchType{},
+			}
+			must.True(t, have.EqualSome(want))
+		})
+		t.Run("no", func(t *testing.T) {
+			t.Parallel()
+			have, err := configdomain.ParseDisplayTypes("no", "unit test")
+			must.NoError(t, err)
+			want := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierNo,
+				BranchTypes: []configdomain.BranchType{},
+			}
+			must.True(t, have.EqualSome(want))
+		})
+
+		t.Run("exclude branch types", func(t *testing.T) {
+			t.Parallel()
+			have, err := configdomain.ParseDisplayTypes("no feature prototype", "unit test")
+			must.NoError(t, err)
+			want := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierNo,
+				BranchTypes: []configdomain.BranchType{configdomain.BranchTypeFeatureBranch, configdomain.BranchTypePrototypeBranch},
+			}
+			must.True(t, have.EqualSome(want))
+		})
+
+		t.Run("only branch types", func(t *testing.T) {
+			t.Parallel()
+			have, err := configdomain.ParseDisplayTypes("observed contribution", "unit test")
+			must.NoError(t, err)
+			want := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierOnly,
+				BranchTypes: []configdomain.BranchType{configdomain.BranchTypeObservedBranch, configdomain.BranchTypeContributionBranch},
+			}
+			must.True(t, have.EqualSome(want))
+		})
+
+		t.Run("empty string", func(t *testing.T) {
+			t.Parallel()
+			have, err := configdomain.ParseDisplayTypes("", "unit test")
+			must.Nil(t, err)
+			must.True(t, have.IsNone())
+		})
+
+		t.Run("invalid string", func(t *testing.T) {
+			t.Parallel()
+			_, err := configdomain.ParseDisplayTypes("zonk", "unit test")
+			must.NotNil(t, err)
+		})
+	})
+
+	t.Run("ShouldDisplayType", func(t *testing.T) {
+		t.Parallel()
+		t.Run("all", func(t *testing.T) {
+			t.Parallel()
+			displayTypes := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierAll,
+				BranchTypes: []configdomain.BranchType{},
+			}
+			for _, branchType := range configdomain.AllBranchTypes() {
+				must.True(t, displayTypes.ShouldDisplayType(branchType))
+			}
+		})
+		t.Run("no", func(t *testing.T) {
+			t.Parallel()
+			displayTypes := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierNo,
+				BranchTypes: []configdomain.BranchType{},
+			}
+			for _, branchType := range configdomain.AllBranchTypes() {
+				t.Run(branchType.String(), func(t *testing.T) {
+					t.Parallel()
+					must.False(t, displayTypes.ShouldDisplayType(branchType))
+				})
+			}
+		})
+		t.Run("exclude specific types", func(t *testing.T) {
+			t.Parallel()
+			displayTypes := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierNo,
+				BranchTypes: []configdomain.BranchType{configdomain.BranchTypeFeatureBranch, configdomain.BranchTypePrototypeBranch},
+			}
+			tests := map[configdomain.BranchType]bool{
+				configdomain.BranchTypeFeatureBranch:      false,
+				configdomain.BranchTypePrototypeBranch:    false,
+				configdomain.BranchTypePerennialBranch:    true,
+				configdomain.BranchTypeMainBranch:         true,
+				configdomain.BranchTypeObservedBranch:     true,
+				configdomain.BranchTypeContributionBranch: true,
+			}
+			for give, want := range tests {
+				t.Run(fmt.Sprintf("%s ==> %t", give, want), func(t *testing.T) {
+					t.Parallel()
+					have := displayTypes.ShouldDisplayType(give)
+					must.EqOp(t, want, have)
+				})
+			}
+		})
+		t.Run("only specific types", func(t *testing.T) {
+			t.Parallel()
+			displayTypes := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierOnly,
+				BranchTypes: []configdomain.BranchType{configdomain.BranchTypeObservedBranch, configdomain.BranchTypeContributionBranch},
+			}
+			tests := map[configdomain.BranchType]bool{
+				configdomain.BranchTypeFeatureBranch:      false,
+				configdomain.BranchTypePrototypeBranch:    false,
+				configdomain.BranchTypePerennialBranch:    false,
+				configdomain.BranchTypeMainBranch:         false,
+				configdomain.BranchTypeObservedBranch:     true,
+				configdomain.BranchTypeContributionBranch: true,
+			}
+			for give, want := range tests {
+				t.Run(fmt.Sprintf("%s ==> %t", give, want), func(t *testing.T) {
+					t.Parallel()
+					have := displayTypes.ShouldDisplayType(give)
+					must.EqOp(t, want, have)
+				})
+			}
+		})
+	})
+
+	t.Run("String", func(t *testing.T) {
+		t.Parallel()
+		t.Run("all", func(t *testing.T) {
+			t.Parallel()
+			give := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierAll,
+				BranchTypes: []configdomain.BranchType{},
+			}
+			have := give.String()
+			want := "all branch types"
+			must.EqOp(t, want, have)
+		})
+
+		t.Run("no", func(t *testing.T) {
+			t.Parallel()
+			give := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierNo,
+				BranchTypes: []configdomain.BranchType{},
+			}
+			have := give.String()
+			want := "no branch types"
+			must.EqOp(t, want, have)
+		})
+
+		t.Run("exclude specific branches", func(t *testing.T) {
+			t.Parallel()
+			give := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierNo,
+				BranchTypes: []configdomain.BranchType{configdomain.BranchTypeFeatureBranch, configdomain.BranchTypePrototypeBranch, configdomain.BranchTypeMainBranch},
+			}
+			have := give.String()
+			want := `all branch types except "feature", "prototype", and "main"`
+			must.EqOp(t, want, have)
+		})
+
+		t.Run("only specific branches", func(t *testing.T) {
+			t.Parallel()
+			give := configdomain.DisplayTypes{
+				Quantifier:  configdomain.QuantifierOnly,
+				BranchTypes: []configdomain.BranchType{configdomain.BranchTypeFeatureBranch, configdomain.BranchTypePrototypeBranch},
+			}
+			have := give.String()
+			want := `only the branch types "feature" and "prototype"`
+			must.EqOp(t, want, have)
+		})
+	})
+}
diff --git a/internal/config/configfile/save_test.go b/internal/config/configfile/save_test.go
index 1d7d8588e..18a67bfc7 100644
--- a/internal/config/configfile/save_test.go
+++ b/internal/config/configfile/save_test.go
@@ -46,8 +46,12 @@ func TestSave(t *testing.T) {
 		t.Run("all options given", func(t *testing.T) {
 			t.Parallel()
 			have := configfile.RenderTOML(configdomain.PartialConfig{
-				AutoResolve:              Some(configdomain.AutoResolve(false)),
-				DevRemote:                Some(gitdomain.RemoteOrigin),
+				AutoResolve: Some(configdomain.AutoResolve(false)),
+				DevRemote:   Some(gitdomain.RemoteOrigin),
+				DisplayTypes: Some(configdomain.DisplayTypes{
+					BranchTypes: []configdomain.BranchType{configdomain.BranchTypeMainBranch, configdomain.BranchTypePerennialBranch},
+					Quantifier:  configdomain.QuantifierNo,
+				}),
 				ForgeType:                asserts.NoError1(forgedomain.ParseForgeType("github")),
 				HostingOriginHostname:    configdomain.ParseHostingOriginHostname("forge"),
 				MainBranch:               Some(gitdomain.NewLocalBranchName("main")),
@@ -74,6 +78,7 @@ main = "main"
 order = "desc"
 perennials = ["qa", "staging"]
 perennial-regex = "perennial-"
+display-types = "no main perennial"
 
 [create]
 new-branch-type = "prototype"
diff --git a/internal/gohacks/slice/join_sentence_test.go b/internal/gohacks/slice/join_sentence_test.go
new file mode 100644
index 000000000..b5d71a893
--- /dev/null
+++ b/internal/gohacks/slice/join_sentence_test.go
@@ -0,0 +1,53 @@
+package slice_test
+
+import (
+	"testing"
+
+	"github.com/git-town/git-town/v22/internal/config/configdomain"
+	"github.com/git-town/git-town/v22/internal/gohacks/slice"
+	"github.com/shoenig/test/must"
+)
+
+func TestJoinSentenceQuotes(t *testing.T) {
+	t.Parallel()
+
+	t.Run("0 elements", func(t *testing.T) {
+		t.Parallel()
+		give := []configdomain.BranchType{}
+		have := slice.JoinSentenceQuotes(give)
+		want := ""
+		must.EqOp(t, want, have)
+	})
+
+	t.Run("1 element", func(t *testing.T) {
+		t.Parallel()
+		give := []configdomain.BranchType{configdomain.BranchTypeMainBranch}
+		have := slice.JoinSentenceQuotes(give)
+		want := `"main"`
+		must.EqOp(t, want, have)
+	})
+
+	t.Run("2 elements", func(t *testing.T) {
+		t.Parallel()
+		give := []configdomain.BranchType{configdomain.BranchTypeMainBranch, configdomain.BranchTypeFeatureBranch}
+		have := slice.JoinSentenceQuotes(give)
+		want := `"main" and "feature"`
+		must.EqOp(t, want, have)
+	})
+
+	t.Run("3 elements", func(t *testing.T) {
+		t.Parallel()
+		give := []configdomain.BranchType{configdomain.BranchTypeMainBranch, configdomain.BranchTypeFeatureBranch, configdomain.BranchTypeContributionBranch}
+		have := slice.JoinSentenceQuotes(give)
+		want := `"main", "feature", and "contribution"`
+		must.EqOp(t, want, have)
+	})
+
+	t.Run("4 elements", func(t *testing.T) {
+		t.Parallel()
+		give := []configdomain.BranchType{configdomain.BranchTypeMainBranch, configdomain.BranchTypeFeatureBranch, configdomain.BranchTypeContributionBranch, configdomain.BranchTypeObservedBranch}
+		have := slice.JoinSentenceQuotes(give)
+		want := `"main", "feature", "contribution", and "observed"`
+		must.EqOp(t, want, have)
+	})
+}
diff --git a/internal/gohacks/slice/stringify_test.go b/internal/gohacks/slice/stringify_test.go
new file mode 100644
index 000000000..15439a6c3
--- /dev/null
+++ b/internal/gohacks/slice/stringify_test.go
@@ -0,0 +1,34 @@
+package slice_test
+
+import (
+	"testing"
+
+	"github.com/git-town/git-town/v22/internal/config/configdomain"
+	"github.com/git-town/git-town/v22/internal/gohacks/slice"
+	"github.com/shoenig/test/must"
+)
+
+func TestStringify(t *testing.T) {
+	t.Parallel()
+
+	t.Run("0 elements", func(t *testing.T) {
+		t.Parallel()
+		have := slice.Stringify([]configdomain.BranchType{})
+		want := []string{}
+		must.Eq(t, want, have)
+	})
+
+	t.Run("1 element", func(t *testing.T) {
+		t.Parallel()
+		have := slice.Stringify([]configdomain.BranchType{configdomain.BranchTypeMainBranch})
+		want := []string{"main"}
+		must.Eq(t, want, have)
+	})
+
+	t.Run("2 elements", func(t *testing.T) {
+		t.Parallel()
+		have := slice.Stringify([]configdomain.BranchType{configdomain.BranchTypeMainBranch, configdomain.BranchTypePerennialBranch})
+		want := []string{"main", "perennial"}
+		must.Eq(t, want, have)
+	})
+}