feat(git): better logic

This commit is contained in:
Jan De Dobbeleer 2021-12-05 18:51:10 +01:00 committed by Jan De Dobbeleer
parent 4f921dbb0f
commit 98472abafc
4 changed files with 497 additions and 559 deletions

View file

@ -87,13 +87,13 @@ func (g *git) deprecatedString(statusColorsEnabled bool) string {
if len(g.BranchStatus) > 0 {
buffer.WriteString(g.BranchStatus)
}
if g.Staging.Changed {
if g.Staging.Changed() {
fmt.Fprint(buffer, g.getStatusDetailString(g.Staging, StagingColor, LocalStagingIcon, " \uF046"))
}
if g.Staging.Changed && g.Working.Changed {
if g.Staging.Changed() && g.Working.Changed() {
fmt.Fprint(buffer, g.props.getString(StatusSeparatorIcon, " |"))
}
if g.Working.Changed {
if g.Working.Changed() {
fmt.Fprint(buffer, g.getStatusDetailString(g.Working, WorkingColor, LocalWorkingIcon, " \uF044"))
}
if g.StashCount != 0 {
@ -114,7 +114,7 @@ func (g *git) SetStatusColor() {
}
func (g *git) getStatusColor(defaultValue string) string {
if g.Staging.Changed || g.Working.Changed {
if g.Staging.Changed() || g.Working.Changed() {
return g.props.getColor(LocalChangesColor, defaultValue)
} else if g.Ahead > 0 && g.Behind > 0 {
return g.props.getColor(AheadAndBehindColor, defaultValue)

View file

@ -13,8 +13,7 @@ import (
func TestGetStatusDetailStringDefault(t *testing.T) {
expected := "icon +1"
status := &GitStatus{
Changed: true,
Added: 1,
Added: 1,
}
g := &git{}
assert.Equal(t, expected, g.getStatusDetailString(status, WorkingColor, LocalWorkingIcon, "icon"))
@ -23,8 +22,7 @@ func TestGetStatusDetailStringDefault(t *testing.T) {
func TestGetStatusDetailStringDefaultColorOverride(t *testing.T) {
expected := "<#123456>icon +1</>"
status := &GitStatus{
Changed: true,
Added: 1,
Added: 1,
}
var props properties = map[Property]interface{}{
WorkingColor: "#123456",
@ -38,8 +36,7 @@ func TestGetStatusDetailStringDefaultColorOverride(t *testing.T) {
func TestGetStatusDetailStringDefaultColorOverrideAndIconColorOverride(t *testing.T) {
expected := "<#789123>work</> <#123456>+1</>"
status := &GitStatus{
Changed: true,
Added: 1,
Added: 1,
}
var props properties = map[Property]interface{}{
WorkingColor: "#123456",
@ -54,8 +51,7 @@ func TestGetStatusDetailStringDefaultColorOverrideAndIconColorOverride(t *testin
func TestGetStatusDetailStringDefaultColorOverrideNoIconColorOverride(t *testing.T) {
expected := "<#123456>work +1</>"
status := &GitStatus{
Changed: true,
Added: 1,
Added: 1,
}
var props properties = map[Property]interface{}{
WorkingColor: "#123456",
@ -70,8 +66,7 @@ func TestGetStatusDetailStringDefaultColorOverrideNoIconColorOverride(t *testing
func TestGetStatusDetailStringNoStatus(t *testing.T) {
expected := "icon"
status := &GitStatus{
Changed: true,
Added: 1,
Added: 1,
}
var props properties = map[Property]interface{}{
DisplayStatusDetail: false,
@ -85,8 +80,7 @@ func TestGetStatusDetailStringNoStatus(t *testing.T) {
func TestGetStatusDetailStringNoStatusColorOverride(t *testing.T) {
expected := "<#123456>icon</>"
status := &GitStatus{
Changed: true,
Added: 1,
Added: 1,
}
var props properties = map[Property]interface{}{
DisplayStatusDetail: false,
@ -106,8 +100,9 @@ func TestGetStatusColorLocalChangesStaging(t *testing.T) {
g := &git{
props: props,
Staging: &GitStatus{
Changed: true,
Modified: 1,
},
Working: &GitStatus{},
}
assert.Equal(t, expected, g.getStatusColor("#fg1111"))
}
@ -121,7 +116,7 @@ func TestGetStatusColorLocalChangesWorking(t *testing.T) {
props: props,
Staging: &GitStatus{},
Working: &GitStatus{
Changed: true,
Modified: 1,
},
}
assert.Equal(t, expected, g.getStatusColor("#fg1111"))
@ -196,8 +191,9 @@ func TestSetStatusColorForeground(t *testing.T) {
g := &git{
props: props,
Staging: &GitStatus{
Changed: true,
Added: 1,
},
Working: &GitStatus{},
}
g.SetStatusColor()
assert.Equal(t, expected, g.props[ForegroundOverride])
@ -210,9 +206,10 @@ func TestSetStatusColorBackground(t *testing.T) {
ColorBackground: true,
}
g := &git{
props: props,
Staging: &GitStatus{
Changed: true,
props: props,
Staging: &GitStatus{},
Working: &GitStatus{
Modified: 1,
},
}
g.SetStatusColor()
@ -221,16 +218,30 @@ func TestSetStatusColorBackground(t *testing.T) {
func TestStatusColorsWithoutDisplayStatus(t *testing.T) {
expected := changesColor
context := &detachedContext{
status: "## main...origin/main [ahead 33]\n M myfile",
status := "## main...origin/main [ahead 33]\n M myfile"
env := new(MockedEnvironment)
env.On("isWsl", nil).Return(false)
env.On("getRuntimeGOOS", nil).Return("unix")
env.On("hasFolder", "/rebase-merge").Return(false)
env.On("hasFolder", "/rebase-apply").Return(false)
env.On("hasFolder", "/sequencer").Return(false)
env.On("getFileContent", "/HEAD").Return(status)
env.On("hasFilesInDir", "", "CHERRY_PICK_HEAD").Return(false)
env.On("hasFilesInDir", "", "REVERT_HEAD").Return(false)
env.On("hasFilesInDir", "", "MERGE_MSG").Return(false)
env.On("hasFilesInDir", "", "MERGE_HEAD").Return(false)
env.On("hasFilesInDir", "", "sequencer/todo").Return(false)
env.mockGitCommand("", "describe", "--tags", "--exact-match")
env.mockGitCommand(status, "status", "-unormal", "--branch", "--porcelain=2")
g := &git{
env: env,
gitWorkingFolder: "",
props: map[Property]interface{}{
DisplayStatus: false,
StatusColorsEnabled: true,
LocalChangesColor: expected,
},
}
g := setupHEADContextEnv(context)
var props properties = map[Property]interface{}{
DisplayStatus: false,
StatusColorsEnabled: true,
LocalChangesColor: expected,
}
g.props = props
g.string()
assert.Equal(t, expected, g.props[BackgroundOverride])
}

View file

@ -14,37 +14,25 @@ type GitStatus struct {
Deleted int
Added int
Modified int
Changed bool
}
func (s *GitStatus) parse(output []string, working bool) {
if len(output) <= 1 {
func (s *GitStatus) add(code string) {
switch code {
case ".":
return
case "D":
s.Deleted++
case "A", "?":
s.Added++
case "U":
s.Unmerged++
case "M", "R", "C", "m":
s.Modified++
}
for _, line := range output[1:] {
if len(line) < 2 {
continue
}
code := line[0:1]
if working {
code = line[1:2]
}
switch code {
case "?":
if working {
s.Added++
}
case "D":
s.Deleted++
case "A":
s.Added++
case "U":
s.Unmerged++
case "M", "R", "C", "m":
s.Modified++
}
}
s.Changed = s.Added > 0 || s.Deleted > 0 || s.Modified > 0 || s.Unmerged > 0
}
func (s *GitStatus) Changed() bool {
return s.Added > 0 || s.Deleted > 0 || s.Modified > 0 || s.Unmerged > 0
}
func (s *GitStatus) String() string {
@ -71,6 +59,8 @@ type git struct {
Ahead int
Behind int
HEAD string
Ref string
Hash string
BranchStatus string
Upstream string
UpstreamIcon string
@ -133,6 +123,9 @@ const (
GitlabIcon Property = "gitlab_icon"
// GitIcon shows when the upstream can't be identified
GitIcon Property = "git_icon"
DETACHED = "(detached)"
BRANCHPREFIX = "ref: refs/heads/"
)
func (g *git) enabled() bool {
@ -147,12 +140,10 @@ func (g *git) enabled() bool {
return false
}
g.Staging = &GitStatus{}
g.Working = &GitStatus{}
if gitdir.isDir {
g.gitWorkingFolder = gitdir.path
g.gitRootFolder = gitdir.path
g.gitWorktreeFolder = strings.TrimSuffix(gitdir.path, ".git")
return true
}
// handle worktree
@ -186,10 +177,12 @@ func (g *git) string() string {
statusColorsEnabled := g.props.getBool(StatusColorsEnabled, false)
displayStatus := g.props.getOneOfBool(FetchStatus, DisplayStatus, false)
if !displayStatus {
g.HEAD = g.getPrettyHEADName()
g.setPrettyHEADName()
}
if displayStatus || statusColorsEnabled {
g.setGitStatus()
g.setGitHEADContext()
g.BranchStatus = g.getBranchStatus()
}
if g.Upstream != "" && g.props.getOneOfBool(FetchUpstreamIcon, DisplayUpstreamIcon, false) {
g.UpstreamIcon = g.getUpstreamIcon()
@ -266,20 +259,47 @@ func (g *git) getUpstreamIcon() string {
}
func (g *git) setGitStatus() {
output := g.getGitCommandOutput("status", "-unormal", "--short", "--branch")
splittedOutput := strings.Split(output, "\n")
g.Working.parse(splittedOutput, true)
g.Staging.parse(splittedOutput, false)
status := g.parseGitStatusInfo(splittedOutput[0])
if status["local"] != "" {
g.Ahead, _ = strconv.Atoi(status["ahead"])
g.Behind, _ = strconv.Atoi(status["behind"])
if status["upstream_status"] != "gone" {
g.Upstream = status["upstream"]
addToStatus := func(status string) {
if len(status) <= 4 {
return
}
workingCode := status[3:4]
stagingCode := status[2:3]
g.Working.add(workingCode)
g.Staging.add(stagingCode)
}
const (
HASH = "# branch.oid "
REF = "# branch.head "
UPSTREAM = "# branch.upstream "
BRANCHSTATUS = "# branch.ab "
)
g.Staging = &GitStatus{}
g.Working = &GitStatus{}
output := g.getGitCommandOutput("status", "-unormal", "--branch", "--porcelain=2")
for _, line := range strings.Split(output, "\n") {
if strings.HasPrefix(line, HASH) {
g.Hash = line[len(HASH) : len(HASH)+7]
continue
}
if strings.HasPrefix(line, REF) {
g.Ref = line[len(REF):]
continue
}
if strings.HasPrefix(line, UPSTREAM) {
g.Upstream = line[len(UPSTREAM):]
continue
}
if strings.HasPrefix(line, BRANCHSTATUS) {
status := line[len(BRANCHSTATUS):]
splitted := strings.Split(status, " ")
g.Ahead, _ = strconv.Atoi(splitted[0])
behind, _ := strconv.Atoi(splitted[1])
g.Behind = -behind
continue
}
addToStatus(line)
}
g.HEAD = g.getGitHEADContext(status["local"])
g.BranchStatus = g.getBranchStatus()
}
func (g *git) getGitCommand() string {
@ -309,53 +329,74 @@ func (g *git) getGitCommandOutput(args ...string) string {
return val
}
func (g *git) getGitHEADContext(ref string) string {
func (g *git) setGitHEADContext() {
branchIcon := g.props.getString(BranchIcon, "\uE0A0")
if ref == "" {
ref = g.getPrettyHEADName()
if g.Ref == DETACHED {
g.setPrettyHEADName()
} else {
ref = g.truncateBranch(ref)
ref = fmt.Sprintf("%s%s", branchIcon, ref)
head := g.formatHEAD(g.Ref)
g.HEAD = fmt.Sprintf("%s%s", branchIcon, head)
}
// rebase
formatDetached := func() string {
if g.Ref == DETACHED {
return fmt.Sprintf("%sdetached at %s", branchIcon, g.HEAD)
}
return g.HEAD
}
getPrettyNameOrigin := func(file string) string {
var origin string
head := g.getGitFileContents(g.gitWorkingFolder, file)
if head == "detached HEAD" {
origin = formatDetached()
} else {
head = strings.Replace(head, "refs/heads/", "", 1)
origin = branchIcon + g.formatHEAD(head)
}
return origin
}
if g.env.hasFolder(g.gitWorkingFolder + "/rebase-merge") {
head := g.getGitFileContents(g.gitWorkingFolder, "rebase-merge/head-name")
origin := strings.Replace(head, "refs/heads/", "", 1)
origin = g.truncateBranch(origin)
origin := getPrettyNameOrigin("rebase-merge/head-name")
onto := g.getGitRefFileSymbolicName("rebase-merge/onto")
onto = g.truncateBranch(onto)
onto = g.formatHEAD(onto)
step := g.getGitFileContents(g.gitWorkingFolder, "rebase-merge/msgnum")
total := g.getGitFileContents(g.gitWorkingFolder, "rebase-merge/end")
icon := g.props.getString(RebaseIcon, "\uE728 ")
return fmt.Sprintf("%s%s%s onto %s%s (%s/%s) at %s", icon, branchIcon, origin, branchIcon, onto, step, total, ref)
g.HEAD = fmt.Sprintf("%s%s onto %s%s (%s/%s) at %s", icon, origin, branchIcon, onto, step, total, g.HEAD)
return
}
if g.env.hasFolder(g.gitWorkingFolder + "/rebase-apply") {
head := g.getGitFileContents(g.gitWorkingFolder, "rebase-apply/head-name")
origin := strings.Replace(head, "refs/heads/", "", 1)
origin = g.truncateBranch(origin)
origin := getPrettyNameOrigin("rebase-apply/head-name")
step := g.getGitFileContents(g.gitWorkingFolder, "rebase-apply/next")
total := g.getGitFileContents(g.gitWorkingFolder, "rebase-apply/last")
icon := g.props.getString(RebaseIcon, "\uE728 ")
return fmt.Sprintf("%s%s%s (%s/%s) at %s", icon, branchIcon, origin, step, total, ref)
g.HEAD = fmt.Sprintf("%s%s (%s/%s) at %s", icon, origin, step, total, g.HEAD)
return
}
// merge
if g.hasGitFile("MERGE_MSG") && g.hasGitFile("MERGE_HEAD") {
commitIcon := g.props.getString(CommitIcon, "\uF417")
if g.hasGitFile("MERGE_MSG") {
icon := g.props.getString(MergeIcon, "\uE727 ")
mergeContext := g.getGitFileContents(g.gitWorkingFolder, "MERGE_MSG")
matches := findNamedRegexMatch(`Merge (?P<type>(remote-tracking )?branch|commit|tag) '(?P<head>.*)' into`, mergeContext)
if matches != nil && matches["head"] != "" {
var headIcon string
matches := findNamedRegexMatch(`Merge (remote-tracking )?(?P<type>branch|commit|tag) '(?P<theirs>.*)'`, mergeContext)
// head := g.getGitRefFileSymbolicName("ORIG_HEAD")
if matches != nil && matches["theirs"] != "" {
var headIcon, theirs string
switch matches["type"] {
case "tag":
headIcon = g.props.getString(TagIcon, "\uF412")
theirs = matches["theirs"]
case "commit":
headIcon = g.props.getString(CommitIcon, "\uF417")
headIcon = commitIcon
theirs = g.formatSHA(matches["theirs"])
default:
headIcon = branchIcon
theirs = g.formatHEAD(matches["theirs"])
}
head := g.truncateBranch(matches["head"])
return fmt.Sprintf("%s%s%s into %s", icon, headIcon, head, ref)
g.HEAD = fmt.Sprintf("%s%s%s into %s", icon, headIcon, theirs, formatDetached())
return
}
}
// sequencer status
@ -365,13 +406,17 @@ func (g *git) getGitHEADContext(ref string) string {
// the todo file.
if g.hasGitFile("CHERRY_PICK_HEAD") {
sha := g.getGitFileContents(g.gitWorkingFolder, "CHERRY_PICK_HEAD")
icon := g.props.getString(CherryPickIcon, "\uE29B ")
return fmt.Sprintf("%s%s onto %s", icon, sha[0:6], ref)
} else if g.hasGitFile("REVERT_HEAD") {
cherry := g.props.getString(CherryPickIcon, "\uE29B ")
g.HEAD = fmt.Sprintf("%s%s%s onto %s", cherry, commitIcon, g.formatSHA(sha), formatDetached())
return
}
if g.hasGitFile("REVERT_HEAD") {
sha := g.getGitFileContents(g.gitWorkingFolder, "REVERT_HEAD")
icon := g.props.getString(RevertIcon, "\uF0E2 ")
return fmt.Sprintf("%s%s onto %s", icon, sha[0:6], ref)
} else if g.hasGitFile("sequencer/todo") {
revert := g.props.getString(RevertIcon, "\uF0E2 ")
g.HEAD = fmt.Sprintf("%s%s%s onto %s", revert, commitIcon, g.formatSHA(sha), formatDetached())
return
}
if g.hasGitFile("sequencer/todo") {
todo := g.getGitFileContents(g.gitWorkingFolder, "sequencer/todo")
matches := findNamedRegexMatch(`^(?P<action>p|pick|revert)\s+(?P<sha>\S+)`, todo)
if matches != nil && matches["sha"] != "" {
@ -379,24 +424,33 @@ func (g *git) getGitHEADContext(ref string) string {
sha := matches["sha"]
switch action {
case "p", "pick":
icon := g.props.getString(CherryPickIcon, "\uE29B ")
return fmt.Sprintf("%s%s onto %s", icon, sha[0:6], ref)
cherry := g.props.getString(CherryPickIcon, "\uE29B ")
g.HEAD = fmt.Sprintf("%s%s%s onto %s", cherry, commitIcon, g.formatSHA(sha), formatDetached())
return
case "revert":
icon := g.props.getString(RevertIcon, "\uF0E2 ")
return fmt.Sprintf("%s%s onto %s", icon, sha[0:6], ref)
revert := g.props.getString(RevertIcon, "\uF0E2 ")
g.HEAD = fmt.Sprintf("%s%s%s onto %s", revert, commitIcon, g.formatSHA(sha), formatDetached())
return
}
}
}
return ref
g.HEAD = formatDetached()
}
func (g *git) truncateBranch(branch string) string {
func (g *git) formatHEAD(head string) string {
maxLength := g.props.getInt(BranchMaxLength, 0)
if maxLength == 0 || len(branch) < maxLength {
return branch
if maxLength == 0 || len(head) < maxLength {
return head
}
symbol := g.props.getString(TruncateSymbol, "")
return branch[0:maxLength] + symbol
return head[0:maxLength] + symbol
}
func (g *git) formatSHA(sha string) string {
if len(sha) <= 7 {
return sha
}
return sha[0:7]
}
func (g *git) hasGitFile(file string) bool {
@ -412,28 +466,32 @@ func (g *git) getGitRefFileSymbolicName(refFile string) string {
return g.getGitCommandOutput("name-rev", "--name-only", "--exclude=tags/*", ref)
}
func (g *git) getPrettyHEADName() string {
var ref string
HEAD := g.getGitFileContents(g.gitWorkingFolder, "HEAD")
branchPrefix := "ref: refs/heads/"
if strings.HasPrefix(HEAD, branchPrefix) {
ref = strings.TrimPrefix(HEAD, branchPrefix)
}
if ref != "" {
ref = g.truncateBranch(ref)
return fmt.Sprintf("%s%s", g.props.getString(BranchIcon, "\uE0A0"), ref)
func (g *git) setPrettyHEADName() {
// we didn't fetch status, fallback to parsing the HEAD file
if len(g.Hash) == 0 {
HEADRef := g.getGitFileContents(g.gitWorkingFolder, "HEAD")
if strings.HasPrefix(HEADRef, BRANCHPREFIX) {
branchName := strings.TrimPrefix(HEADRef, BRANCHPREFIX)
g.HEAD = fmt.Sprintf("%s%s", g.props.getString(BranchIcon, "\uE0A0"), g.formatHEAD(branchName))
return
}
// no branch, points to commit
if len(HEADRef) >= 7 {
g.Hash = HEADRef[0:7]
}
}
// check for tag
ref = g.getGitCommandOutput("describe", "--tags", "--exact-match")
if ref != "" {
return fmt.Sprintf("%s%s", g.props.getString(TagIcon, "\uF412"), ref)
tagName := g.getGitCommandOutput("describe", "--tags", "--exact-match")
if len(tagName) > 0 {
g.HEAD = fmt.Sprintf("%s%s", g.props.getString(TagIcon, "\uF412"), tagName)
return
}
// fallback to commit
ref = g.getGitCommandOutput("rev-parse", "--short", "HEAD")
if ref == "" {
return g.props.getString(NoCommitsIcon, "\uF594 ")
if len(g.Hash) == 0 {
g.HEAD = g.props.getString(NoCommitsIcon, "\uF594 ")
return
}
return fmt.Sprintf("%s%s", g.props.getString(CommitIcon, "\uF417"), ref)
g.HEAD = fmt.Sprintf("%s%s", g.props.getString(CommitIcon, "\uF417"), g.Hash)
}
func (g *git) getStashContext() int {
@ -453,11 +511,6 @@ func (g *git) getWorktreeContext() int {
return len(worktreeFolders)
}
func (g *git) parseGitStatusInfo(branchInfo string) map[string]string {
var branchRegex = `^## (?P<local>\S+?)(\.{3}(?P<upstream>\S+?)( \[(?P<upstream_status>(ahead (?P<ahead>\d+)(, )?)?(behind (?P<behind>\d+))?(gone)?)])?)?$`
return findNamedRegexMatch(branchRegex, branchInfo)
}
func (g *git) getOriginURL(upstream string) string {
cfg, err := ini.Load(g.gitRootFolder + "/config")
if err != nil {

View file

@ -2,6 +2,7 @@ package main
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@ -9,6 +10,7 @@ import (
const (
changesColor = "#BD8BDE"
branchName = "main"
)
func TestEnabledGitNotFound(t *testing.T) {
@ -76,299 +78,301 @@ func TestGetGitOutputForCommand(t *testing.T) {
assert.Equal(t, want, got)
}
type detachedContext struct {
currentCommit string
rebase string
rebaseMerge bool
rebaseApply bool
origin string
onto string
step string
total string
branchName string
tagName string
cherryPick bool
cherryPickSHA string
revert bool
revertSHA string
sequencer bool
sequencerTodo string
merge bool
mergeHEAD string
mergeMsgStart string
status string
}
func setupHEADContextEnv(context *detachedContext) *git {
env := new(MockedEnvironment)
env.On("isWsl", nil).Return(false)
env.On("hasFolder", "/rebase-merge").Return(context.rebaseMerge)
env.On("hasFolder", "/rebase-apply").Return(context.rebaseApply)
env.On("hasFolder", "/sequencer").Return(context.sequencer)
env.On("getFileContent", "/rebase-merge/head-name").Return(context.origin)
env.On("getFileContent", "/rebase-merge/onto").Return(context.onto)
env.On("getFileContent", "/rebase-merge/msgnum").Return(context.step)
env.On("getFileContent", "/rebase-apply/next").Return(context.step)
env.On("getFileContent", "/rebase-merge/end").Return(context.total)
env.On("getFileContent", "/rebase-apply/last").Return(context.total)
env.On("getFileContent", "/rebase-apply/head-name").Return(context.origin)
env.On("getFileContent", "/CHERRY_PICK_HEAD").Return(context.cherryPickSHA)
env.On("getFileContent", "/REVERT_HEAD").Return(context.revertSHA)
env.On("getFileContent", "/MERGE_MSG").Return(fmt.Sprintf("%s '%s' into %s", context.mergeMsgStart, context.mergeHEAD, context.onto))
env.On("getFileContent", "/sequencer/todo").Return(context.sequencerTodo)
env.On("getFileContent", "/HEAD").Return(context.branchName)
env.On("hasFilesInDir", "", "CHERRY_PICK_HEAD").Return(context.cherryPick)
env.On("hasFilesInDir", "", "REVERT_HEAD").Return(context.revert)
env.On("hasFilesInDir", "", "MERGE_MSG").Return(context.merge)
env.On("hasFilesInDir", "", "MERGE_HEAD").Return(context.merge)
env.On("hasFilesInDir", "", "sequencer/todo").Return(context.sequencer)
env.mockGitCommand(context.currentCommit, "rev-parse", "--short", "HEAD")
env.mockGitCommand(context.tagName, "describe", "--tags", "--exact-match")
env.mockGitCommand(context.origin, "name-rev", "--name-only", "--exclude=tags/*", context.origin)
env.mockGitCommand(context.onto, "name-rev", "--name-only", "--exclude=tags/*", context.onto)
env.mockGitCommand(context.branchName, "branch", "--show-current")
env.mockGitCommand(context.status, "status", "-unormal", "--short", "--branch")
env.On("getRuntimeGOOS", nil).Return("unix")
g := &git{
env: env,
gitWorkingFolder: "",
Working: &GitStatus{},
Staging: &GitStatus{},
}
return g
}
func (m *MockedEnvironment) mockGitCommand(returnValue string, args ...string) {
args = append([]string{"-C", "", "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...)
m.On("runCommand", "git", args).Return(returnValue, nil)
}
func TestGetGitDetachedCommitHash(t *testing.T) {
want := "\uf417lalasha1"
context := &detachedContext{
currentCommit: "lalasha1",
func TestSetGitHEADContextClean(t *testing.T) {
cases := []struct {
Case string
Expected string
Ref string
RebaseMerge bool
RebaseApply bool
Merge bool
CherryPick bool
Revert bool
Sequencer bool
Ours string
Theirs string
Step string
Total string
}{
{Case: "detached on commit", Ref: DETACHED, Expected: "branch detached at commit 1234567"},
{Case: "not detached, clean", Ref: "main", Expected: "branch main"},
{
Case: "rebase merge",
Ref: DETACHED,
Expected: "rebase branch origin/main onto branch main (1/2) at commit 1234567",
RebaseMerge: true,
Ours: "refs/heads/origin/main",
Theirs: "main",
Step: "1",
Total: "2",
},
{
Case: "rebase apply",
Ref: DETACHED,
Expected: "rebase branch origin/main (1/2) at commit 1234567",
RebaseApply: true,
Ours: "refs/heads/origin/main",
Step: "1",
Total: "2",
},
{
Case: "merge branch",
Ref: "main",
Expected: "merge branch feat-1 into branch main",
Merge: true,
Theirs: "branch 'feat-1'",
Ours: "main",
},
{
Case: "merge commit",
Ref: "main",
Expected: "merge commit 1234567 into branch main",
Merge: true,
Theirs: "commit '123456789101112'",
Ours: "main",
},
{
Case: "merge tag",
Ref: "main",
Expected: "merge tag 1.2.4 into branch main",
Merge: true,
Theirs: "tag '1.2.4'",
Ours: "main",
},
{
Case: "cherry pick",
Ref: "main",
Expected: "pick commit 1234567 onto branch main",
CherryPick: true,
Theirs: "123456789101012",
Ours: "main",
},
{
Case: "revert",
Ref: "main",
Expected: "revert commit 1234567 onto branch main",
Revert: true,
Theirs: "123456789101012",
Ours: "main",
},
{
Case: "sequencer cherry",
Ref: "main",
Expected: "pick commit 1234567 onto branch main",
Sequencer: true,
Theirs: "pick 123456789101012",
Ours: "main",
},
{
Case: "sequencer cherry p",
Ref: "main",
Expected: "pick commit 1234567 onto branch main",
Sequencer: true,
Theirs: "p 123456789101012",
Ours: "main",
},
{
Case: "sequencer revert",
Ref: "main",
Expected: "revert commit 1234567 onto branch main",
Sequencer: true,
Theirs: "revert 123456789101012",
Ours: "main",
},
}
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getRuntimeGOOS", nil).Return("unix")
env.On("isWsl", nil).Return(false)
env.mockGitCommand("", "describe", "--tags", "--exact-match")
env.mockGitCommand(tc.Theirs, "name-rev", "--name-only", "--exclude=tags/*", tc.Theirs)
env.mockGitCommand(tc.Ours, "name-rev", "--name-only", "--exclude=tags/*", tc.Ours)
// rebase merge
env.On("hasFolder", "/rebase-merge").Return(tc.RebaseMerge)
env.On("getFileContent", "/rebase-merge/head-name").Return(tc.Ours)
env.On("getFileContent", "/rebase-merge/onto").Return(tc.Theirs)
env.On("getFileContent", "/rebase-merge/msgnum").Return(tc.Step)
env.On("getFileContent", "/rebase-merge/end").Return(tc.Total)
// rebase apply
env.On("hasFolder", "/rebase-apply").Return(tc.RebaseApply)
env.On("getFileContent", "/rebase-apply/head-name").Return(tc.Ours)
env.On("getFileContent", "/rebase-apply/next").Return(tc.Step)
env.On("getFileContent", "/rebase-apply/last").Return(tc.Total)
// merge
env.On("hasFilesInDir", "", "MERGE_MSG").Return(tc.Merge)
env.On("getFileContent", "/MERGE_MSG").Return(fmt.Sprintf("Merge %s into %s", tc.Theirs, tc.Ours))
// cherry pick
env.On("hasFilesInDir", "", "CHERRY_PICK_HEAD").Return(tc.CherryPick)
env.On("getFileContent", "/CHERRY_PICK_HEAD").Return(tc.Theirs)
// revert
env.On("hasFilesInDir", "", "REVERT_HEAD").Return(tc.Revert)
env.On("getFileContent", "/REVERT_HEAD").Return(tc.Theirs)
// sequencer
env.On("hasFilesInDir", "", "sequencer/todo").Return(tc.Sequencer)
env.On("getFileContent", "/sequencer/todo").Return(tc.Theirs)
g := &git{
env: env,
props: map[Property]interface{}{
BranchIcon: "branch ",
CommitIcon: "commit ",
RebaseIcon: "rebase ",
MergeIcon: "merge ",
CherryPickIcon: "pick ",
TagIcon: "tag ",
RevertIcon: "revert ",
},
Hash: "1234567",
Ref: tc.Ref,
}
g.setGitHEADContext()
assert.Equal(t, tc.Expected, g.HEAD, tc.Case)
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextTagName(t *testing.T) {
want := "\uf412lalasha1"
context := &detachedContext{
currentCommit: "whatever",
tagName: "lalasha1",
func TestSetPrettyHEADName(t *testing.T) {
cases := []struct {
Case string
Expected string
Hash string
Tag string
HEAD string
}{
{Case: "main", Expected: "branch main", HEAD: BRANCHPREFIX + "main"},
{Case: "no hash", Expected: "commit 1234567", HEAD: "12345678910"},
{Case: "hash on tag", Hash: "132312322321", Expected: "tag tag-1", HEAD: "12345678910", Tag: "tag-1"},
{Case: "no hash on tag", Expected: "tag tag-1", Tag: "tag-1"},
{Case: "hash on commit", Hash: "1234567", Expected: "commit 1234567"},
{Case: "no hash on commit", Expected: "commit 1234567", HEAD: "12345678910"},
}
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getFileContent", "/HEAD").Return(tc.HEAD)
env.On("getRuntimeGOOS", nil).Return("unix")
env.On("isWsl", nil).Return(false)
env.mockGitCommand(tc.Tag, "describe", "--tags", "--exact-match")
g := &git{
env: env,
props: map[Property]interface{}{
BranchIcon: "branch ",
CommitIcon: "commit ",
TagIcon: "tag ",
},
Hash: tc.Hash,
}
g.setPrettyHEADName()
assert.Equal(t, tc.Expected, g.HEAD, tc.Case)
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextRebaseMerge(t *testing.T) {
want := "\ue728 \ue0a0cool-feature-bro onto \ue0a0main (2/3) at \uf417whatever"
context := &detachedContext{
currentCommit: "whatever",
rebase: "true",
rebaseMerge: true,
origin: "cool-feature-bro",
onto: "main",
step: "2",
total: "3",
func TestSetGitStatus(t *testing.T) {
cases := []struct {
Case string
Output string
ExpectedWorking *GitStatus
ExpectedStaging *GitStatus
ExpectedHash string
ExpectedRef string
ExpectedUpstream string
ExpectedAhead int
ExpectedBehind int
}{
{
Case: "all different options on working and staging, no remote",
Output: `
# branch.oid 1234567891011121314
# branch.head rework-git-status
1 .R N...
1 .C N...
1 .M N...
1 .m N...
1 .? N...
1 .D N...
1 .A N...
1 .U N...
1 A. N...
`,
ExpectedWorking: &GitStatus{Modified: 4, Added: 2, Deleted: 1, Unmerged: 1},
ExpectedStaging: &GitStatus{Added: 1},
ExpectedHash: "1234567",
ExpectedRef: "rework-git-status",
},
{
Case: "all different options on working and staging, with remote",
Output: `
# branch.oid 1234567891011121314
# branch.head rework-git-status
# branch.upstream origin/rework-git-status
1 .R N...
1 .C N...
1 .M N...
1 .m N...
1 .? N...
1 .D N...
1 .A N...
1 .U N...
1 A. N...
`,
ExpectedWorking: &GitStatus{Modified: 4, Added: 2, Deleted: 1, Unmerged: 1},
ExpectedStaging: &GitStatus{Added: 1},
ExpectedUpstream: "origin/rework-git-status",
ExpectedHash: "1234567",
ExpectedRef: "rework-git-status",
},
{
Case: "remote with equal branch",
Output: `
# branch.oid 1234567891011121314
# branch.head rework-git-status
# branch.upstream origin/rework-git-status
`,
ExpectedUpstream: "origin/rework-git-status",
ExpectedHash: "1234567",
ExpectedRef: "rework-git-status",
},
{
Case: "remote with branch status",
Output: `
# branch.oid 1234567891011121314
# branch.head rework-git-status
# branch.upstream origin/rework-git-status
# branch.ab +2 -1
`,
ExpectedUpstream: "origin/rework-git-status",
ExpectedHash: "1234567",
ExpectedRef: "rework-git-status",
ExpectedAhead: 2,
ExpectedBehind: 1,
},
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextRebaseApply(t *testing.T) {
want := "\ue728 \ue0a0cool-feature-bro (2/3) at \uf417whatever"
context := &detachedContext{
currentCommit: "whatever",
rebase: "true",
rebaseApply: true,
origin: "cool-feature-bro",
step: "2",
total: "3",
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getRuntimeGOOS", nil).Return("unix")
env.On("isWsl", nil).Return(false)
env.mockGitCommand(strings.ReplaceAll(tc.Output, "\t", ""), "status", "-unormal", "--branch", "--porcelain=2")
g := &git{
env: env,
}
if tc.ExpectedWorking == nil {
tc.ExpectedWorking = &GitStatus{}
}
if tc.ExpectedStaging == nil {
tc.ExpectedStaging = &GitStatus{}
}
g.setGitStatus()
assert.Equal(t, tc.ExpectedStaging, g.Staging, tc.Case)
assert.Equal(t, tc.ExpectedWorking, g.Working, tc.Case)
assert.Equal(t, tc.ExpectedHash, g.Hash, tc.Case)
assert.Equal(t, tc.ExpectedRef, g.Ref, tc.Case)
assert.Equal(t, tc.ExpectedUpstream, g.Upstream, tc.Case)
assert.Equal(t, tc.ExpectedAhead, g.Ahead, tc.Case)
assert.Equal(t, tc.ExpectedBehind, g.Behind, tc.Case)
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextRebaseUnknown(t *testing.T) {
want := "\uf417whatever"
context := &detachedContext{
currentCommit: "whatever",
rebase: "true",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextCherryPickOnBranch(t *testing.T) {
want := "\ue29b pickme onto \ue0a0main"
context := &detachedContext{
currentCommit: "whatever",
branchName: "main",
cherryPick: true,
cherryPickSHA: "pickme",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("main")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextCherryPickOnTag(t *testing.T) {
want := "\ue29b pickme onto \uf412v3.4.6"
context := &detachedContext{
currentCommit: "whatever",
tagName: "v3.4.6",
cherryPick: true,
cherryPickSHA: "pickme",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextRevertOnBranch(t *testing.T) {
want := "\uf0e2 012345 onto \ue0a0main"
context := &detachedContext{
currentCommit: "whatever",
branchName: "main",
revert: true,
revertSHA: "01234567",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("main")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextRevertOnTag(t *testing.T) {
want := "\uf0e2 012345 onto \uf412v3.4.6"
context := &detachedContext{
currentCommit: "whatever",
tagName: "v3.4.6",
revert: true,
revertSHA: "01234567",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextSequencerCherryPickOnBranch(t *testing.T) {
want := "\ue29b pickme onto \ue0a0main"
context := &detachedContext{
currentCommit: "whatever",
branchName: "main",
sequencer: true,
sequencerTodo: "pick pickme message\npick notme message",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("main")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextSequencerCherryPickOnTag(t *testing.T) {
want := "\ue29b pickme onto \uf412v3.4.6"
context := &detachedContext{
currentCommit: "whatever",
tagName: "v3.4.6",
sequencer: true,
sequencerTodo: "pick pickme message\npick notme message",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextSequencerRevertOnBranch(t *testing.T) {
want := "\uf0e2 012345 onto \ue0a0main"
context := &detachedContext{
currentCommit: "whatever",
branchName: "main",
sequencer: true,
sequencerTodo: "revert 01234567 message\nrevert notme message",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("main")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextSequencerRevertOnTag(t *testing.T) {
want := "\uf0e2 012345 onto \uf412v3.4.6"
context := &detachedContext{
currentCommit: "whatever",
tagName: "v3.4.6",
sequencer: true,
sequencerTodo: "revert 01234567 message\nrevert notme message",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextMerge(t *testing.T) {
want := "\ue727 \ue0a0feat into \ue0a0main"
context := &detachedContext{
merge: true,
mergeHEAD: "feat",
mergeMsgStart: "Merge branch",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("main")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextMergeRemote(t *testing.T) {
want := "\ue727 \ue0a0feat into \ue0a0main"
context := &detachedContext{
merge: true,
mergeHEAD: "feat",
mergeMsgStart: "Merge remote-tracking branch",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("main")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextMergeTag(t *testing.T) {
want := "\ue727 \uf412v7.8.9 into \ue0a0main"
context := &detachedContext{
merge: true,
mergeHEAD: "v7.8.9",
mergeMsgStart: "Merge tag",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("main")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextMergeCommit(t *testing.T) {
want := "\ue727 \uf4178d7e869 into \ue0a0main"
context := &detachedContext{
merge: true,
mergeHEAD: "8d7e869",
mergeMsgStart: "Merge commit",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("main")
assert.Equal(t, want, got)
}
func TestGetGitHEADContextMergeIntoTag(t *testing.T) {
want := "\ue727 \ue0a0feat into \uf412v3.4.6"
context := &detachedContext{
tagName: "v3.4.6",
merge: true,
mergeHEAD: "feat",
mergeMsgStart: "Merge branch",
}
g := setupHEADContextEnv(context)
got := g.getGitHEADContext("")
assert.Equal(t, want, got)
}
func TestGetStashContextZeroEntries(t *testing.T) {
@ -392,62 +396,6 @@ func TestGetStashContextZeroEntries(t *testing.T) {
}
}
func TestParseGitBranchInfoEqual(t *testing.T) {
g := git{}
branchInfo := "## master...origin/master"
got := g.parseGitStatusInfo(branchInfo)
assert.Equal(t, "master", got["local"])
assert.Equal(t, "origin/master", got["upstream"])
assert.Empty(t, got["ahead"])
assert.Empty(t, got["behind"])
}
func TestParseGitBranchInfoAhead(t *testing.T) {
g := git{}
branchInfo := "## master...origin/master [ahead 1]"
got := g.parseGitStatusInfo(branchInfo)
assert.Equal(t, "master", got["local"])
assert.Equal(t, "origin/master", got["upstream"])
assert.Equal(t, "1", got["ahead"])
assert.Empty(t, got["behind"])
}
func TestParseGitBranchInfoBehind(t *testing.T) {
g := git{}
branchInfo := "## master...origin/master [behind 1]"
got := g.parseGitStatusInfo(branchInfo)
assert.Equal(t, "master", got["local"])
assert.Equal(t, "origin/master", got["upstream"])
assert.Equal(t, "1", got["behind"])
assert.Empty(t, got["ahead"])
}
func TestParseGitBranchInfoBehindandAhead(t *testing.T) {
g := git{}
branchInfo := "## master...origin/master [ahead 1, behind 2]"
got := g.parseGitStatusInfo(branchInfo)
assert.Equal(t, "master", got["local"])
assert.Equal(t, "origin/master", got["upstream"])
assert.Equal(t, "2", got["behind"])
assert.Equal(t, "1", got["ahead"])
}
func TestParseGitBranchInfoNoRemote(t *testing.T) {
g := git{}
branchInfo := "## master"
got := g.parseGitStatusInfo(branchInfo)
assert.Equal(t, "master", got["local"])
assert.Empty(t, got["upstream"])
}
func TestParseGitBranchInfoRemoteGone(t *testing.T) {
g := git{}
branchInfo := "## test-branch...origin/test-branch [gone]"
got := g.parseGitStatusInfo(branchInfo)
assert.Equal(t, "test-branch", got["local"])
assert.Equal(t, "gone", got["upstream_status"])
}
func TestGitStatusUnmerged(t *testing.T) {
expected := "x1"
status := &GitStatus{
@ -471,71 +419,6 @@ func TestGitStatusEmpty(t *testing.T) {
assert.Equal(t, expected, status.String())
}
func TestParseGitStatsWorking(t *testing.T) {
status := &GitStatus{}
output := []string{
"## amazing-feat",
" M change.go",
"DD change.go",
" ? change.go",
" ? change.go",
" A change.go",
" U change.go",
" R change.go",
" C change.go",
}
status.parse(output, true)
assert.Equal(t, 3, status.Modified)
assert.Equal(t, 1, status.Unmerged)
assert.Equal(t, 3, status.Added)
assert.Equal(t, 1, status.Deleted)
assert.True(t, status.Changed)
}
func TestParseGitStatsStaging(t *testing.T) {
status := &GitStatus{}
output := []string{
"## amazing-feat",
" M change.go",
"DD change.go",
" ? change.go",
"?? change.go",
" A change.go",
"DU change.go",
"MR change.go",
"AC change.go",
}
status.parse(output, false)
assert.Equal(t, 1, status.Modified)
assert.Equal(t, 0, status.Unmerged)
assert.Equal(t, 1, status.Added)
assert.Equal(t, 2, status.Deleted)
assert.True(t, status.Changed)
}
func TestParseGitStatsNoChanges(t *testing.T) {
status := &GitStatus{}
expected := &GitStatus{}
output := []string{
"## amazing-feat",
}
status.parse(output, false)
assert.Equal(t, expected, status)
assert.False(t, status.Changed)
}
func TestParseGitStatsInvalidLine(t *testing.T) {
status := &GitStatus{}
expected := &GitStatus{}
output := []string{
"## amazing-feat",
"#",
}
status.parse(output, false)
assert.Equal(t, expected, status)
assert.False(t, status.Changed)
}
func TestGitUpstream(t *testing.T) {
cases := []struct {
Case string
@ -580,7 +463,7 @@ func TestGetBranchStatus(t *testing.T) {
Behind int
Upstream string
}{
{Case: "Equal with remote", Expected: " equal", Upstream: "main"},
{Case: "Equal with remote", Expected: " equal", Upstream: branchName},
{Case: "Ahead", Expected: " up2", Ahead: 2},
{Case: "Behind", Expected: " down8", Behind: 8},
{Case: "Behind and ahead", Expected: " up7 down8", Behind: 8, Ahead: 7},
@ -657,7 +540,7 @@ func TestTruncateBranch(t *testing.T) {
g := &git{
props: props,
}
assert.Equal(t, tc.Expected, g.truncateBranch(tc.Branch), tc.Case)
assert.Equal(t, tc.Expected, g.formatHEAD(tc.Branch), tc.Case)
}
}
@ -684,7 +567,7 @@ func TestTruncateBranchWithSymbol(t *testing.T) {
g := &git{
props: props,
}
assert.Equal(t, tc.Expected, g.truncateBranch(tc.Branch), tc.Case)
assert.Equal(t, tc.Expected, g.formatHEAD(tc.Branch), tc.Case)
}
}
@ -730,10 +613,10 @@ func TestGitTemplateString(t *testing.T) {
}{
{
Case: "Only HEAD name",
Expected: "main",
Expected: branchName,
Template: "{{ .HEAD }}",
Git: &git{
HEAD: "main",
HEAD: branchName,
Behind: 2,
},
},
@ -742,23 +625,20 @@ func TestGitTemplateString(t *testing.T) {
Expected: "main \uF044 +2 ~3",
Template: "{{ .HEAD }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}",
Git: &git{
HEAD: "main",
HEAD: branchName,
Working: &GitStatus{
Added: 2,
Modified: 3,
Changed: true,
},
},
},
{
Case: "No working area changes",
Expected: "main",
Expected: branchName,
Template: "{{ .HEAD }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}",
Git: &git{
HEAD: "main",
Working: &GitStatus{
Changed: false,
},
HEAD: branchName,
Working: &GitStatus{},
},
},
{
@ -766,16 +646,14 @@ func TestGitTemplateString(t *testing.T) {
Expected: "main \uF046 +5 ~1 \uF044 +2 ~3",
Template: "{{ .HEAD }}{{ if .Staging.Changed }} \uF046 {{ .Staging.String }}{{ end }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}",
Git: &git{
HEAD: "main",
HEAD: branchName,
Working: &GitStatus{
Added: 2,
Modified: 3,
Changed: true,
},
Staging: &GitStatus{
Added: 5,
Modified: 1,
Changed: true,
},
},
},
@ -784,16 +662,14 @@ func TestGitTemplateString(t *testing.T) {
Expected: "main \uF046 +5 ~1 | \uF044 +2 ~3",
Template: "{{ .HEAD }}{{ if .Staging.Changed }} \uF046 {{ .Staging.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}", //nolint:lll
Git: &git{
HEAD: "main",
HEAD: branchName,
Working: &GitStatus{
Added: 2,
Modified: 3,
Changed: true,
},
Staging: &GitStatus{
Added: 5,
Modified: 1,
Changed: true,
},
},
},
@ -802,26 +678,24 @@ func TestGitTemplateString(t *testing.T) {
Expected: "main \uF046 +5 ~1 | \uF044 +2 ~3 \uf692 3",
Template: "{{ .HEAD }}{{ if .Staging.Changed }} \uF046 {{ .Staging.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}{{ if gt .StashCount 0 }} \uF692 {{ .StashCount }}{{ end }}", //nolint:lll
Git: &git{
HEAD: "main",
HEAD: branchName,
Working: &GitStatus{
Added: 2,
Modified: 3,
Changed: true,
},
Staging: &GitStatus{
Added: 5,
Modified: 1,
Changed: true,
},
StashCount: 3,
},
},
{
Case: "No local changes",
Expected: "main",
Expected: branchName,
Template: "{{ .HEAD }}{{ if .Staging.Changed }} \uF046{{ .Staging.String }}{{ end }}{{ if .Working.Changed }} \uF044{{ .Working.String }}{{ end }}",
Git: &git{
HEAD: "main",
HEAD: branchName,
Staging: &GitStatus{},
Working: &GitStatus{},
},
@ -831,7 +705,7 @@ func TestGitTemplateString(t *testing.T) {
Expected: "from GitHub on main",
Template: "from {{ .UpstreamIcon }} on {{ .HEAD }}",
Git: &git{
HEAD: "main",
HEAD: branchName,
Staging: &GitStatus{},
Working: &GitStatus{},
UpstreamIcon: "GitHub",