diff --git a/pkg/commands/branch.go b/pkg/commands/branch.go index d6c4d953b..ad37985fd 100644 --- a/pkg/commands/branch.go +++ b/pkg/commands/branch.go @@ -3,7 +3,9 @@ package commands // Branch : A git branch // duplicating this for now type Branch struct { - Name string + Name string + // the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf' + DisplayName string Recency string Pushables string Pullables string diff --git a/pkg/commands/branch_list_builder.go b/pkg/commands/branch_list_builder.go index 421ffecc7..a04691b47 100644 --- a/pkg/commands/branch_list_builder.go +++ b/pkg/commands/branch_list_builder.go @@ -34,14 +34,6 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *GitCommand) (*BranchLis }, nil } -func (b *BranchListBuilder) obtainCurrentBranchName() string { - branchName, err := b.GitCommand.CurrentBranchName() - if err != nil { - panic(err.Error()) - } - return branchName -} - func (b *BranchListBuilder) obtainBranches() []*Branch { cmdStr := `git for-each-ref --sort=-committerdate --format="%(HEAD)|%(refname:short)|%(upstream:short)|%(upstream:track)" refs/heads` output, err := b.GitCommand.OSCommand.RunCommandWithOutput(cmdStr) @@ -100,7 +92,6 @@ func (b *BranchListBuilder) obtainBranches() []*Branch { // Build the list of branches for the current repo func (b *BranchListBuilder) Build() []*Branch { - currentBranchName := b.obtainCurrentBranchName() branches := b.obtainBranches() reflogBranches := b.obtainReflogBranches() @@ -128,7 +119,6 @@ outer: for i, branch := range branches { if branch.Head { foundHead = true - branch.Name = currentBranchName branch.Recency = " *" branches = append(branches[0:i], branches[i+1:]...) branches = append([]*Branch{branch}, branches...) @@ -136,7 +126,11 @@ outer: } } if !foundHead { - branches = append([]*Branch{{Name: currentBranchName, Head: true, Recency: " *"}}, branches...) + currentBranchName, currentBranchDisplayName, err := b.GitCommand.CurrentBranchName() + if err != nil { + panic(err) + } + branches = append([]*Branch{{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"}}, branches...) } return branches diff --git a/pkg/commands/commit_list_builder.go b/pkg/commands/commit_list_builder.go index cab1b714d..834d478e0 100644 --- a/pkg/commands/commit_list_builder.go +++ b/pkg/commands/commit_list_builder.go @@ -279,7 +279,7 @@ func (c *CommitListBuilder) setCommitCherryPickStatuses(commits []*Commit) ([]*C } func (c *CommitListBuilder) getMergeBase() (string, error) { - currentBranch, err := c.GitCommand.CurrentBranchName() + currentBranch, _, err := c.GitCommand.CurrentBranchName() if err != nil { return "", err } diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 4efb4c2cd..4884519ea 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -331,19 +331,29 @@ func (c *GitCommand) NewBranch(name string, baseBranch string) error { return c.OSCommand.RunCommand("git checkout -b %s %s", name, baseBranch) } -// CurrentBranchName is a function. -func (c *GitCommand) CurrentBranchName() (string, error) { +// CurrentBranchName get the current branch name and displayname. +// the first returned string is the name and the second is the displayname +// e.g. name is 123asdf and displayname is '(HEAD detached at 123asdf)' +func (c *GitCommand) CurrentBranchName() (string, string, error) { branchName, err := c.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD") - if err != nil || branchName == "HEAD\n" { - output, err := c.OSCommand.RunCommandWithOutput("git branch --contains") - if err != nil { - return "", err - } - re := regexp.MustCompile(CurrentBranchNameRegex) - match := re.FindStringSubmatch(output) - branchName = match[1] + if err == nil && branchName != "HEAD\n" { + trimmedBranchName := strings.TrimSpace(branchName) + return trimmedBranchName, trimmedBranchName, nil } - return strings.TrimSpace(branchName), nil + output, err := c.OSCommand.RunCommandWithOutput("git branch --contains") + if err != nil { + return "", "", err + } + for _, line := range utils.SplitLines(output) { + re := regexp.MustCompile(CurrentBranchNameRegex) + match := re.FindStringSubmatch(line) + if len(match) > 0 { + branchName = match[1] + displayBranchName := match[0][2:] + return branchName, displayBranchName, nil + } + } + return "HEAD", "HEAD", nil } // DeleteBranch delete branch diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 9bee3613e..249d104e2 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1549,7 +1549,7 @@ func TestGitCommandCurrentBranchName(t *testing.T) { type scenario struct { testName string command func(string, ...string) *exec.Cmd - test func(string, error) + test func(string, string, error) } scenarios := []scenario{ @@ -1559,9 +1559,10 @@ func TestGitCommandCurrentBranchName(t *testing.T) { assert.Equal(t, "git", cmd) return exec.Command("echo", "master") }, - func(output string, err error) { + func(name string, displayname string, err error) { assert.NoError(t, err) - assert.EqualValues(t, "master", output) + assert.EqualValues(t, "master", name) + assert.EqualValues(t, "master", displayname) }, }, { @@ -1580,9 +1581,32 @@ func TestGitCommandCurrentBranchName(t *testing.T) { return nil }, - func(output string, err error) { + func(name string, displayname string, err error) { assert.NoError(t, err) - assert.EqualValues(t, "master", output) + assert.EqualValues(t, "master", name) + assert.EqualValues(t, "master", displayname) + }, + }, + { + "handles a detached head", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + + switch args[0] { + case "symbolic-ref": + assert.EqualValues(t, []string{"symbolic-ref", "--short", "HEAD"}, args) + return exec.Command("test") + case "branch": + assert.EqualValues(t, []string{"branch", "--contains"}, args) + return exec.Command("echo", "* (HEAD detached at 123abcd)") + } + + return nil + }, + func(name string, displayname string, err error) { + assert.NoError(t, err) + assert.EqualValues(t, "123abcd", name) + assert.EqualValues(t, "(HEAD detached at 123abcd)", displayname) }, }, { @@ -1591,9 +1615,10 @@ func TestGitCommandCurrentBranchName(t *testing.T) { assert.Equal(t, "git", cmd) return exec.Command("test") }, - func(output string, err error) { + func(name string, displayname string, err error) { assert.Error(t, err) - assert.EqualValues(t, "", output) + assert.EqualValues(t, "", name) + assert.EqualValues(t, "", displayname) }, }, } diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go index 4283ee1dd..b12ed8ca2 100644 --- a/pkg/gui/presentation/branches.go +++ b/pkg/gui/presentation/branches.go @@ -22,14 +22,18 @@ func GetBranchListDisplayStrings(branches []*commands.Branch, fullDescription bo // getBranchDisplayStrings returns the display string of branch func getBranchDisplayStrings(b *commands.Branch, fullDescription bool) []string { - displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name)) + displayName := b.Name + if b.DisplayName != "" { + displayName = b.DisplayName + } + coloredName := utils.ColoredString(displayName, GetBranchColor(b.Name)) if b.Pushables != "" && b.Pullables != "" && b.Pushables != "?" && b.Pullables != "?" { trackColor := color.FgYellow if b.Pushables == "0" && b.Pullables == "0" { trackColor = color.FgGreen } track := utils.ColoredString(fmt.Sprintf("ā†‘%sā†“%s", b.Pushables, b.Pullables), trackColor) - displayName = fmt.Sprintf("%s %s", displayName, track) + coloredName = fmt.Sprintf("%s %s", coloredName, track) } recencyColor := color.FgCyan @@ -38,10 +42,10 @@ func getBranchDisplayStrings(b *commands.Branch, fullDescription bool) []string } if fullDescription { - return []string{utils.ColoredString(b.Recency, recencyColor), displayName, utils.ColoredString(b.UpstreamName, color.FgYellow)} + return []string{utils.ColoredString(b.Recency, recencyColor), coloredName, utils.ColoredString(b.UpstreamName, color.FgYellow)} } - return []string{utils.ColoredString(b.Recency, recencyColor), displayName} + return []string{utils.ColoredString(b.Recency, recencyColor), coloredName} } // GetBranchColor branch color