From 98472abafc4feb9ec149889d137e82013f37137d Mon Sep 17 00:00:00 2001 From: Jan De Dobbeleer Date: Sun, 5 Dec 2021 18:51:10 +0100 Subject: [PATCH] feat(git): better logic --- src/segment_deprecated.go | 8 +- src/segment_deprecated_test.go | 65 +-- src/segment_git.go | 259 +++++++----- src/segment_git_test.go | 724 ++++++++++++++------------------- 4 files changed, 497 insertions(+), 559 deletions(-) diff --git a/src/segment_deprecated.go b/src/segment_deprecated.go index 6436799c..b5059ace 100644 --- a/src/segment_deprecated.go +++ b/src/segment_deprecated.go @@ -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) diff --git a/src/segment_deprecated_test.go b/src/segment_deprecated_test.go index 5f230048..bb6b0078 100644 --- a/src/segment_deprecated_test.go +++ b/src/segment_deprecated_test.go @@ -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]) } diff --git a/src/segment_git.go b/src/segment_git.go index a9cebba3..ea1f8926 100644 --- a/src/segment_git.go +++ b/src/segment_git.go @@ -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(remote-tracking )?branch|commit|tag) '(?P.*)' into`, mergeContext) - - if matches != nil && matches["head"] != "" { - var headIcon string + matches := findNamedRegexMatch(`Merge (remote-tracking )?(?Pbranch|commit|tag) '(?P.*)'`, 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(`^(?Pp|pick|revert)\s+(?P\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\S+?)(\.{3}(?P\S+?)( \[(?P(ahead (?P\d+)(, )?)?(behind (?P\d+))?(gone)?)])?)?$` - return findNamedRegexMatch(branchRegex, branchInfo) -} - func (g *git) getOriginURL(upstream string) string { cfg, err := ini.Load(g.gitRootFolder + "/config") if err != nil { diff --git a/src/segment_git_test.go b/src/segment_git_test.go index 1eb0411e..31388c2a 100644 --- a/src/segment_git_test.go +++ b/src/segment_git_test.go @@ -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",