mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-28 04:19:41 -08:00
feat(git): better logic
This commit is contained in:
parent
4f921dbb0f
commit
98472abafc
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue