mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-11-09 20:44:03 -08:00
feat: rebase support
This commit is contained in:
parent
e0adc584e5
commit
9f23514268
|
@ -28,7 +28,8 @@ Local changes can also shown by default using the following syntax for both the
|
|||
"branch_ahead_icon": "↑",
|
||||
"branch_behind_icon": "↓",
|
||||
"local_working_icon": "",
|
||||
"local_staged_icon": ""
|
||||
"local_staged_icon": "",
|
||||
"rebase_icon": " "
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -42,3 +43,4 @@ Local changes can also shown by default using the following syntax for both the
|
|||
- local_working_icon: `string` - the icon to display in front of the working area changes
|
||||
- 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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -18,6 +19,8 @@ type environmentInfo interface {
|
|||
getcwd() string
|
||||
homeDir() string
|
||||
hasFiles(pattern string) bool
|
||||
hasFolder(folder string) bool
|
||||
getFileContent(file string) string
|
||||
getPathSeperator() string
|
||||
getCurrentUser() (*user.User, error)
|
||||
isRunningAsRoot() bool
|
||||
|
@ -69,6 +72,19 @@ func (env *environment) hasFiles(pattern string) bool {
|
|||
return len(matches) > 0
|
||||
}
|
||||
|
||||
func (env *environment) hasFolder(folder string) bool {
|
||||
_, err := os.Stat(folder)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func (env *environment) getFileContent(file string) string {
|
||||
content, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(content)
|
||||
}
|
||||
|
||||
func (env *environment) getPathSeperator() string {
|
||||
return string(os.PathSeparator)
|
||||
}
|
||||
|
|
|
@ -62,8 +62,6 @@ func (g *git) enabled() bool {
|
|||
func (g *git) string() string {
|
||||
g.getGitStatus()
|
||||
buffer := new(bytes.Buffer)
|
||||
// branchsymbol
|
||||
buffer.WriteString(g.props.getString(BranchIcon, "Branch: "))
|
||||
// branchName
|
||||
fmt.Fprintf(buffer, "%s", g.repo.branch)
|
||||
displayStatus := g.props.getBool(DisplayStatus, true)
|
||||
|
@ -101,7 +99,7 @@ func (g *git) init(props *properties, env environmentInfo) {
|
|||
|
||||
func (g *git) getGitStatus() {
|
||||
g.repo = &gitRepo{}
|
||||
output := g.getGitOutputForCommand("status", "--porcelain", "-b", "--ignore-submodules")
|
||||
output := g.getGitCommandOutput("status", "--porcelain", "-b", "--ignore-submodules")
|
||||
splittedOutput := strings.Split(output, "\n")
|
||||
g.repo.working = g.parseGitStats(splittedOutput, true)
|
||||
g.repo.staging = g.parseGitStats(splittedOutput, false)
|
||||
|
@ -109,35 +107,69 @@ func (g *git) getGitStatus() {
|
|||
if branchInfo["local"] != "" {
|
||||
g.repo.ahead, _ = strconv.Atoi(branchInfo["ahead"])
|
||||
g.repo.behind, _ = strconv.Atoi(branchInfo["behind"])
|
||||
g.repo.branch = branchInfo["local"]
|
||||
g.repo.branch = fmt.Sprintf("%s%s", g.props.getString(BranchIcon, "Branch:"), branchInfo["local"])
|
||||
g.repo.upstream = branchInfo["upstream"]
|
||||
} else {
|
||||
g.repo.branch = g.getGitDetachedBranch()
|
||||
g.repo.branch = g.getGitDetachedBranchContext()
|
||||
}
|
||||
g.repo.stashCount = g.getStashContext()
|
||||
}
|
||||
|
||||
func (g *git) getGitOutputForCommand(args ...string) string {
|
||||
func (g *git) getGitCommandOutput(args ...string) string {
|
||||
args = append([]string{"-c", "core.quotepath=false", "-c", "color.status=false"}, args...)
|
||||
return g.env.runCommand("git", args...)
|
||||
}
|
||||
|
||||
func (g *git) getGitDetachedBranch() string {
|
||||
commit := g.getGitOutputForCommand("rev-parse", "--short", "HEAD")
|
||||
rebase := g.getGitOutputForCommand("rebase", "--show-current-patch")
|
||||
func (g *git) getGitDetachedBranchContext() string {
|
||||
commit := g.getGitCommandOutput("rev-parse", "--short", "HEAD")
|
||||
rebase := g.getGitCommandOutput("rebase", "--show-current-patch")
|
||||
if rebase != "" {
|
||||
return fmt.Sprintf("%s%s", g.props.getString(RebaseIcon, "REBASE: "), commit)
|
||||
return g.getGitRebaseContext(commit)
|
||||
}
|
||||
ref := g.getGitOutputForCommand("symbolic-ref", "-q", "--short", "HEAD")
|
||||
// name of branch
|
||||
ref := g.getGitCommandOutput("symbolic-ref", "-q", "--short", "HEAD")
|
||||
if ref == "" {
|
||||
ref = g.getGitOutputForCommand("describe", "--tags", "--exact-match")
|
||||
// get a tag name if there's a match for HEAD
|
||||
ref = g.getGitCommandOutput("describe", "--tags", "--exact-match")
|
||||
}
|
||||
if ref == "" {
|
||||
// revert to the short commit hash
|
||||
ref = commit
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
func (g *git) getGitRebaseContext(commit string) string {
|
||||
if g.env.hasFolder(".git/rebase-merge") {
|
||||
origin := g.getGitRefFileSymbolicName("rebase-merge/orig-head")
|
||||
onto := g.getGitRefFileSymbolicName("rebase-merge/onto")
|
||||
step := g.getGitFileContents("rebase-merge/msgnum")
|
||||
total := g.getGitFileContents("rebase-merge/end")
|
||||
icon := g.props.getString(RebaseIcon, "REBASE:")
|
||||
return fmt.Sprintf("%s%s onto %s (%s/%s) at %s", icon, origin, onto, step, total, commit)
|
||||
}
|
||||
if g.env.hasFolder(".git/rebase-apply") {
|
||||
head := g.getGitFileContents("rebase-apply/head-name")
|
||||
origin := strings.Replace(head, "refs/heads/", "", 1)
|
||||
step := g.getGitFileContents("rebase-apply/next")
|
||||
total := g.getGitFileContents("rebase-apply/last")
|
||||
icon := g.props.getString(RebaseIcon, "REBASING:")
|
||||
return fmt.Sprintf("%s%s (%s/%s) at %s", icon, origin, step, total, commit)
|
||||
}
|
||||
icon := g.props.getString(RebaseIcon, "REBASE:")
|
||||
return fmt.Sprintf("%sUNKNOWN", icon)
|
||||
}
|
||||
|
||||
func (g *git) getGitFileContents(file string) string {
|
||||
content := g.env.getFileContent(fmt.Sprintf(".git/%s", file))
|
||||
return strings.Trim(content, " \r\n")
|
||||
}
|
||||
|
||||
func (g *git) getGitRefFileSymbolicName(refFile string) string {
|
||||
ref := g.getGitFileContents(refFile)
|
||||
return g.getGitCommandOutput("name-rev", "--name-only", "--exclude=tags/*", ref)
|
||||
}
|
||||
|
||||
func (g *git) parseGitStats(output []string, working bool) *gitStatus {
|
||||
status := gitStatus{}
|
||||
if len(output) <= 1 {
|
||||
|
@ -168,7 +200,7 @@ func (g *git) parseGitStats(output []string, working bool) *gitStatus {
|
|||
}
|
||||
|
||||
func (g *git) getStashContext() int {
|
||||
stash := g.getGitOutputForCommand("stash", "list")
|
||||
stash := g.getGitCommandOutput("stash", "list")
|
||||
return numberOfLinesInString(stash)
|
||||
}
|
||||
|
||||
|
|
|
@ -35,29 +35,118 @@ func TestGetGitOutputForCommand(t *testing.T) {
|
|||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitOutputForCommand(commandArgs...)
|
||||
got := g.getGitCommandOutput(commandArgs...)
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedBranch(t *testing.T) {
|
||||
want := "master"
|
||||
type detachedContext struct {
|
||||
currentCommit string
|
||||
rebase string
|
||||
rebaseMerge bool
|
||||
rebaseApply bool
|
||||
origin string
|
||||
onto string
|
||||
step string
|
||||
total string
|
||||
branchName string
|
||||
tagName string
|
||||
}
|
||||
|
||||
func setupDetachedHeadEnv(context *detachedContext) environmentInfo {
|
||||
env := new(MockedEnvironment)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "symbolic-ref", "--short", "HEAD"}).Return(want)
|
||||
env.On("hasFolder", ".git/rebase-merge").Return(context.rebaseMerge)
|
||||
env.On("hasFolder", ".git/rebase-apply").Return(context.rebaseApply)
|
||||
env.On("getFileContent", ".git/rebase-merge/orig-head").Return(context.origin)
|
||||
env.On("getFileContent", ".git/rebase-merge/onto").Return(context.onto)
|
||||
env.On("getFileContent", ".git/rebase-merge/msgnum").Return(context.step)
|
||||
env.On("getFileContent", ".git/rebase-apply/next").Return(context.step)
|
||||
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("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)
|
||||
return env
|
||||
}
|
||||
|
||||
func TestGetGitDetachedCommitHash(t *testing.T) {
|
||||
want := "lalasha1"
|
||||
context := &detachedContext{
|
||||
currentCommit: want,
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranch()
|
||||
got := g.getGitDetachedBranchContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedBranchEmpty(t *testing.T) {
|
||||
want := "unknown"
|
||||
env := new(MockedEnvironment)
|
||||
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "symbolic-ref", "--short", "HEAD"}).Return("")
|
||||
func TestGetGitDetachedTagName(t *testing.T) {
|
||||
want := "lalasha1"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
tagName: want,
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranch()
|
||||
got := g.getGitDetachedBranchContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedRebaseMerge(t *testing.T) {
|
||||
want := "REBASE:cool-feature-bro onto main (2/3) at whatever"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
rebase: "true",
|
||||
rebaseMerge: true,
|
||||
origin: "cool-feature-bro",
|
||||
onto: "main",
|
||||
step: "2",
|
||||
total: "3",
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranchContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedRebaseApply(t *testing.T) {
|
||||
want := "REBASING:cool-feature-bro (2/3) at whatever"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
rebase: "true",
|
||||
rebaseApply: true,
|
||||
origin: "cool-feature-bro",
|
||||
step: "2",
|
||||
total: "3",
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranchContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
func TestGetGitDetachedRebaseUnknown(t *testing.T) {
|
||||
want := "REBASE:UNKNOWN"
|
||||
context := &detachedContext{
|
||||
currentCommit: "whatever",
|
||||
rebase: "true",
|
||||
}
|
||||
env := setupDetachedHeadEnv(context)
|
||||
g := &git{
|
||||
env: env,
|
||||
}
|
||||
got := g.getGitDetachedBranchContext()
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,16 @@ func (env *MockedEnvironment) hasFiles(pattern string) bool {
|
|||
return args.Bool(0)
|
||||
}
|
||||
|
||||
func (env *MockedEnvironment) hasFolder(folder string) bool {
|
||||
args := env.Called(folder)
|
||||
return args.Bool(0)
|
||||
}
|
||||
|
||||
func (env *MockedEnvironment) getFileContent(file string) string {
|
||||
args := env.Called(file)
|
||||
return args.String(0)
|
||||
}
|
||||
|
||||
func (env *MockedEnvironment) getPathSeperator() string {
|
||||
args := env.Called(nil)
|
||||
return args.String(0)
|
||||
|
|
|
@ -52,7 +52,8 @@
|
|||
"branch_behind_icon": "↓",
|
||||
"branch_gone_icon": "≢",
|
||||
"local_working_icon": "",
|
||||
"local_staged_icon": ""
|
||||
"local_staged_icon": "",
|
||||
"rebase_icon": " "
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue