mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-02-02 05:41:10 -08:00
feat: cherry-pick visualization
This commit is contained in:
parent
9f23514268
commit
055877ba6b
|
@ -29,7 +29,10 @@ Local changes can also shown by default using the following syntax for both the
|
|||
"branch_behind_icon": "↓",
|
||||
"local_working_icon": "",
|
||||
"local_staged_icon": "",
|
||||
"rebase_icon": " "
|
||||
"rebase_icon": " ",
|
||||
"cherry_pick_icon": " ",
|
||||
"detached_icon": " ",
|
||||
"tag_icon": "笠 "
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -44,3 +47,6 @@ Local changes can also shown by default using the following syntax for both the
|
|||
- local_staged_icon: `string` - the icon to display in front of the staged area changes
|
||||
- display_status: `boolean` - display the local changes or not
|
||||
- rebase_icon: `string` - icon/text to display before the context when in a rebase
|
||||
- cherry_pick_icon: `string` - icon/text to display before the context when doing a cherry-pick
|
||||
- detached_icon: `string` - icon/text to display before the detached head context
|
||||
- tag_icon: `string` - icon/text to display before the tag context
|
||||
|
|
|
@ -49,6 +49,12 @@ const (
|
|||
DisplayStatus Property = "display_status"
|
||||
//RebaseIcon shows before the rebase context
|
||||
RebaseIcon Property = "rebase_icon"
|
||||
//CherryPickIcon shows before the cherry-pick context
|
||||
CherryPickIcon Property = "cherry_pick_icon"
|
||||
//DetachedIcon shows before the detached context
|
||||
DetachedIcon Property = "detached_icon"
|
||||
//TagIcon shows before the tag context
|
||||
TagIcon Property = "tag_icon"
|
||||
)
|
||||
|
||||
func (g *git) enabled() bool {
|
||||
|
@ -103,15 +109,13 @@ func (g *git) getGitStatus() {
|
|||
splittedOutput := strings.Split(output, "\n")
|
||||
g.repo.working = g.parseGitStats(splittedOutput, true)
|
||||
g.repo.staging = g.parseGitStats(splittedOutput, false)
|
||||
branchInfo := g.parseGitBranchInfo(splittedOutput[0])
|
||||
if branchInfo["local"] != "" {
|
||||
g.repo.ahead, _ = strconv.Atoi(branchInfo["ahead"])
|
||||
g.repo.behind, _ = strconv.Atoi(branchInfo["behind"])
|
||||
g.repo.branch = fmt.Sprintf("%s%s", g.props.getString(BranchIcon, "Branch:"), branchInfo["local"])
|
||||
g.repo.upstream = branchInfo["upstream"]
|
||||
} else {
|
||||
g.repo.branch = g.getGitDetachedBranchContext()
|
||||
status := g.parseGitStatusInfo(splittedOutput[0])
|
||||
if status["local"] != "" {
|
||||
g.repo.ahead, _ = strconv.Atoi(status["ahead"])
|
||||
g.repo.behind, _ = strconv.Atoi(status["behind"])
|
||||
g.repo.upstream = status["upstream"]
|
||||
}
|
||||
g.repo.branch = g.getGitHEADContext()
|
||||
g.repo.stashCount = g.getStashContext()
|
||||
}
|
||||
|
||||
|
@ -120,23 +124,31 @@ func (g *git) getGitCommandOutput(args ...string) string {
|
|||
return g.env.runCommand("git", args...)
|
||||
}
|
||||
|
||||
func (g *git) getGitDetachedBranchContext() string {
|
||||
func (g *git) getGitHEADContext() string {
|
||||
commit := g.getGitCommandOutput("rev-parse", "--short", "HEAD")
|
||||
rebase := g.getGitCommandOutput("rebase", "--show-current-patch")
|
||||
if rebase != "" {
|
||||
return g.getGitRebaseContext(commit)
|
||||
}
|
||||
// name of branch
|
||||
// branch
|
||||
icon := g.props.getString(BranchIcon, "BRANCH:")
|
||||
ref := g.getGitCommandOutput("symbolic-ref", "-q", "--short", "HEAD")
|
||||
if ref == "" {
|
||||
// get a tag name if there's a match for HEAD
|
||||
icon = g.props.getString(TagIcon, "TAG:")
|
||||
ref = g.getGitCommandOutput("describe", "--tags", "--exact-match")
|
||||
}
|
||||
if ref == "" {
|
||||
// revert to the short commit hash
|
||||
ref = commit
|
||||
// validate additional context
|
||||
if g.env.hasFiles(".git/CHERRY_PICK_HEAD") {
|
||||
sha := g.getGitRefFileSymbolicName("CHERRY_PICK_HEAD")
|
||||
icon := g.props.getString(CherryPickIcon, "CHERRY PICK:")
|
||||
return fmt.Sprintf("%s%s onto %s", icon, sha, ref)
|
||||
}
|
||||
return ref
|
||||
if ref == "" {
|
||||
ref = commit
|
||||
icon = g.props.getString(DetachedIcon, "DETACHED:")
|
||||
}
|
||||
return fmt.Sprintf("%s%s", icon, ref)
|
||||
}
|
||||
|
||||
func (g *git) getGitRebaseContext(commit string) string {
|
||||
|
@ -212,7 +224,7 @@ func (g *git) hasWorking() bool {
|
|||
return g.repo.working.deleted > 0 || g.repo.working.added > 0 || g.repo.working.unmerged > 0 || g.repo.working.modified > 0 || g.repo.working.untracked > 0
|
||||
}
|
||||
|
||||
func (g *git) parseGitBranchInfo(branchInfo string) map[string]string {
|
||||
func (g *git) parseGitStatusInfo(branchInfo string) map[string]string {
|
||||
var branchRegex = regexp.MustCompile(`^## (?P<local>\S+?)(\.{3}(?P<upstream>\S+?)( \[(ahead (?P<ahead>\d+)(, )?)?(behind (?P<behind>\d+))?])?)?$`)
|
||||
return groupDict(branchRegex, branchInfo)
|
||||
}
|
||||
|
|
|
@ -50,9 +50,11 @@ type detachedContext struct {
|
|||
total string
|
||||
branchName string
|
||||
tagName string
|
||||
cherryPick bool
|
||||
cherryPickSHA string
|
||||
}
|
||||
|
||||
func setupDetachedHeadEnv(context *detachedContext) environmentInfo {
|
||||
func setupHEADContextEnv(context *detachedContext) environmentInfo {
|
||||
env := new(MockedEnvironment)
|
||||
env.On("hasFolder", ".git/rebase-merge").Return(context.rebaseMerge)
|
||||
env.On("hasFolder", ".git/rebase-apply").Return(context.rebaseApply)
|
||||
|
@ -63,43 +65,46 @@ func setupDetachedHeadEnv(context *detachedContext) environmentInfo {
|
|||
env.On("getFileContent", ".git/rebase-merge/end").Return(context.total)
|
||||
env.On("getFileContent", ".git/rebase-apply/last").Return(context.total)
|
||||
env.On("getFileContent", ".git/rebase-apply/head-name").Return(context.origin)
|
||||
env.On("getFileContent", ".git/CHERRY_PICK_HEAD").Return(context.cherryPickSHA)
|
||||
env.On("hasFiles", ".git/CHERRY_PICK_HEAD").Return(context.cherryPick)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "rev-parse", "--short", "HEAD"}).Return(context.currentCommit)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "rebase", "--show-current-patch"}).Return(context.rebase)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "symbolic-ref", "-q", "--short", "HEAD"}).Return(context.branchName)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "describe", "--tags", "--exact-match"}).Return(context.tagName)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "name-rev", "--name-only", "--exclude=tags/*", context.origin}).Return(context.origin)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "name-rev", "--name-only", "--exclude=tags/*", context.onto}).Return(context.onto)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "name-rev", "--name-only", "--exclude=tags/*", context.cherryPickSHA}).Return(context.cherryPickSHA)
|
||||
return env
|
||||
}
|
||||
|
||||
func TestGetGitDetachedCommitHash(t *testing.T) {
|
||||
want := "lalasha1"
|
||||
want := "DETACHED:lalasha1"
|
||||
context := &detachedContext{
|
||||
currentCommit: want,
|
||||
currentCommit: "lalasha1",
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
env := setupHEADContextEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranchContext()
|
||||
got := g.getGitHEADContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedTagName(t *testing.T) {
|
||||
want := "lalasha1"
|
||||
func TestGetGitHEADContextTagName(t *testing.T) {
|
||||
want := "TAG:lalasha1"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
tagName: want,
|
||||
tagName: "lalasha1",
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
env := setupHEADContextEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranchContext()
|
||||
got := g.getGitHEADContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedRebaseMerge(t *testing.T) {
|
||||
func TestGetGitHEADContextRebaseMerge(t *testing.T) {
|
||||
want := "REBASE:cool-feature-bro onto main (2/3) at whatever"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
|
@ -110,15 +115,15 @@ func TestGetGitDetachedRebaseMerge(t *testing.T) {
|
|||
step: "2",
|
||||
total: "3",
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
env := setupHEADContextEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranchContext()
|
||||
got := g.getGitHEADContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedRebaseApply(t *testing.T) {
|
||||
func TestGetGitHEADContextRebaseApply(t *testing.T) {
|
||||
want := "REBASING:cool-feature-bro (2/3) at whatever"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
|
@ -128,25 +133,57 @@ func TestGetGitDetachedRebaseApply(t *testing.T) {
|
|||
step: "2",
|
||||
total: "3",
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
env := setupHEADContextEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranchContext()
|
||||
got := g.getGitHEADContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedRebaseUnknown(t *testing.T) {
|
||||
func TestGetGitHEADContextRebaseUnknown(t *testing.T) {
|
||||
want := "REBASE:UNKNOWN"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
rebase: "true",
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
env := setupHEADContextEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranchContext()
|
||||
got := g.getGitHEADContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitHEADContextCherryPickOnBranch(t *testing.T) {
|
||||
want := "CHERRY PICK:pickme onto main"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
branchName: "main",
|
||||
cherryPick: true,
|
||||
cherryPickSHA: "pickme",
|
||||
}
|
||||
env := setupHEADContextEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitHEADContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitHEADContextCherryPickOnTag(t *testing.T) {
|
||||
want := "CHERRY PICK:pickme onto v3.4.6"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
tagName: "v3.4.6",
|
||||
cherryPick: true,
|
||||
cherryPickSHA: "pickme",
|
||||
}
|
||||
env := setupHEADContextEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitHEADContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
|
@ -190,7 +227,7 @@ func TestGetStashContextOneEntry(t *testing.T) {
|
|||
func TestParseGitBranchInfoEqual(t *testing.T) {
|
||||
g := git{}
|
||||
branchInfo := "## master...origin/master"
|
||||
got := g.parseGitBranchInfo(branchInfo)
|
||||
got := g.parseGitStatusInfo(branchInfo)
|
||||
assert.Equal(t, "master", got["local"])
|
||||
assert.Equal(t, "origin/master", got["upstream"])
|
||||
assert.Empty(t, got["ahead"])
|
||||
|
@ -200,7 +237,7 @@ func TestParseGitBranchInfoEqual(t *testing.T) {
|
|||
func TestParseGitBranchInfoAhead(t *testing.T) {
|
||||
g := git{}
|
||||
branchInfo := "## master...origin/master [ahead 1]"
|
||||
got := g.parseGitBranchInfo(branchInfo)
|
||||
got := g.parseGitStatusInfo(branchInfo)
|
||||
assert.Equal(t, "master", got["local"])
|
||||
assert.Equal(t, "origin/master", got["upstream"])
|
||||
assert.Equal(t, "1", got["ahead"])
|
||||
|
@ -210,7 +247,7 @@ func TestParseGitBranchInfoAhead(t *testing.T) {
|
|||
func TestParseGitBranchInfoBehind(t *testing.T) {
|
||||
g := git{}
|
||||
branchInfo := "## master...origin/master [behind 1]"
|
||||
got := g.parseGitBranchInfo(branchInfo)
|
||||
got := g.parseGitStatusInfo(branchInfo)
|
||||
assert.Equal(t, "master", got["local"])
|
||||
assert.Equal(t, "origin/master", got["upstream"])
|
||||
assert.Equal(t, "1", got["behind"])
|
||||
|
@ -220,7 +257,7 @@ func TestParseGitBranchInfoBehind(t *testing.T) {
|
|||
func TestParseGitBranchInfoBehindandAhead(t *testing.T) {
|
||||
g := git{}
|
||||
branchInfo := "## master...origin/master [ahead 1, behind 2]"
|
||||
got := g.parseGitBranchInfo(branchInfo)
|
||||
got := g.parseGitStatusInfo(branchInfo)
|
||||
assert.Equal(t, "master", got["local"])
|
||||
assert.Equal(t, "origin/master", got["upstream"])
|
||||
assert.Equal(t, "2", got["behind"])
|
||||
|
|
|
@ -53,7 +53,10 @@
|
|||
"branch_gone_icon": "≢",
|
||||
"local_working_icon": "",
|
||||
"local_staged_icon": "",
|
||||
"rebase_icon": " "
|
||||
"rebase_icon": " ",
|
||||
"cherry_pick_icon": " ",
|
||||
"detached_icon": " ",
|
||||
"tag_icon": "笠"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue