diff --git a/docs/Config.md b/docs/Config.md
index bc4b5b92b69..447ccae5f35 100644
--- a/docs/Config.md
+++ b/docs/Config.md
@@ -62,7 +62,6 @@ gui:
   showListFooter: true # for seeing the '5 of 20' message in list panels
   showRandomTip: true
   showBranchCommitHash: false # show commit hashes alongside branch names
-  experimentalShowBranchHeads: false # visualize branch heads with (*) in commits list
   showBottomLine: true # for hiding the bottom information line (unless it has important information to tell you)
   showCommandLog: true
   showIcons: false # deprecated: use nerdFontsVersion instead
diff --git a/docs/README.md b/docs/README.md
index acce8338c7d..604c8a07a9d 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -6,4 +6,5 @@
 * [Keybindings](./keybindings)
 * [Undo/Redo](./Undoing.md)
 * [Searching/Filtering](./Searching.md)
+* [Stacked Branches](./Stacked_Branches.md)
 * [Dev docs](./dev)
diff --git a/docs/Stacked_Branches.md b/docs/Stacked_Branches.md
new file mode 100644
index 00000000000..b73e4aca59b
--- /dev/null
+++ b/docs/Stacked_Branches.md
@@ -0,0 +1,18 @@
+# Working with stacked branches
+
+When working on a large branch it can often be useful to break it down into
+smaller pieces, and it can help to create separate branches for each independent
+chunk of changes. For example, you could have one branch for preparatory
+refactorings, one for backend changes, and one for frontend changes. Those
+branches would then all be stacked onto each other.
+
+Git has support for rebasing such a stack as a whole; you can enable it by
+setting the git config `rebase.updateRfs` to true. If you then rebase the
+topmost branch of the stack, the other ones in the stack will follow. This
+includes interactive rebases, so for example amending a commit in the first
+branch of the stack will "just work" in the sense that it keeps the other
+branches properly stacked onto it.
+
+Lazygit visualizes the invidual branch heads in the stack by marking them with a
+cyan asterisk (or a cyan branch symbol if you are using [nerd
+fonts](Config.md#display-nerd-fonts-icons)).
diff --git a/pkg/commands/git_commands/branch.go b/pkg/commands/git_commands/branch.go
index caa876f3f8e..24244080bda 100644
--- a/pkg/commands/git_commands/branch.go
+++ b/pkg/commands/git_commands/branch.go
@@ -70,6 +70,21 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
 	}, nil
 }
 
+// CurrentBranchName get name of current branch
+func (self *BranchCommands) CurrentBranchName() (string, error) {
+	cmdArgs := NewGitCmd("rev-parse").
+		Arg("--abbrev-ref").
+		Arg("--verify").
+		Arg("HEAD").
+		ToArgv()
+
+	output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
+	if err == nil {
+		return strings.TrimSpace(output), nil
+	}
+	return "", err
+}
+
 // Delete delete branch
 func (self *BranchCommands) Delete(branch string, force bool) error {
 	cmdArgs := NewGitCmd("branch").
diff --git a/pkg/commands/git_commands/branch_loader.go b/pkg/commands/git_commands/branch_loader.go
index 094354cadbf..b7e55a9108e 100644
--- a/pkg/commands/git_commands/branch_loader.go
+++ b/pkg/commands/git_commands/branch_loader.go
@@ -171,7 +171,7 @@ var branchFields = []string{
 	"upstream:short",
 	"upstream:track",
 	"subject",
-	fmt.Sprintf("objectname:short=%d", utils.COMMIT_HASH_SHORT_SIZE),
+	"objectname",
 }
 
 // Obtain branch information from parsed line output of getRawBranches()
diff --git a/pkg/commands/git_commands/config.go b/pkg/commands/git_commands/config.go
index 46a00f9dbbb..de9707fe9ba 100644
--- a/pkg/commands/git_commands/config.go
+++ b/pkg/commands/git_commands/config.go
@@ -107,3 +107,7 @@ func (self *ConfigCommands) GetCoreCommentChar() byte {
 
 	return '#'
 }
+
+func (self *ConfigCommands) GetRebaseUpdateRefs() bool {
+	return self.gitConfig.GetBool("rebase.updateRefs")
+}
diff --git a/pkg/commands/git_commands/status.go b/pkg/commands/git_commands/status.go
index 13ff02cc015..784ddb424db 100644
--- a/pkg/commands/git_commands/status.go
+++ b/pkg/commands/git_commands/status.go
@@ -1,6 +1,7 @@
 package git_commands
 
 import (
+	"os"
 	"path/filepath"
 	"strconv"
 	"strings"
@@ -71,3 +72,14 @@ func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
 func (self *StatusCommands) IsInMergeState() (bool, error) {
 	return self.os.FileExists(filepath.Join(self.repoPaths.WorktreeGitDirPath(), "MERGE_HEAD"))
 }
+
+// Full ref (e.g. "refs/heads/mybranch") of the branch that is currently
+// being rebased, or empty string when we're not in a rebase
+func (self *StatusCommands) BranchBeingRebased() string {
+	for _, dir := range []string{"rebase-merge", "rebase-apply"} {
+		if bytesContent, err := os.ReadFile(filepath.Join(self.repoPaths.WorktreeGitDirPath(), dir, "head-name")); err == nil {
+			return strings.TrimSpace(string(bytesContent))
+		}
+	}
+	return ""
+}
diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go
index 6ac64b2b57c..a5121f0c5ed 100644
--- a/pkg/config/user_config.go
+++ b/pkg/config/user_config.go
@@ -27,36 +27,35 @@ type RefresherConfig struct {
 }
 
 type GuiConfig struct {
-	AuthorColors                map[string]string  `yaml:"authorColors"`
-	BranchColors                map[string]string  `yaml:"branchColors"`
-	ScrollHeight                int                `yaml:"scrollHeight"`
-	ScrollPastBottom            bool               `yaml:"scrollPastBottom"`
-	MouseEvents                 bool               `yaml:"mouseEvents"`
-	SkipDiscardChangeWarning    bool               `yaml:"skipDiscardChangeWarning"`
-	SkipStashWarning            bool               `yaml:"skipStashWarning"`
-	SidePanelWidth              float64            `yaml:"sidePanelWidth"`
-	ExpandFocusedSidePanel      bool               `yaml:"expandFocusedSidePanel"`
-	MainPanelSplitMode          string             `yaml:"mainPanelSplitMode"`
-	Language                    string             `yaml:"language"`
-	TimeFormat                  string             `yaml:"timeFormat"`
-	ShortTimeFormat             string             `yaml:"shortTimeFormat"`
-	Theme                       ThemeConfig        `yaml:"theme"`
-	CommitLength                CommitLengthConfig `yaml:"commitLength"`
-	SkipNoStagedFilesWarning    bool               `yaml:"skipNoStagedFilesWarning"`
-	ShowListFooter              bool               `yaml:"showListFooter"`
-	ShowFileTree                bool               `yaml:"showFileTree"`
-	ShowRandomTip               bool               `yaml:"showRandomTip"`
-	ShowCommandLog              bool               `yaml:"showCommandLog"`
-	ShowBottomLine              bool               `yaml:"showBottomLine"`
-	ShowIcons                   bool               `yaml:"showIcons"`
-	NerdFontsVersion            string             `yaml:"nerdFontsVersion"`
-	ShowBranchCommitHash        bool               `yaml:"showBranchCommitHash"`
-	ExperimentalShowBranchHeads bool               `yaml:"experimentalShowBranchHeads"`
-	CommandLogSize              int                `yaml:"commandLogSize"`
-	SplitDiff                   string             `yaml:"splitDiff"`
-	SkipRewordInEditorWarning   bool               `yaml:"skipRewordInEditorWarning"`
-	WindowSize                  string             `yaml:"windowSize"`
-	Border                      string             `yaml:"border"`
+	AuthorColors              map[string]string  `yaml:"authorColors"`
+	BranchColors              map[string]string  `yaml:"branchColors"`
+	ScrollHeight              int                `yaml:"scrollHeight"`
+	ScrollPastBottom          bool               `yaml:"scrollPastBottom"`
+	MouseEvents               bool               `yaml:"mouseEvents"`
+	SkipDiscardChangeWarning  bool               `yaml:"skipDiscardChangeWarning"`
+	SkipStashWarning          bool               `yaml:"skipStashWarning"`
+	SidePanelWidth            float64            `yaml:"sidePanelWidth"`
+	ExpandFocusedSidePanel    bool               `yaml:"expandFocusedSidePanel"`
+	MainPanelSplitMode        string             `yaml:"mainPanelSplitMode"`
+	Language                  string             `yaml:"language"`
+	TimeFormat                string             `yaml:"timeFormat"`
+	ShortTimeFormat           string             `yaml:"shortTimeFormat"`
+	Theme                     ThemeConfig        `yaml:"theme"`
+	CommitLength              CommitLengthConfig `yaml:"commitLength"`
+	SkipNoStagedFilesWarning  bool               `yaml:"skipNoStagedFilesWarning"`
+	ShowListFooter            bool               `yaml:"showListFooter"`
+	ShowFileTree              bool               `yaml:"showFileTree"`
+	ShowRandomTip             bool               `yaml:"showRandomTip"`
+	ShowCommandLog            bool               `yaml:"showCommandLog"`
+	ShowBottomLine            bool               `yaml:"showBottomLine"`
+	ShowIcons                 bool               `yaml:"showIcons"`
+	NerdFontsVersion          string             `yaml:"nerdFontsVersion"`
+	ShowBranchCommitHash      bool               `yaml:"showBranchCommitHash"`
+	CommandLogSize            int                `yaml:"commandLogSize"`
+	SplitDiff                 string             `yaml:"splitDiff"`
+	SkipRewordInEditorWarning bool               `yaml:"skipRewordInEditorWarning"`
+	WindowSize                string             `yaml:"windowSize"`
+	Border                    string             `yaml:"border"`
 }
 
 type ThemeConfig struct {
@@ -436,21 +435,20 @@ func GetDefaultConfig() *UserConfig {
 				UnstagedChangesColor:       []string{"red"},
 				DefaultFgColor:             []string{"default"},
 			},
-			CommitLength:                CommitLengthConfig{Show: true},
-			SkipNoStagedFilesWarning:    false,
-			ShowListFooter:              true,
-			ShowCommandLog:              true,
-			ShowBottomLine:              true,
-			ShowFileTree:                true,
-			ShowRandomTip:               true,
-			ShowIcons:                   false,
-			NerdFontsVersion:            "",
-			ExperimentalShowBranchHeads: false,
-			ShowBranchCommitHash:        false,
-			CommandLogSize:              8,
-			SplitDiff:                   "auto",
-			SkipRewordInEditorWarning:   false,
-			Border:                      "single",
+			CommitLength:              CommitLengthConfig{Show: true},
+			SkipNoStagedFilesWarning:  false,
+			ShowListFooter:            true,
+			ShowCommandLog:            true,
+			ShowBottomLine:            true,
+			ShowFileTree:              true,
+			ShowRandomTip:             true,
+			ShowIcons:                 false,
+			NerdFontsVersion:          "",
+			ShowBranchCommitHash:      false,
+			CommandLogSize:            8,
+			SplitDiff:                 "auto",
+			SkipRewordInEditorWarning: false,
+			Border:                    "single",
 		},
 		Git: GitConfig{
 			Paging: PagingConfig{
diff --git a/pkg/gui/context/branches_context.go b/pkg/gui/context/branches_context.go
index 85c45c64a80..e4806165f3b 100644
--- a/pkg/gui/context/branches_context.go
+++ b/pkg/gui/context/branches_context.go
@@ -83,3 +83,7 @@ func (self *BranchesContext) GetDiffTerminals() []string {
 	}
 	return nil
 }
+
+func (self *BranchesContext) ShowBranchHeadsInSubCommits() bool {
+	return true
+}
diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go
index 84204591cba..417a4f30a65 100644
--- a/pkg/gui/context/local_commits_context.go
+++ b/pkg/gui/context/local_commits_context.go
@@ -38,10 +38,14 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
 		}
 
 		showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == enums.REBASE_MODE_REBASING
+		showBranchMarkerForHeadCommit := c.Git().Config.GetRebaseUpdateRefs()
 
 		return presentation.GetCommitListDisplayStrings(
 			c.Common,
 			c.Model().Commits,
+			c.Model().Branches,
+			c.Model().CheckedOutBranch,
+			showBranchMarkerForHeadCommit,
 			c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
 			c.Modes().CherryPicking.SelectedShaSet(),
 			c.Modes().Diffing.Ref,
diff --git a/pkg/gui/context/reflog_commits_context.go b/pkg/gui/context/reflog_commits_context.go
index 421a7c8d5a3..5038b18706e 100644
--- a/pkg/gui/context/reflog_commits_context.go
+++ b/pkg/gui/context/reflog_commits_context.go
@@ -86,3 +86,7 @@ func (self *ReflogCommitsContext) GetDiffTerminals() []string {
 
 	return []string{itemId}
 }
+
+func (self *ReflogCommitsContext) ShowBranchHeadsInSubCommits() bool {
+	return false
+}
diff --git a/pkg/gui/context/remote_branches_context.go b/pkg/gui/context/remote_branches_context.go
index 602a19a65e8..fbc91f3526f 100644
--- a/pkg/gui/context/remote_branches_context.go
+++ b/pkg/gui/context/remote_branches_context.go
@@ -72,3 +72,7 @@ func (self *RemoteBranchesContext) GetDiffTerminals() []string {
 
 	return []string{itemId}
 }
+
+func (self *RemoteBranchesContext) ShowBranchHeadsInSubCommits() bool {
+	return true
+}
diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go
index 0cf8845898c..ba2f5e3f60a 100644
--- a/pkg/gui/context/sub_commits_context.go
+++ b/pkg/gui/context/sub_commits_context.go
@@ -37,6 +37,13 @@ func NewSubCommitsContext(
 	}
 
 	getDisplayStrings := func(startIdx int, length int) [][]string {
+		// This can happen if a sub-commits view is asked to be rerendered while
+		// it is invisble; for example when switching screen modes, which
+		// rerenders all views.
+		if viewModel.GetRef() == nil {
+			return [][]string{}
+		}
+
 		selectedCommitSha := ""
 		if c.CurrentContext().GetKey() == SUB_COMMITS_CONTEXT_KEY {
 			selectedCommit := viewModel.GetSelected()
@@ -44,9 +51,17 @@ func NewSubCommitsContext(
 				selectedCommitSha = selectedCommit.Sha
 			}
 		}
+		branches := []*models.Branch{}
+		if viewModel.GetShowBranchHeads() {
+			branches = c.Model().Branches
+		}
+		showBranchMarkerForHeadCommit := c.Git().Config.GetRebaseUpdateRefs()
 		return presentation.GetCommitListDisplayStrings(
 			c.Common,
 			c.Model().SubCommits,
+			branches,
+			viewModel.GetRef().RefName(),
+			showBranchMarkerForHeadCommit,
 			c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
 			c.Modes().CherryPicking.SelectedShaSet(),
 			c.Modes().Diffing.Ref,
@@ -97,7 +112,8 @@ type SubCommitsViewModel struct {
 	ref types.Ref
 	*ListViewModel[*models.Commit]
 
-	limitCommits bool
+	limitCommits    bool
+	showBranchHeads bool
 }
 
 func (self *SubCommitsViewModel) SetRef(ref types.Ref) {
@@ -108,6 +124,14 @@ func (self *SubCommitsViewModel) GetRef() types.Ref {
 	return self.ref
 }
 
+func (self *SubCommitsViewModel) SetShowBranchHeads(value bool) {
+	self.showBranchHeads = value
+}
+
+func (self *SubCommitsViewModel) GetShowBranchHeads() bool {
+	return self.showBranchHeads
+}
+
 func (self *SubCommitsContext) GetSelectedItemId() string {
 	item := self.GetSelected()
 	if item == nil {
diff --git a/pkg/gui/context/tags_context.go b/pkg/gui/context/tags_context.go
index 71ea369814e..95b845a28e9 100644
--- a/pkg/gui/context/tags_context.go
+++ b/pkg/gui/context/tags_context.go
@@ -69,3 +69,7 @@ func (self *TagsContext) GetDiffTerminals() []string {
 
 	return []string{itemId}
 }
+
+func (self *TagsContext) ShowBranchHeadsInSubCommits() bool {
+	return true
+}
diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go
index 182e20d85a6..fd586c4d596 100644
--- a/pkg/gui/controllers/helpers/refresh_helper.go
+++ b/pkg/gui/controllers/helpers/refresh_helper.go
@@ -272,6 +272,32 @@ func (self *RefreshHelper) refreshCommitsAndCommitFiles() {
 	}
 }
 
+func (self *RefreshHelper) determineCheckedOutBranchName() string {
+	if rebasedBranch := self.c.Git().Status.BranchBeingRebased(); rebasedBranch != "" {
+		// During a rebase we're on a detached head, so cannot determine the
+		// branch name in the usual way. We need to read it from the
+		// ".git/rebase-merge/head-name" file instead.
+		return strings.TrimPrefix(rebasedBranch, "refs/heads/")
+	}
+
+	if bisectInfo := self.c.Git().Bisect.GetInfo(); bisectInfo.Bisecting() && bisectInfo.GetStartSha() != "" {
+		// Likewise, when we're bisecting we're on a detached head as well. In
+		// this case we read the branch name from the ".git/BISECT_START" file.
+		return bisectInfo.GetStartSha()
+	}
+
+	// In all other cases, get the branch name by asking git what branch is
+	// checked out. Note that if we're on a detached head (for reasons other
+	// than rebasing or bisecting, i.e. it was explicitly checked out), then
+	// this will return its sha.
+	if branchName, err := self.c.Git().Branch.CurrentBranchName(); err == nil {
+		return branchName
+	}
+
+	// Should never get here unless the working copy is corrupt
+	return ""
+}
+
 func (self *RefreshHelper) refreshCommitsWithLimit() error {
 	self.c.Mutexes().LocalCommitsMutex.Lock()
 	defer self.c.Mutexes().LocalCommitsMutex.Unlock()
@@ -291,6 +317,7 @@ func (self *RefreshHelper) refreshCommitsWithLimit() error {
 	self.c.Model().Commits = commits
 	self.RefreshAuthors(commits)
 	self.c.Model().WorkingTreeStateAtLastCommitRefresh = self.c.Git().Status.WorkingTreeState()
+	self.c.Model().CheckedOutBranch = self.determineCheckedOutBranchName()
 
 	return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
 }
@@ -412,6 +439,12 @@ func (self *RefreshHelper) refreshBranches() {
 		self.c.Log.Error(err)
 	}
 
+	// Need to re-render the commits view because the visualization of local
+	// branch heads might have changed
+	if err := self.c.Contexts().LocalCommits.HandleRender(); err != nil {
+		self.c.Log.Error(err)
+	}
+
 	self.refreshStatus()
 }
 
diff --git a/pkg/gui/controllers/switch_to_sub_commits_controller.go b/pkg/gui/controllers/switch_to_sub_commits_controller.go
index 8aa92f14bd6..68fc7d0a08f 100644
--- a/pkg/gui/controllers/switch_to_sub_commits_controller.go
+++ b/pkg/gui/controllers/switch_to_sub_commits_controller.go
@@ -11,6 +11,7 @@ var _ types.IController = &SwitchToSubCommitsController{}
 type CanSwitchToSubCommits interface {
 	types.Context
 	GetSelectedRef() types.Ref
+	ShowBranchHeadsInSubCommits() bool
 }
 
 type SwitchToSubCommitsController struct {
@@ -79,6 +80,7 @@ func (self *SwitchToSubCommitsController) viewCommits() error {
 	subCommitsContext.SetTitleRef(ref.Description())
 	subCommitsContext.SetRef(ref)
 	subCommitsContext.SetLimitCommits(true)
+	subCommitsContext.SetShowBranchHeads(self.context.ShowBranchHeadsInSubCommits())
 	subCommitsContext.ClearSearchString()
 	subCommitsContext.GetView().ClearSearch()
 
diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go
index 849a56ebabd..6d4620238a8 100644
--- a/pkg/gui/presentation/branches.go
+++ b/pkg/gui/presentation/branches.go
@@ -71,7 +71,7 @@ func getBranchDisplayStrings(
 	}
 
 	if fullDescription || userConfig.Gui.ShowBranchCommitHash {
-		res = append(res, b.CommitHash)
+		res = append(res, utils.ShortSha(b.CommitHash))
 	}
 
 	res = append(res, coloredName)
diff --git a/pkg/gui/presentation/commits.go b/pkg/gui/presentation/commits.go
index 4da03f02fbd..c00564e9df0 100644
--- a/pkg/gui/presentation/commits.go
+++ b/pkg/gui/presentation/commits.go
@@ -39,6 +39,9 @@ type bisectBounds struct {
 func GetCommitListDisplayStrings(
 	common *common.Common,
 	commits []*models.Commit,
+	branches []*models.Branch,
+	currentBranchName string,
+	showBranchMarkerForHeadCommit bool,
 	fullDescription bool,
 	cherryPickedCommitShaSet *set.Set[string],
 	diffName string,
@@ -99,6 +102,30 @@ func GetCommitListDisplayStrings(
 		getGraphLine = func(idx int) string { return "" }
 	}
 
+	// Determine the hashes of the local branches for which we want to show a
+	// branch marker in the commits list. We only want to do this for branches
+	// that are not the current branch, and not any of the main branches. The
+	// goal is to visualize stacks of local branches, so anything that doesn't
+	// contribute to a branch stack shouldn't show a marker.
+	//
+	// If there are other branches pointing to the current head commit, we only
+	// want to show the marker if the rebase.updateRefs config is on.
+	branchHeadsToVisualize := set.NewFromSlice(lo.FilterMap(branches,
+		func(b *models.Branch, index int) (string, bool) {
+			return b.CommitHash,
+				// Don't consider branches that don't have a commit hash. As far
+				// as I can see, this happens for a detached head, so filter
+				// these out
+				b.CommitHash != "" &&
+					// Don't show a marker for the current branch
+					b.Name != currentBranchName &&
+					// Don't show a marker for main branches
+					!lo.Contains(common.UserConfig.Git.MainBranches, b.Name) &&
+					// Don't show a marker for the head commit unless the
+					// rebase.updateRefs config is on
+					(showBranchMarkerForHeadCommit || b.CommitHash != commits[0].Sha)
+		}))
+
 	lines := make([][]string, 0, len(filteredCommits))
 	var bisectStatus BisectStatus
 	for i, commit := range filteredCommits {
@@ -112,6 +139,7 @@ func GetCommitListDisplayStrings(
 		lines = append(lines, displayCommit(
 			common,
 			commit,
+			branchHeadsToVisualize,
 			cherryPickedCommitShaSet,
 			diffName,
 			timeFormat,
@@ -260,6 +288,7 @@ func getBisectStatusText(bisectStatus BisectStatus, bisectInfo *git_commands.Bis
 func displayCommit(
 	common *common.Common,
 	commit *models.Commit,
+	branchHeadsToVisualize *set.Set[string],
 	cherryPickedCommitShaSet *set.Set[string],
 	diffName string,
 	timeFormat string,
@@ -289,8 +318,11 @@ func displayCommit(
 	} else {
 		if len(commit.Tags) > 0 {
 			tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(commit.Tags, " ")) + " "
-		} else if common.UserConfig.Gui.ExperimentalShowBranchHeads && commit.ExtraInfo != "" {
-			tagString = style.FgMagenta.SetBold().Sprint("(*)") + " "
+		}
+
+		if branchHeadsToVisualize.Includes(commit.Sha) && commit.Status != models.StatusMerged {
+			tagString = style.FgCyan.SetBold().Sprint(
+				lo.Ternary(icons.IsIconEnabled(), icons.BRANCH_ICON, "*") + " " + tagString)
 		}
 	}
 
diff --git a/pkg/gui/presentation/commits_test.go b/pkg/gui/presentation/commits_test.go
index 31c6c813dae..4bf4c09af03 100644
--- a/pkg/gui/presentation/commits_test.go
+++ b/pkg/gui/presentation/commits_test.go
@@ -28,6 +28,9 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
 	scenarios := []struct {
 		testName                 string
 		commits                  []*models.Commit
+		branches                 []*models.Branch
+		currentBranchName        string
+		hasUpdateRefConfig       bool
 		fullDescription          bool
 		cherryPickedCommitShaSet *set.Set[string]
 		diffName                 string
@@ -72,6 +75,120 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
 		sha2 commit2
 						`),
 		},
+		{
+			testName: "commit with tags",
+			commits: []*models.Commit{
+				{Name: "commit1", Sha: "sha1", Tags: []string{"tag1", "tag2"}},
+				{Name: "commit2", Sha: "sha2"},
+			},
+			startIdx:                 0,
+			length:                   2,
+			showGraph:                false,
+			bisectInfo:               git_commands.NewNullBisectInfo(),
+			cherryPickedCommitShaSet: set.New[string](),
+			now:                      time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
+			expected: formatExpected(`
+		sha1 tag1 tag2 commit1
+		sha2 commit2
+						`),
+		},
+		{
+			testName: "show local branch head, except the current branch, main branches, or merged branches",
+			commits: []*models.Commit{
+				{Name: "commit1", Sha: "sha1"},
+				{Name: "commit2", Sha: "sha2"},
+				{Name: "commit3", Sha: "sha3"},
+				{Name: "commit4", Sha: "sha4", Status: models.StatusMerged},
+			},
+			branches: []*models.Branch{
+				{Name: "current-branch", CommitHash: "sha1", Head: true},
+				{Name: "other-branch", CommitHash: "sha2", Head: false},
+				{Name: "master", CommitHash: "sha3", Head: false},
+				{Name: "old-branch", CommitHash: "sha4", Head: false},
+			},
+			currentBranchName:        "current-branch",
+			hasUpdateRefConfig:       true,
+			startIdx:                 0,
+			length:                   4,
+			showGraph:                false,
+			bisectInfo:               git_commands.NewNullBisectInfo(),
+			cherryPickedCommitShaSet: set.New[string](),
+			now:                      time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
+			expected: formatExpected(`
+		sha1 commit1
+		sha2 * commit2
+		sha3 commit3
+		sha4 commit4
+						`),
+		},
+		{
+			testName: "show local branch head for head commit if updateRefs is on",
+			commits: []*models.Commit{
+				{Name: "commit1", Sha: "sha1"},
+				{Name: "commit2", Sha: "sha2"},
+			},
+			branches: []*models.Branch{
+				{Name: "current-branch", CommitHash: "sha1", Head: true},
+				{Name: "other-branch", CommitHash: "sha1", Head: false},
+			},
+			currentBranchName:        "current-branch",
+			hasUpdateRefConfig:       true,
+			startIdx:                 0,
+			length:                   2,
+			showGraph:                false,
+			bisectInfo:               git_commands.NewNullBisectInfo(),
+			cherryPickedCommitShaSet: set.New[string](),
+			now:                      time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
+			expected: formatExpected(`
+		sha1 * commit1
+		sha2 commit2
+						`),
+		},
+		{
+			testName: "don't show local branch head for head commit if updateRefs is off",
+			commits: []*models.Commit{
+				{Name: "commit1", Sha: "sha1"},
+				{Name: "commit2", Sha: "sha2"},
+			},
+			branches: []*models.Branch{
+				{Name: "current-branch", CommitHash: "sha1", Head: true},
+				{Name: "other-branch", CommitHash: "sha1", Head: false},
+			},
+			currentBranchName:        "current-branch",
+			hasUpdateRefConfig:       false,
+			startIdx:                 0,
+			length:                   2,
+			showGraph:                false,
+			bisectInfo:               git_commands.NewNullBisectInfo(),
+			cherryPickedCommitShaSet: set.New[string](),
+			now:                      time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
+			expected: formatExpected(`
+		sha1 commit1
+		sha2 commit2
+						`),
+		},
+		{
+			testName: "show local branch head and tag if both exist",
+			commits: []*models.Commit{
+				{Name: "commit1", Sha: "sha1"},
+				{Name: "commit2", Sha: "sha2", Tags: []string{"some-tag"}},
+				{Name: "commit3", Sha: "sha3"},
+			},
+			branches: []*models.Branch{
+				{Name: "some-branch", CommitHash: "sha2"},
+			},
+			startIdx:                 0,
+			length:                   3,
+			showGraph:                false,
+			bisectInfo:               git_commands.NewNullBisectInfo(),
+			cherryPickedCommitShaSet: set.New[string](),
+			now:                      time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
+			expected: formatExpected(`
+		sha1 commit1
+		sha2 * some-tag commit2
+		sha3 commit3
+						`),
+		},
 		{
 			testName: "showing graph",
 			commits: []*models.Commit{
@@ -285,6 +402,9 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
 				result := GetCommitListDisplayStrings(
 					common,
 					s.commits,
+					s.branches,
+					s.currentBranchName,
+					s.hasUpdateRefConfig,
 					s.fullDescription,
 					s.cherryPickedCommitShaSet,
 					s.diffName,
diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go
index 8be1c869793..6480f1fcc20 100644
--- a/pkg/gui/types/common.go
+++ b/pkg/gui/types/common.go
@@ -217,6 +217,10 @@ type Model struct {
 	RemoteBranches                      []*models.RemoteBranch
 	Tags                                []*models.Tag
 
+	// Name of the currently checked out branch. This will be set even when
+	// we're on a detached head because we're rebasing or bisecting.
+	CheckedOutBranch string
+
 	// for displaying suggestions while typing in a file name
 	FilesTrie *patricia.Trie
 
diff --git a/pkg/integration/tests/bisect/basic.go b/pkg/integration/tests/bisect/basic.go
index e17f0d50f1a..1dfe6368b01 100644
--- a/pkg/integration/tests/bisect/basic.go
+++ b/pkg/integration/tests/bisect/basic.go
@@ -11,6 +11,7 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
 	Skip:         false,
 	SetupRepo: func(shell *Shell) {
 		shell.
+			NewBranch("mybranch").
 			CreateNCommits(10)
 	},
 	SetupConfig: func(cfg *config.AppConfig) {},
@@ -31,20 +32,21 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
 
 		t.Views().Commits().
 			Focus().
-			SelectedLine(Contains("commit 10")).
-			NavigateToLine(Contains("commit 09")).
+			SelectedLine(Contains("CI commit 10")).
+			NavigateToLine(Contains("CI commit 09")).
 			Tap(func() {
 				markCommitAsBad()
 
 				t.Views().Information().Content(Contains("Bisecting"))
 			}).
 			SelectedLine(Contains("<-- bad")).
-			NavigateToLine(Contains("commit 02")).
+			NavigateToLine(Contains("CI commit 02")).
 			Tap(markCommitAsGood).
+			TopLines(Contains("CI commit 10")).
 			// lazygit will land us in the commit between our good and bad commits.
-			SelectedLine(Contains("commit 05").Contains("<-- current")).
+			SelectedLine(Contains("CI commit 05").Contains("<-- current")).
 			Tap(markCommitAsBad).
-			SelectedLine(Contains("commit 04").Contains("<-- current")).
+			SelectedLine(Contains("CI commit 04").Contains("<-- current")).
 			Tap(func() {
 				markCommitAsGood()
 
@@ -52,7 +54,7 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
 				t.ExpectPopup().Alert().Title(Equals("Bisect complete")).Content(MatchesRegexp("(?s)commit 05.*Do you want to reset")).Confirm()
 			}).
 			IsFocused().
-			Content(Contains("commit 04"))
+			Content(Contains("CI commit 04"))
 
 		t.Views().Information().Content(DoesNotContain("Bisecting"))
 	},
diff --git a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go
index 4bd738d6a35..3fa221d7281 100644
--- a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go
+++ b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go
@@ -10,12 +10,16 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
 	ExtraCmdArgs: []string{},
 	Skip:         false,
 	GitVersion:   AtLeast("2.38.0"),
-	SetupConfig:  func(config *config.AppConfig) {},
+	SetupConfig: func(config *config.AppConfig) {
+		config.GetUserConfig().Git.MainBranches = []string{"master"}
+	},
 	SetupRepo: func(shell *Shell) {
 		shell.
-			CreateNCommits(3).
-			NewBranch("mybranch").
-			CreateNCommitsStartingAt(3, 4)
+			CreateNCommits(1).
+			NewBranch("branch1").
+			CreateNCommitsStartingAt(3, 2).
+			NewBranch("branch2").
+			CreateNCommitsStartingAt(3, 5)
 
 		shell.SetConfig("rebase.updateRefs", "true")
 	},
@@ -23,26 +27,28 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
 		t.Views().Commits().
 			Focus().
 			Lines(
-				Contains("commit 06").IsSelected(),
-				Contains("commit 05"),
-				Contains("commit 04"),
-				Contains("commit 03"),
-				Contains("commit 02"),
-				Contains("commit 01"),
+				Contains("CI commit 07").IsSelected(),
+				Contains("CI commit 06"),
+				Contains("CI commit 05"),
+				Contains("CI * commit 04"),
+				Contains("CI commit 03"),
+				Contains("CI commit 02"),
+				Contains("CI commit 01"),
 			).
-			NavigateToLine(Contains("commit 01")).
+			NavigateToLine(Contains("commit 02")).
 			Press(keys.Universal.Edit).
 			Focus().
 			Lines(
-				Contains("pick").Contains("commit 06"),
-				Contains("pick").Contains("commit 05"),
-				Contains("pick").Contains("commit 04"),
-				Contains("update-ref").Contains("master"),
-				Contains("pick").Contains("commit 03"),
-				Contains("pick").Contains("commit 02"),
-				Contains("<-- YOU ARE HERE --- commit 01"),
+				Contains("pick").Contains("CI commit 07"),
+				Contains("pick").Contains("CI commit 06"),
+				Contains("pick").Contains("CI commit 05"),
+				Contains("update-ref").Contains("branch1").DoesNotContain("*"),
+				Contains("pick").Contains("CI * commit 04"),
+				Contains("pick").Contains("CI commit 03"),
+				Contains("<-- YOU ARE HERE --- commit 02"),
+				Contains("CI commit 01"),
 			).
-			NavigateToLine(Contains("commit 05")).
+			NavigateToLine(Contains("commit 06")).
 			Press(keys.Universal.Remove)
 
 		t.Common().ContinueRebase()
@@ -50,11 +56,12 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
 		t.Views().Commits().
 			IsFocused().
 			Lines(
-				Contains("commit 06"),
-				Contains("commit 04"),
-				Contains("commit 03"),
-				Contains("commit 02"),
-				Contains("commit 01"),
+				Contains("CI commit 07"),
+				Contains("CI commit 05"),
+				Contains("CI * commit 04"),
+				Contains("CI commit 03"),
+				Contains("CI commit 02"),
+				Contains("CI commit 01"),
 			)
 	},
 })
diff --git a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref_show_branch_heads.go b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref_show_branch_heads.go
deleted file mode 100644
index b8cd4105514..00000000000
--- a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref_show_branch_heads.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package interactive_rebase
-
-import (
-	"github.com/jesseduffield/lazygit/pkg/config"
-	. "github.com/jesseduffield/lazygit/pkg/integration/components"
-)
-
-var DropTodoCommitWithUpdateRefShowBranchHeads = NewIntegrationTest(NewIntegrationTestArgs{
-	Description:  "Drops a commit during interactive rebase when there is an update-ref in the git-rebase-todo file (with experimentalShowBranchHeads on)",
-	ExtraCmdArgs: []string{},
-	Skip:         false,
-	GitVersion:   AtLeast("2.38.0"),
-	SetupConfig: func(config *config.AppConfig) {
-		config.UserConfig.Gui.ExperimentalShowBranchHeads = true
-	},
-	SetupRepo: func(shell *Shell) {
-		shell.
-			CreateNCommits(3).
-			NewBranch("mybranch").
-			CreateNCommitsStartingAt(3, 4)
-
-		shell.SetConfig("rebase.updateRefs", "true")
-	},
-	Run: func(t *TestDriver, keys config.KeybindingConfig) {
-		t.Views().Commits().
-			Focus().
-			Lines(
-				Contains("(*) commit 06").IsSelected(),
-				Contains("commit 05"),
-				Contains("commit 04"),
-				Contains("(*) commit 03"),
-				Contains("commit 02"),
-				Contains("commit 01"),
-			).
-			NavigateToLine(Contains("commit 01")).
-			Press(keys.Universal.Edit).
-			Focus().
-			Lines(
-				Contains("pick").Contains("(*) commit 06"),
-				Contains("pick").Contains("commit 05"),
-				Contains("pick").Contains("commit 04"),
-				Contains("update-ref").Contains("master"),
-				Contains("pick").Contains("(*) commit 03"),
-				Contains("pick").Contains("commit 02"),
-				Contains("<-- YOU ARE HERE --- commit 01"),
-			).
-			NavigateToLine(Contains("commit 05")).
-			Press(keys.Universal.Remove)
-
-		t.Common().ContinueRebase()
-
-		t.Views().Commits().
-			IsFocused().
-			Lines(
-				Contains("(*) commit 06"),
-				Contains("commit 04"),
-				Contains("(*) commit 03"),
-				Contains("commit 02"),
-				Contains("commit 01"),
-			)
-	},
-})
diff --git a/pkg/integration/tests/reflog/do_not_show_branch_markers_in_reflog_subcommits.go b/pkg/integration/tests/reflog/do_not_show_branch_markers_in_reflog_subcommits.go
new file mode 100644
index 00000000000..8e888c95aa2
--- /dev/null
+++ b/pkg/integration/tests/reflog/do_not_show_branch_markers_in_reflog_subcommits.go
@@ -0,0 +1,71 @@
+package reflog
+
+import (
+	"github.com/jesseduffield/lazygit/pkg/config"
+	. "github.com/jesseduffield/lazygit/pkg/integration/components"
+)
+
+var DoNotShowBranchMarkersInReflogSubcommits = NewIntegrationTest(NewIntegrationTestArgs{
+	Description:  "Verify that no branch heads are shown in the subcommits view of a reflog entry",
+	ExtraCmdArgs: []string{},
+	Skip:         false,
+	SetupConfig:  func(config *config.AppConfig) {},
+	SetupRepo: func(shell *Shell) {
+		shell.NewBranch("branch1")
+		shell.EmptyCommit("one")
+		shell.EmptyCommit("two")
+		shell.NewBranch("branch2")
+		shell.EmptyCommit("three")
+	},
+	Run: func(t *TestDriver, keys config.KeybindingConfig) {
+		// Check that the local commits view does show a branch marker for branch1
+		t.Views().Commits().
+			Lines(
+				Contains("CI three"),
+				Contains("CI * two"),
+				Contains("CI one"),
+			)
+
+		t.Views().Branches().
+			Focus().
+			// Check out branch1
+			NavigateToLine(Contains("branch1")).
+			PressPrimaryAction().
+			// Look at the subcommits of branch2
+			NavigateToLine(Contains("branch2")).
+			PressEnter().
+			// Check that we see a marker for branch1 here (but not for
+			// branch2), even though branch1 is checked out
+			Tap(func() {
+				t.Views().SubCommits().
+					IsFocused().
+					Lines(
+						Contains("CI three"),
+						Contains("CI * two"),
+						Contains("CI one"),
+					).
+					PressEscape()
+			}).
+			// Check out branch2 again
+			NavigateToLine(Contains("branch2")).
+			PressPrimaryAction()
+
+		t.Views().ReflogCommits().
+			Focus().
+			TopLines(
+				Contains("checkout: moving from branch1 to branch2").IsSelected(),
+			).
+			PressEnter().
+			// Check that the subcommits view for a reflog entry doesn't show
+			// any branch markers
+			Tap(func() {
+				t.Views().SubCommits().
+					IsFocused().
+					Lines(
+						Contains("CI three"),
+						Contains("CI two"),
+						Contains("CI one"),
+					)
+			})
+	},
+})
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index bdd2a2bfd0f..9dcb57deeb4 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -117,7 +117,6 @@ var tests = []*components.IntegrationTest{
 	interactive_rebase.AmendMerge,
 	interactive_rebase.AmendNonHeadCommitDuringRebase,
 	interactive_rebase.DropTodoCommitWithUpdateRef,
-	interactive_rebase.DropTodoCommitWithUpdateRefShowBranchHeads,
 	interactive_rebase.DropWithCustomCommentChar,
 	interactive_rebase.EditFirstCommit,
 	interactive_rebase.EditNonTodoCommitDuringRebase,
@@ -164,6 +163,7 @@ var tests = []*components.IntegrationTest{
 	patch_building.StartNewPatch,
 	reflog.Checkout,
 	reflog.CherryPick,
+	reflog.DoNotShowBranchMarkersInReflogSubcommits,
 	reflog.Patch,
 	reflog.Reset,
 	staging.DiffContextChange,