diff --git a/pkg/commands/git_commands/branch.go b/pkg/commands/git_commands/branch.go index caa876f3f..24244080b 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/status.go b/pkg/commands/git_commands/status.go index 13ff02cc0..784ddb424 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/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 182e20d85..b6da18374 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) } diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 8be1c8697..6480f1fcc 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