mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-31 13:57:26 -08:00
feat: add svn segment
This commit is contained in:
parent
d0fe5b7ade
commit
a9c1292d83
|
@ -163,6 +163,8 @@ const (
|
||||||
SPOTIFY SegmentType = "spotify"
|
SPOTIFY SegmentType = "spotify"
|
||||||
// STRAVA is a sports activity tracker
|
// STRAVA is a sports activity tracker
|
||||||
STRAVA SegmentType = "strava"
|
STRAVA SegmentType = "strava"
|
||||||
|
// Subversion segment
|
||||||
|
SVN SegmentType = "svn"
|
||||||
// SWIFT writes the active swift version
|
// SWIFT writes the active swift version
|
||||||
SWIFT SegmentType = "swift"
|
SWIFT SegmentType = "swift"
|
||||||
// SYSTEMINFO writes system information (memory, cpu, load)
|
// SYSTEMINFO writes system information (memory, cpu, load)
|
||||||
|
@ -296,6 +298,7 @@ func (segment *Segment) mapSegmentWithWriter(env environment.Environment) error
|
||||||
SHELL: &segments.Shell{},
|
SHELL: &segments.Shell{},
|
||||||
SPOTIFY: &segments.Spotify{},
|
SPOTIFY: &segments.Spotify{},
|
||||||
STRAVA: &segments.Strava{},
|
STRAVA: &segments.Strava{},
|
||||||
|
SVN: &segments.Svn{},
|
||||||
SWIFT: &segments.Swift{},
|
SWIFT: &segments.Swift{},
|
||||||
SYSTEMINFO: &segments.SystemInfo{},
|
SYSTEMINFO: &segments.SystemInfo{},
|
||||||
TERRAFORM: &segments.Terraform{},
|
TERRAFORM: &segments.Terraform{},
|
||||||
|
|
|
@ -221,6 +221,11 @@ func (env *MockedEnvironment) MockGitCommand(dir, returnValue string, args ...st
|
||||||
env.On("RunCommand", "git", args).Return(returnValue, nil)
|
env.On("RunCommand", "git", args).Return(returnValue, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (env *MockedEnvironment) MockSvnCommand(dir, returnValue string, args ...string) {
|
||||||
|
args = append([]string{"-C", dir, "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...)
|
||||||
|
env.On("RunCommand", "svn", args).Return(returnValue, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (env *MockedEnvironment) HasFileInParentDirs(pattern string, depth uint) bool {
|
func (env *MockedEnvironment) HasFileInParentDirs(pattern string, depth uint) bool {
|
||||||
args := env.Called(pattern, depth)
|
args := env.Called(pattern, depth)
|
||||||
return args.Bool(0)
|
return args.Bool(0)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package segments
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"oh-my-posh/environment"
|
|
||||||
"oh-my-posh/properties"
|
"oh-my-posh/properties"
|
||||||
"oh-my-posh/regex"
|
"oh-my-posh/regex"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -49,14 +48,6 @@ type Git struct {
|
||||||
StashCount int
|
StashCount int
|
||||||
WorktreeCount int
|
WorktreeCount int
|
||||||
IsWorkTree bool
|
IsWorkTree bool
|
||||||
|
|
||||||
gitWorkingFolder string // .git working folder
|
|
||||||
gitRootFolder string // .git root folder
|
|
||||||
gitRealFolder string // .git real folder(can be different from current path when in worktrees)
|
|
||||||
|
|
||||||
gitCommand string
|
|
||||||
|
|
||||||
IsWslSharedPath bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -108,6 +99,7 @@ const (
|
||||||
|
|
||||||
DETACHED = "(detached)"
|
DETACHED = "(detached)"
|
||||||
BRANCHPREFIX = "ref: refs/heads/"
|
BRANCHPREFIX = "ref: refs/heads/"
|
||||||
|
GITCOMMAND = "git"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *Git) Template() string {
|
func (g *Git) Template() string {
|
||||||
|
@ -145,7 +137,7 @@ func (g *Git) shouldDisplay() bool {
|
||||||
// we must use git.exe and convert paths accordingly
|
// we must use git.exe and convert paths accordingly
|
||||||
// for worktrees, stashes, and path to work
|
// for worktrees, stashes, and path to work
|
||||||
g.IsWslSharedPath = g.env.InWSLSharedDrive()
|
g.IsWslSharedPath = g.env.InWSLSharedDrive()
|
||||||
if !g.env.HasCommand(g.getGitCommand()) {
|
if !g.env.HasCommand(g.getCommand(GITCOMMAND)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
gitdir, err := g.env.HasParentFilePath(".git")
|
gitdir, err := g.env.HasParentFilePath(".git")
|
||||||
|
@ -157,48 +149,48 @@ func (g *Git) shouldDisplay() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if gitdir.IsDir {
|
if gitdir.IsDir {
|
||||||
g.gitWorkingFolder = gitdir.Path
|
g.workingFolder = gitdir.Path
|
||||||
g.gitRootFolder = gitdir.Path
|
g.rootFolder = gitdir.Path
|
||||||
// convert the worktree file path to a windows one when in wsl 2 shared folder
|
// convert the worktree file path to a windows one when in wsl 2 shared folder
|
||||||
g.gitRealFolder = strings.TrimSuffix(g.convertToWindowsPath(gitdir.Path), ".git")
|
g.realFolder = strings.TrimSuffix(g.convertToWindowsPath(gitdir.Path), ".git")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// handle worktree
|
// handle worktree
|
||||||
g.gitRootFolder = gitdir.Path
|
g.rootFolder = gitdir.Path
|
||||||
dirPointer := strings.Trim(g.env.FileContent(gitdir.Path), " \r\n")
|
dirPointer := strings.Trim(g.env.FileContent(gitdir.Path), " \r\n")
|
||||||
matches := regex.FindNamedRegexMatch(`^gitdir: (?P<dir>.*)$`, dirPointer)
|
matches := regex.FindNamedRegexMatch(`^gitdir: (?P<dir>.*)$`, dirPointer)
|
||||||
if matches != nil && matches["dir"] != "" {
|
if matches != nil && matches["dir"] != "" {
|
||||||
// if we open a worktree file in a shared wsl2 folder, we have to convert it back
|
// if we open a worktree file in a shared wsl2 folder, we have to convert it back
|
||||||
// to the mounted path
|
// to the mounted path
|
||||||
g.gitWorkingFolder = g.convertToLinuxPath(matches["dir"])
|
g.workingFolder = g.convertToLinuxPath(matches["dir"])
|
||||||
|
|
||||||
// in worktrees, the path looks like this: gitdir: path/.git/worktrees/branch
|
// in worktrees, the path looks like this: gitdir: path/.git/worktrees/branch
|
||||||
// strips the last .git/worktrees part
|
// strips the last .git/worktrees part
|
||||||
// :ind+5 = index + /.git
|
// :ind+5 = index + /.git
|
||||||
ind := strings.LastIndex(g.gitWorkingFolder, "/.git/worktrees")
|
ind := strings.LastIndex(g.workingFolder, "/.git/worktrees")
|
||||||
if ind > -1 {
|
if ind > -1 {
|
||||||
g.gitRootFolder = g.gitWorkingFolder[:ind+5]
|
g.rootFolder = g.workingFolder[:ind+5]
|
||||||
g.gitRealFolder = strings.TrimSuffix(g.env.FileContent(g.gitWorkingFolder+"/gitdir"), ".git\n")
|
g.realFolder = strings.TrimSuffix(g.env.FileContent(g.workingFolder+"/gitdir"), ".git\n")
|
||||||
g.IsWorkTree = true
|
g.IsWorkTree = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// in submodules, the path looks like this: gitdir: ../.git/modules/test-submodule
|
// in submodules, the path looks like this: gitdir: ../.git/modules/test-submodule
|
||||||
// we need the parent folder to detect where the real .git folder is
|
// we need the parent folder to detect where the real .git folder is
|
||||||
ind = strings.LastIndex(g.gitWorkingFolder, "/.git/modules")
|
ind = strings.LastIndex(g.workingFolder, "/.git/modules")
|
||||||
if ind > -1 {
|
if ind > -1 {
|
||||||
g.gitRootFolder = gitdir.ParentFolder + "/" + g.gitWorkingFolder
|
g.rootFolder = gitdir.ParentFolder + "/" + g.workingFolder
|
||||||
g.gitRealFolder = g.gitRootFolder
|
g.realFolder = g.rootFolder
|
||||||
g.gitWorkingFolder = g.gitRootFolder
|
g.workingFolder = g.rootFolder
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for separate git folder(--separate-git-dir)
|
// check for separate git folder(--separate-git-dir)
|
||||||
// check if the folder contains a HEAD file
|
// check if the folder contains a HEAD file
|
||||||
if g.env.HasFilesInDir(g.gitWorkingFolder, "HEAD") {
|
if g.env.HasFilesInDir(g.workingFolder, "HEAD") {
|
||||||
gitFolder := strings.TrimSuffix(g.gitRootFolder, ".git")
|
gitFolder := strings.TrimSuffix(g.rootFolder, ".git")
|
||||||
g.gitRootFolder = g.gitWorkingFolder
|
g.rootFolder = g.workingFolder
|
||||||
g.gitWorkingFolder = gitFolder
|
g.workingFolder = gitFolder
|
||||||
g.gitRealFolder = gitFolder
|
g.realFolder = gitFolder
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -304,20 +296,9 @@ func (g *Git) setGitStatus() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Git) getGitCommand() string {
|
|
||||||
if len(g.gitCommand) > 0 {
|
|
||||||
return g.gitCommand
|
|
||||||
}
|
|
||||||
g.gitCommand = "git"
|
|
||||||
if g.env.GOOS() == environment.WindowsPlatform || g.IsWslSharedPath {
|
|
||||||
g.gitCommand = "git.exe"
|
|
||||||
}
|
|
||||||
return g.gitCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Git) getGitCommandOutput(args ...string) string {
|
func (g *Git) getGitCommandOutput(args ...string) string {
|
||||||
args = append([]string{"-C", g.gitRealFolder, "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...)
|
args = append([]string{"-C", g.realFolder, "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...)
|
||||||
val, err := g.env.RunCommand(g.getGitCommand(), args...)
|
val, err := g.env.RunCommand(g.getCommand(GITCOMMAND), args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -342,7 +323,7 @@ func (g *Git) setGitHEADContext() {
|
||||||
|
|
||||||
getPrettyNameOrigin := func(file string) string {
|
getPrettyNameOrigin := func(file string) string {
|
||||||
var origin string
|
var origin string
|
||||||
head := g.FileContents(g.gitWorkingFolder, file)
|
head := g.FileContents(g.workingFolder, file)
|
||||||
if head == "detached HEAD" {
|
if head == "detached HEAD" {
|
||||||
origin = formatDetached()
|
origin = formatDetached()
|
||||||
} else {
|
} else {
|
||||||
|
@ -352,20 +333,20 @@ func (g *Git) setGitHEADContext() {
|
||||||
return origin
|
return origin
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.env.HasFolder(g.gitWorkingFolder + "/rebase-merge") {
|
if g.env.HasFolder(g.workingFolder + "/rebase-merge") {
|
||||||
origin := getPrettyNameOrigin("rebase-merge/head-name")
|
origin := getPrettyNameOrigin("rebase-merge/head-name")
|
||||||
onto := g.getGitRefFileSymbolicName("rebase-merge/onto")
|
onto := g.getGitRefFileSymbolicName("rebase-merge/onto")
|
||||||
onto = g.formatHEAD(onto)
|
onto = g.formatHEAD(onto)
|
||||||
step := g.FileContents(g.gitWorkingFolder, "rebase-merge/msgnum")
|
step := g.FileContents(g.workingFolder, "rebase-merge/msgnum")
|
||||||
total := g.FileContents(g.gitWorkingFolder, "rebase-merge/end")
|
total := g.FileContents(g.workingFolder, "rebase-merge/end")
|
||||||
icon := g.props.GetString(RebaseIcon, "\uE728 ")
|
icon := g.props.GetString(RebaseIcon, "\uE728 ")
|
||||||
g.HEAD = fmt.Sprintf("%s%s onto %s%s (%s/%s) at %s", icon, origin, branchIcon, onto, step, total, g.HEAD)
|
g.HEAD = fmt.Sprintf("%s%s onto %s%s (%s/%s) at %s", icon, origin, branchIcon, onto, step, total, g.HEAD)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if g.env.HasFolder(g.gitWorkingFolder + "/rebase-apply") {
|
if g.env.HasFolder(g.workingFolder + "/rebase-apply") {
|
||||||
origin := getPrettyNameOrigin("rebase-apply/head-name")
|
origin := getPrettyNameOrigin("rebase-apply/head-name")
|
||||||
step := g.FileContents(g.gitWorkingFolder, "rebase-apply/next")
|
step := g.FileContents(g.workingFolder, "rebase-apply/next")
|
||||||
total := g.FileContents(g.gitWorkingFolder, "rebase-apply/last")
|
total := g.FileContents(g.workingFolder, "rebase-apply/last")
|
||||||
icon := g.props.GetString(RebaseIcon, "\uE728 ")
|
icon := g.props.GetString(RebaseIcon, "\uE728 ")
|
||||||
g.HEAD = fmt.Sprintf("%s%s (%s/%s) at %s", icon, origin, step, total, g.HEAD)
|
g.HEAD = fmt.Sprintf("%s%s (%s/%s) at %s", icon, origin, step, total, g.HEAD)
|
||||||
return
|
return
|
||||||
|
@ -374,7 +355,7 @@ func (g *Git) setGitHEADContext() {
|
||||||
commitIcon := g.props.GetString(CommitIcon, "\uF417")
|
commitIcon := g.props.GetString(CommitIcon, "\uF417")
|
||||||
if g.hasGitFile("MERGE_MSG") {
|
if g.hasGitFile("MERGE_MSG") {
|
||||||
icon := g.props.GetString(MergeIcon, "\uE727 ")
|
icon := g.props.GetString(MergeIcon, "\uE727 ")
|
||||||
mergeContext := g.FileContents(g.gitWorkingFolder, "MERGE_MSG")
|
mergeContext := g.FileContents(g.workingFolder, "MERGE_MSG")
|
||||||
matches := regex.FindNamedRegexMatch(`Merge (remote-tracking )?(?P<type>branch|commit|tag) '(?P<theirs>.*)'`, mergeContext)
|
matches := regex.FindNamedRegexMatch(`Merge (remote-tracking )?(?P<type>branch|commit|tag) '(?P<theirs>.*)'`, mergeContext)
|
||||||
// head := g.getGitRefFileSymbolicName("ORIG_HEAD")
|
// head := g.getGitRefFileSymbolicName("ORIG_HEAD")
|
||||||
if matches != nil && matches["theirs"] != "" {
|
if matches != nil && matches["theirs"] != "" {
|
||||||
|
@ -400,19 +381,19 @@ func (g *Git) setGitHEADContext() {
|
||||||
// reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read
|
// reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read
|
||||||
// the todo file.
|
// the todo file.
|
||||||
if g.hasGitFile("CHERRY_PICK_HEAD") {
|
if g.hasGitFile("CHERRY_PICK_HEAD") {
|
||||||
sha := g.FileContents(g.gitWorkingFolder, "CHERRY_PICK_HEAD")
|
sha := g.FileContents(g.workingFolder, "CHERRY_PICK_HEAD")
|
||||||
cherry := g.props.GetString(CherryPickIcon, "\uE29B ")
|
cherry := g.props.GetString(CherryPickIcon, "\uE29B ")
|
||||||
g.HEAD = fmt.Sprintf("%s%s%s onto %s", cherry, commitIcon, g.formatSHA(sha), formatDetached())
|
g.HEAD = fmt.Sprintf("%s%s%s onto %s", cherry, commitIcon, g.formatSHA(sha), formatDetached())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if g.hasGitFile("REVERT_HEAD") {
|
if g.hasGitFile("REVERT_HEAD") {
|
||||||
sha := g.FileContents(g.gitWorkingFolder, "REVERT_HEAD")
|
sha := g.FileContents(g.workingFolder, "REVERT_HEAD")
|
||||||
revert := g.props.GetString(RevertIcon, "\uF0E2 ")
|
revert := g.props.GetString(RevertIcon, "\uF0E2 ")
|
||||||
g.HEAD = fmt.Sprintf("%s%s%s onto %s", revert, commitIcon, g.formatSHA(sha), formatDetached())
|
g.HEAD = fmt.Sprintf("%s%s%s onto %s", revert, commitIcon, g.formatSHA(sha), formatDetached())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if g.hasGitFile("sequencer/todo") {
|
if g.hasGitFile("sequencer/todo") {
|
||||||
todo := g.FileContents(g.gitWorkingFolder, "sequencer/todo")
|
todo := g.FileContents(g.workingFolder, "sequencer/todo")
|
||||||
matches := regex.FindNamedRegexMatch(`^(?P<action>p|pick|revert)\s+(?P<sha>\S+)`, todo)
|
matches := regex.FindNamedRegexMatch(`^(?P<action>p|pick|revert)\s+(?P<sha>\S+)`, todo)
|
||||||
if matches != nil && matches["sha"] != "" {
|
if matches != nil && matches["sha"] != "" {
|
||||||
action := matches["action"]
|
action := matches["action"]
|
||||||
|
@ -449,18 +430,18 @@ func (g *Git) formatSHA(sha string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Git) hasGitFile(file string) bool {
|
func (g *Git) hasGitFile(file string) bool {
|
||||||
return g.env.HasFilesInDir(g.gitWorkingFolder, file)
|
return g.env.HasFilesInDir(g.workingFolder, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Git) getGitRefFileSymbolicName(refFile string) string {
|
func (g *Git) getGitRefFileSymbolicName(refFile string) string {
|
||||||
ref := g.FileContents(g.gitWorkingFolder, refFile)
|
ref := g.FileContents(g.workingFolder, refFile)
|
||||||
return g.getGitCommandOutput("name-rev", "--name-only", "--exclude=tags/*", ref)
|
return g.getGitCommandOutput("name-rev", "--name-only", "--exclude=tags/*", ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Git) setPrettyHEADName() {
|
func (g *Git) setPrettyHEADName() {
|
||||||
// we didn't fetch status, fallback to parsing the HEAD file
|
// we didn't fetch status, fallback to parsing the HEAD file
|
||||||
if len(g.Hash) == 0 {
|
if len(g.Hash) == 0 {
|
||||||
HEADRef := g.FileContents(g.gitWorkingFolder, "HEAD")
|
HEADRef := g.FileContents(g.workingFolder, "HEAD")
|
||||||
if strings.HasPrefix(HEADRef, BRANCHPREFIX) {
|
if strings.HasPrefix(HEADRef, BRANCHPREFIX) {
|
||||||
branchName := strings.TrimPrefix(HEADRef, BRANCHPREFIX)
|
branchName := strings.TrimPrefix(HEADRef, BRANCHPREFIX)
|
||||||
g.HEAD = fmt.Sprintf("%s%s", g.props.GetString(BranchIcon, "\uE0A0"), g.formatHEAD(branchName))
|
g.HEAD = fmt.Sprintf("%s%s", g.props.GetString(BranchIcon, "\uE0A0"), g.formatHEAD(branchName))
|
||||||
|
@ -486,7 +467,7 @@ func (g *Git) setPrettyHEADName() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Git) getStashContext() int {
|
func (g *Git) getStashContext() int {
|
||||||
stashContent := g.FileContents(g.gitRootFolder, "logs/refs/stash")
|
stashContent := g.FileContents(g.rootFolder, "logs/refs/stash")
|
||||||
if stashContent == "" {
|
if stashContent == "" {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -495,10 +476,10 @@ func (g *Git) getStashContext() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Git) getWorktreeContext() int {
|
func (g *Git) getWorktreeContext() int {
|
||||||
if !g.env.HasFolder(g.gitRootFolder + "/worktrees") {
|
if !g.env.HasFolder(g.rootFolder + "/worktrees") {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
worktreeFolders := g.env.LsDir(g.gitRootFolder + "/worktrees")
|
worktreeFolders := g.env.LsDir(g.rootFolder + "/worktrees")
|
||||||
var count int
|
var count int
|
||||||
for _, folder := range worktreeFolders {
|
for _, folder := range worktreeFolders {
|
||||||
if folder.IsDir() {
|
if folder.IsDir() {
|
||||||
|
@ -520,7 +501,7 @@ func (g *Git) getOriginURL(upstream string) string {
|
||||||
return fmt.Sprintf("https://%s", url)
|
return fmt.Sprintf("https://%s", url)
|
||||||
}
|
}
|
||||||
var url string
|
var url string
|
||||||
cfg, err := ini.Load(g.gitRootFolder + "/config")
|
cfg, err := ini.Load(g.rootFolder + "/config")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
url = g.getGitCommandOutput("remote", "get-url", upstream)
|
url = g.getGitCommandOutput("remote", "get-url", upstream)
|
||||||
return cleanSSHURL(url)
|
return cleanSSHURL(url)
|
||||||
|
@ -532,24 +513,10 @@ func (g *Git) getOriginURL(upstream string) string {
|
||||||
return cleanSSHURL(url)
|
return cleanSSHURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Git) convertToWindowsPath(path string) string {
|
|
||||||
if !g.IsWslSharedPath {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
return g.env.ConvertToWindowsPath(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Git) convertToLinuxPath(path string) string {
|
|
||||||
if !g.IsWslSharedPath {
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
return g.env.ConvertToLinuxPath(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Git) getUntrackedFilesMode() string {
|
func (g *Git) getUntrackedFilesMode() string {
|
||||||
mode := "normal"
|
mode := "normal"
|
||||||
repoModes := g.props.GetKeyValueMap(UntrackedModes, map[string]string{})
|
repoModes := g.props.GetKeyValueMap(UntrackedModes, map[string]string{})
|
||||||
if val := repoModes[g.gitRealFolder]; len(val) != 0 {
|
if val := repoModes[g.realFolder]; len(val) != 0 {
|
||||||
mode = val
|
mode = val
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("-u%s", mode)
|
return fmt.Sprintf("-u%s", mode)
|
||||||
|
|
|
@ -51,7 +51,7 @@ func TestEnabledInWorkingDirectory(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.True(t, g.Enabled())
|
assert.True(t, g.Enabled())
|
||||||
assert.Equal(t, fileInfo.Path, g.gitWorkingFolder)
|
assert.Equal(t, fileInfo.Path, g.workingFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnabledInWorkingTree(t *testing.T) {
|
func TestEnabledInWorkingTree(t *testing.T) {
|
||||||
|
@ -77,8 +77,8 @@ func TestEnabledInWorkingTree(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.True(t, g.Enabled())
|
assert.True(t, g.Enabled())
|
||||||
assert.Equal(t, "/dev/real_folder/.git/worktrees/folder_worktree", g.gitWorkingFolder)
|
assert.Equal(t, "/dev/real_folder/.git/worktrees/folder_worktree", g.workingFolder)
|
||||||
assert.Equal(t, "/dev/folder_worktree", g.gitRealFolder)
|
assert.Equal(t, "/dev/folder_worktree", g.realFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnabledInSubmodule(t *testing.T) {
|
func TestEnabledInSubmodule(t *testing.T) {
|
||||||
|
@ -104,9 +104,9 @@ func TestEnabledInSubmodule(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.True(t, g.Enabled())
|
assert.True(t, g.Enabled())
|
||||||
assert.Equal(t, "/dev/parent/test-submodule/../.git/modules/test-submodule", g.gitWorkingFolder)
|
assert.Equal(t, "/dev/parent/test-submodule/../.git/modules/test-submodule", g.workingFolder)
|
||||||
assert.Equal(t, "/dev/parent/test-submodule/../.git/modules/test-submodule", g.gitRealFolder)
|
assert.Equal(t, "/dev/parent/test-submodule/../.git/modules/test-submodule", g.realFolder)
|
||||||
assert.Equal(t, "/dev/parent/test-submodule/../.git/modules/test-submodule", g.gitRootFolder)
|
assert.Equal(t, "/dev/parent/test-submodule/../.git/modules/test-submodule", g.rootFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnabledInSeparateGitDir(t *testing.T) {
|
func TestEnabledInSeparateGitDir(t *testing.T) {
|
||||||
|
@ -132,9 +132,9 @@ func TestEnabledInSeparateGitDir(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.True(t, g.Enabled())
|
assert.True(t, g.Enabled())
|
||||||
assert.Equal(t, "/dev/parent/test-separate-git-dir/", g.gitWorkingFolder)
|
assert.Equal(t, "/dev/parent/test-separate-git-dir/", g.workingFolder)
|
||||||
assert.Equal(t, "/dev/parent/test-separate-git-dir/", g.gitRealFolder)
|
assert.Equal(t, "/dev/parent/test-separate-git-dir/", g.realFolder)
|
||||||
assert.Equal(t, "/dev/separate-git-dir", g.gitRootFolder)
|
assert.Equal(t, "/dev/separate-git-dir", g.rootFolder)
|
||||||
}
|
}
|
||||||
func TestGetGitOutputForCommand(t *testing.T) {
|
func TestGetGitOutputForCommand(t *testing.T) {
|
||||||
args := []string{"-C", "", "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}
|
args := []string{"-C", "", "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}
|
||||||
|
@ -501,9 +501,9 @@ func TestGetStashContextZeroEntries(t *testing.T) {
|
||||||
env.On("FileContent", "/logs/refs/stash").Return(tc.StashContent)
|
env.On("FileContent", "/logs/refs/stash").Return(tc.StashContent)
|
||||||
g := &Git{
|
g := &Git{
|
||||||
scm: scm{
|
scm: scm{
|
||||||
env: env,
|
env: env,
|
||||||
|
workingFolder: "",
|
||||||
},
|
},
|
||||||
gitWorkingFolder: "",
|
|
||||||
}
|
}
|
||||||
got := g.getStashContext()
|
got := g.getStashContext()
|
||||||
assert.Equal(t, tc.Expected, got)
|
assert.Equal(t, tc.Expected, got)
|
||||||
|
@ -625,7 +625,7 @@ func TestGetGitCommand(t *testing.T) {
|
||||||
} else {
|
} else {
|
||||||
env.On("InWSLSharedDrive").Return(false)
|
env.On("InWSLSharedDrive").Return(false)
|
||||||
}
|
}
|
||||||
assert.Equal(t, tc.Expected, g.getGitCommand(), tc.Case)
|
assert.Equal(t, tc.Expected, g.getCommand(GITCOMMAND), tc.Case)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -796,8 +796,8 @@ func TestGitUntrackedMode(t *testing.T) {
|
||||||
props: properties.Map{
|
props: properties.Map{
|
||||||
UntrackedModes: tc.UntrackedModes,
|
UntrackedModes: tc.UntrackedModes,
|
||||||
},
|
},
|
||||||
|
realFolder: "foo",
|
||||||
},
|
},
|
||||||
gitRealFolder: "foo",
|
|
||||||
}
|
}
|
||||||
got := g.getUntrackedFilesMode()
|
got := g.getUntrackedFilesMode()
|
||||||
assert.Equal(t, tc.Expected, got, tc.Case)
|
assert.Equal(t, tc.Expected, got, tc.Case)
|
||||||
|
|
|
@ -9,15 +9,16 @@ import (
|
||||||
|
|
||||||
// ScmStatus represents part of the status of a repository
|
// ScmStatus represents part of the status of a repository
|
||||||
type ScmStatus struct {
|
type ScmStatus struct {
|
||||||
Unmerged int
|
Unmerged int
|
||||||
Deleted int
|
Deleted int
|
||||||
Added int
|
Added int
|
||||||
Modified int
|
Modified int
|
||||||
Moved int
|
Moved int
|
||||||
|
Conflicted int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ScmStatus) Changed() bool {
|
func (s *ScmStatus) Changed() bool {
|
||||||
return s.Added > 0 || s.Deleted > 0 || s.Modified > 0 || s.Unmerged > 0 || s.Moved > 0
|
return s.Added > 0 || s.Deleted > 0 || s.Modified > 0 || s.Unmerged > 0 || s.Moved > 0 || s.Conflicted > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ScmStatus) String() string {
|
func (s *ScmStatus) String() string {
|
||||||
|
@ -33,12 +34,19 @@ func (s *ScmStatus) String() string {
|
||||||
status += stringIfValue(s.Deleted, "-")
|
status += stringIfValue(s.Deleted, "-")
|
||||||
status += stringIfValue(s.Moved, ">")
|
status += stringIfValue(s.Moved, ">")
|
||||||
status += stringIfValue(s.Unmerged, "x")
|
status += stringIfValue(s.Unmerged, "x")
|
||||||
|
status += stringIfValue(s.Conflicted, "!")
|
||||||
return strings.TrimSpace(status)
|
return strings.TrimSpace(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
type scm struct {
|
type scm struct {
|
||||||
props properties.Properties
|
props properties.Properties
|
||||||
env environment.Environment
|
env environment.Environment
|
||||||
|
|
||||||
|
IsWslSharedPath bool
|
||||||
|
workingFolder string
|
||||||
|
rootFolder string
|
||||||
|
realFolder string // real folder (can be different from current path when in worktrees)
|
||||||
|
command string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -80,3 +88,28 @@ func (s *scm) shouldIgnoreRootRepository(rootDir string) bool {
|
||||||
func (s *scm) FileContents(folder, file string) string {
|
func (s *scm) FileContents(folder, file string) string {
|
||||||
return strings.Trim(s.env.FileContent(folder+"/"+file), " \r\n")
|
return strings.Trim(s.env.FileContent(folder+"/"+file), " \r\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *scm) convertToWindowsPath(path string) string {
|
||||||
|
if !s.IsWslSharedPath {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return s.env.ConvertToWindowsPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scm) convertToLinuxPath(path string) string {
|
||||||
|
if !s.IsWslSharedPath {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return s.env.ConvertToLinuxPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scm) getCommand(command string) string {
|
||||||
|
if len(s.command) > 0 {
|
||||||
|
return s.command
|
||||||
|
}
|
||||||
|
s.command = command
|
||||||
|
if s.env.GOOS() == environment.WindowsPlatform || s.IsWslSharedPath {
|
||||||
|
s.command += ".exe"
|
||||||
|
}
|
||||||
|
return s.command
|
||||||
|
}
|
||||||
|
|
130
src/segments/svn.go
Normal file
130
src/segments/svn.go
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
package segments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"oh-my-posh/regex"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SvnStatus represents part of the status of a Svn repository
|
||||||
|
type SvnStatus struct {
|
||||||
|
ScmStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SvnStatus) add(code string) {
|
||||||
|
switch code {
|
||||||
|
case "C":
|
||||||
|
s.Conflicted++
|
||||||
|
case "D":
|
||||||
|
s.Deleted++
|
||||||
|
case "A":
|
||||||
|
s.Added++
|
||||||
|
case "M":
|
||||||
|
s.Modified++
|
||||||
|
case "R":
|
||||||
|
s.Moved++
|
||||||
|
default:
|
||||||
|
s.Unmerged++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SvnStatus) HasConflicts() bool {
|
||||||
|
return s.Conflicted > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SVNCOMMAND = "svn"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Svn struct {
|
||||||
|
scm
|
||||||
|
|
||||||
|
Working *SvnStatus
|
||||||
|
BaseRev int
|
||||||
|
Branch string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Svn) Template() string {
|
||||||
|
return " \ue0a0{{.Branch}} r{{.BaseRev}} {{.Working.String}} "
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Svn) Enabled() bool {
|
||||||
|
if !s.shouldDisplay() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
displayStatus := s.props.GetBool(FetchStatus, false)
|
||||||
|
if displayStatus {
|
||||||
|
s.setSvnStatus()
|
||||||
|
} else {
|
||||||
|
s.Working = &SvnStatus{}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Svn) shouldDisplay() bool {
|
||||||
|
// when in wsl/wsl2 and in a windows shared folder
|
||||||
|
// we must use Svn.exe and convert paths accordingly
|
||||||
|
// for worktrees, stashes, and path to work
|
||||||
|
s.IsWslSharedPath = s.env.InWSLSharedDrive()
|
||||||
|
if !s.env.HasCommand(s.getCommand(SVNCOMMAND)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Svndir, err := s.env.HasParentFilePath(".svn")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s.shouldIgnoreRootRepository(Svndir.ParentFolder) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if Svndir.IsDir {
|
||||||
|
s.workingFolder = Svndir.Path
|
||||||
|
s.rootFolder = Svndir.Path
|
||||||
|
// convert the worktree file path to a windows one when in wsl 2 shared folder
|
||||||
|
s.realFolder = strings.TrimSuffix(s.convertToWindowsPath(Svndir.Path), ".svn")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// handle worktree
|
||||||
|
s.rootFolder = Svndir.Path
|
||||||
|
dirPointer := strings.Trim(s.env.FileContent(Svndir.Path), " \r\n")
|
||||||
|
matches := regex.FindNamedRegexMatch(`^Svndir: (?P<dir>.*)$`, dirPointer)
|
||||||
|
if matches != nil && matches["dir"] != "" {
|
||||||
|
// if we open a worktree file in a shared wsl2 folder, we have to convert it back
|
||||||
|
// to the mounted path
|
||||||
|
s.workingFolder = s.convertToLinuxPath(matches["dir"])
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Svn) setSvnStatus() {
|
||||||
|
s.BaseRev, _ = strconv.Atoi(s.getSvnCommandOutput("info", "--show-item", "revision"))
|
||||||
|
|
||||||
|
branch := s.getSvnCommandOutput("info", "--show-item", "relative-url")
|
||||||
|
if len(branch) > 2 {
|
||||||
|
s.Branch = branch[2:]
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Working = &SvnStatus{}
|
||||||
|
changes := s.getSvnCommandOutput("status")
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lines := strings.Split(changes, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if len(line) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// element is the element from someSlice for where we are
|
||||||
|
s.Working.add(line[0:1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Svn) getSvnCommandOutput(command string, args ...string) string {
|
||||||
|
args = append([]string{command, s.realFolder}, args...)
|
||||||
|
val, err := s.env.RunCommand(s.getCommand(SVNCOMMAND), args...)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(val)
|
||||||
|
}
|
251
src/segments/svn_test.go
Normal file
251
src/segments/svn_test.go
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
package segments
|
||||||
|
|
||||||
|
import (
|
||||||
|
"oh-my-posh/environment"
|
||||||
|
"oh-my-posh/mock"
|
||||||
|
"oh-my-posh/properties"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSvnEnabledToolNotFound(t *testing.T) {
|
||||||
|
env := new(mock.MockedEnvironment)
|
||||||
|
env.On("InWSLSharedDrive").Return(false)
|
||||||
|
env.On("HasCommand", "svn").Return(false)
|
||||||
|
env.On("GOOS").Return("")
|
||||||
|
env.On("IsWsl").Return(false)
|
||||||
|
s := &Svn{
|
||||||
|
scm: scm{
|
||||||
|
env: env,
|
||||||
|
props: properties.Map{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.False(t, s.Enabled())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSvnEnabledInWorkingDirectory(t *testing.T) {
|
||||||
|
fileInfo := &environment.FileInfo{
|
||||||
|
Path: "/dir/hello",
|
||||||
|
ParentFolder: "/dir",
|
||||||
|
IsDir: true,
|
||||||
|
}
|
||||||
|
env := new(mock.MockedEnvironment)
|
||||||
|
env.On("InWSLSharedDrive").Return(false)
|
||||||
|
env.On("HasCommand", "svn").Return(true)
|
||||||
|
env.On("GOOS").Return("")
|
||||||
|
env.On("FileContent", "/dir/hello/trunk").Return("")
|
||||||
|
env.MockSvnCommand(fileInfo.Path, "", "info", "--tags", "--exact-match")
|
||||||
|
env.On("IsWsl").Return(false)
|
||||||
|
env.On("HasParentFilePath", ".svn").Return(fileInfo, nil)
|
||||||
|
s := &Svn{
|
||||||
|
scm: scm{
|
||||||
|
env: env,
|
||||||
|
props: properties.Map{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.True(t, s.Enabled())
|
||||||
|
assert.Equal(t, fileInfo.Path, s.workingFolder)
|
||||||
|
assert.Equal(t, fileInfo.Path, s.realFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSvnTemplateString(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Case string
|
||||||
|
Expected string
|
||||||
|
Template string
|
||||||
|
Svn *Svn
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Case: "Default template",
|
||||||
|
Expected: "\ue0a0trunk r2 +2 ~3 -7 >13 x5 !1",
|
||||||
|
Template: " \ue0a0{{.Branch}} r{{.BaseRev}} {{.Working.String}} ",
|
||||||
|
Svn: &Svn{
|
||||||
|
Branch: "trunk",
|
||||||
|
BaseRev: 2,
|
||||||
|
Working: &SvnStatus{
|
||||||
|
ScmStatus: ScmStatus{
|
||||||
|
Added: 2,
|
||||||
|
Conflicted: 1,
|
||||||
|
Deleted: 7,
|
||||||
|
Modified: 3,
|
||||||
|
Moved: 13,
|
||||||
|
Unmerged: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "Only Branch name",
|
||||||
|
Expected: "trunk",
|
||||||
|
Template: "{{ .Branch }}",
|
||||||
|
Svn: &Svn{
|
||||||
|
Branch: "trunk",
|
||||||
|
BaseRev: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "Working area changes",
|
||||||
|
Expected: "trunk \uF044 +2 ~3",
|
||||||
|
Template: "{{ .Branch }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}",
|
||||||
|
Svn: &Svn{
|
||||||
|
Branch: "trunk",
|
||||||
|
Working: &SvnStatus{
|
||||||
|
ScmStatus: ScmStatus{
|
||||||
|
Added: 2,
|
||||||
|
Modified: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "No working area changes (using changed flag)",
|
||||||
|
Expected: "trunk",
|
||||||
|
Template: "{{ .Branch }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}",
|
||||||
|
Svn: &Svn{
|
||||||
|
Branch: "trunk",
|
||||||
|
Working: &SvnStatus{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "No working area changes",
|
||||||
|
Expected: "trunk",
|
||||||
|
Template: "{{ .Branch }}{{ .Working.String }}",
|
||||||
|
Svn: &Svn{
|
||||||
|
Branch: "trunk",
|
||||||
|
Working: &SvnStatus{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "Base revision with Working changes",
|
||||||
|
Expected: "trunk - 2 \uF044 +2 ~3",
|
||||||
|
Template: "{{ .Branch }} - {{ .BaseRev }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}",
|
||||||
|
Svn: &Svn{
|
||||||
|
Branch: "trunk",
|
||||||
|
BaseRev: 2,
|
||||||
|
Working: &SvnStatus{
|
||||||
|
ScmStatus: ScmStatus{
|
||||||
|
Added: 2,
|
||||||
|
Modified: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "Working and staging area changes with separator and stash count",
|
||||||
|
Expected: "trunk CONFLICTED \uF044 +2 ~3 !7",
|
||||||
|
Template: "{{ .Branch }}{{ if .Working.HasConflicts }} CONFLICTED{{ end }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}",
|
||||||
|
Svn: &Svn{
|
||||||
|
Branch: "trunk",
|
||||||
|
BaseRev: 2,
|
||||||
|
Working: &SvnStatus{
|
||||||
|
ScmStatus: ScmStatus{
|
||||||
|
Added: 2,
|
||||||
|
Modified: 3,
|
||||||
|
Conflicted: 7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
props := properties.Map{
|
||||||
|
FetchStatus: true,
|
||||||
|
}
|
||||||
|
env := new(mock.MockedEnvironment)
|
||||||
|
tc.Svn.env = env
|
||||||
|
tc.Svn.props = props
|
||||||
|
assert.Equal(t, tc.Expected, renderTemplate(env, tc.Template, tc.Svn), tc.Case)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetSvnStatus(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Case string
|
||||||
|
StatusOutput string
|
||||||
|
RefOutput string
|
||||||
|
BranchOutput string
|
||||||
|
ExpectedWorking *SvnStatus
|
||||||
|
ExpectedBranch string
|
||||||
|
ExpectedRef int
|
||||||
|
ExpectedConflicts bool
|
||||||
|
ExpectedChanged bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Case: "changed",
|
||||||
|
StatusOutput: `
|
||||||
|
! Untracked.File
|
||||||
|
A FileHasBeen.Added
|
||||||
|
D FileMarkedAs.Deleted
|
||||||
|
M Modified.File
|
||||||
|
R Moved.File`,
|
||||||
|
ExpectedWorking: &SvnStatus{ScmStatus: ScmStatus{
|
||||||
|
Modified: 1,
|
||||||
|
Added: 1,
|
||||||
|
Deleted: 1,
|
||||||
|
Unmerged: 1,
|
||||||
|
Moved: 1,
|
||||||
|
}},
|
||||||
|
RefOutput: "1133",
|
||||||
|
ExpectedRef: 1133,
|
||||||
|
BranchOutput: "^/trunk",
|
||||||
|
ExpectedBranch: "trunk",
|
||||||
|
ExpectedChanged: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "conflict",
|
||||||
|
StatusOutput: `C build.cake`,
|
||||||
|
ExpectedWorking: &SvnStatus{ScmStatus: ScmStatus{
|
||||||
|
Conflicted: 1,
|
||||||
|
}},
|
||||||
|
ExpectedChanged: true,
|
||||||
|
ExpectedConflicts: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "no change",
|
||||||
|
ExpectedWorking: &SvnStatus{ScmStatus: ScmStatus{}},
|
||||||
|
ExpectedChanged: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Case: "not an integer ref",
|
||||||
|
ExpectedWorking: &SvnStatus{ScmStatus: ScmStatus{}},
|
||||||
|
ExpectedChanged: false,
|
||||||
|
RefOutput: "not an integer",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
fileInfo := &environment.FileInfo{
|
||||||
|
Path: "/dir/hello",
|
||||||
|
ParentFolder: "/dir",
|
||||||
|
IsDir: true,
|
||||||
|
}
|
||||||
|
env := new(mock.MockedEnvironment)
|
||||||
|
env.On("InWSLSharedDrive").Return(false)
|
||||||
|
env.On("IsWsl").Return(false)
|
||||||
|
env.On("HasCommand", "svn").Return(true)
|
||||||
|
env.On("GOOS").Return("")
|
||||||
|
env.On("FileContent", "/dir/hello/trunk").Return("")
|
||||||
|
env.MockSvnCommand(fileInfo.Path, "", "info", "--tags", "--exact-match")
|
||||||
|
env.On("HasParentFilePath", ".svn").Return(fileInfo, nil)
|
||||||
|
env.On("RunCommand", "svn", []string{"info", "", "--show-item", "revision"}).Return(tc.RefOutput, nil)
|
||||||
|
env.On("RunCommand", "svn", []string{"info", "", "--show-item", "relative-url"}).Return(tc.BranchOutput, nil)
|
||||||
|
env.On("RunCommand", "svn", []string{"status", ""}).Return(tc.StatusOutput, nil)
|
||||||
|
|
||||||
|
s := &Svn{
|
||||||
|
scm: scm{
|
||||||
|
env: env,
|
||||||
|
props: properties.Map{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s.setSvnStatus()
|
||||||
|
if tc.ExpectedWorking == nil {
|
||||||
|
tc.ExpectedWorking = &SvnStatus{}
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.ExpectedWorking, s.Working, tc.Case)
|
||||||
|
assert.Equal(t, tc.ExpectedRef, s.BaseRev, tc.Case)
|
||||||
|
assert.Equal(t, tc.ExpectedBranch, s.Branch, tc.Case)
|
||||||
|
assert.Equal(t, tc.ExpectedChanged, s.Working.Changed(), tc.Case)
|
||||||
|
assert.Equal(t, tc.ExpectedConflicts, s.Working.HasConflicts(), tc.Case)
|
||||||
|
}
|
||||||
|
}
|
|
@ -110,7 +110,7 @@ You can set the following properties to `true` to enable fetching additional inf
|
||||||
|
|
||||||
### Properties
|
### Properties
|
||||||
|
|
||||||
- `.Working`: `GitStatus` - changes in the working tree (see below)
|
- `.Working`: `GitStatus` - changes in the worktree (see below)
|
||||||
- `.Staging`: `GitStatus` - staged changes in the work tree (see below)
|
- `.Staging`: `GitStatus` - staged changes in the work tree (see below)
|
||||||
- `.HEAD`: `string` - the current HEAD context (branch/rebase/merge/...)
|
- `.HEAD`: `string` - the current HEAD context (branch/rebase/merge/...)
|
||||||
- `.Behind`: `int` - commits behind of upstream
|
- `.Behind`: `int` - commits behind of upstream
|
||||||
|
|
76
website/docs/segments/svn.md
Normal file
76
website/docs/segments/svn.md
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
---
|
||||||
|
id: svn
|
||||||
|
title: Svn
|
||||||
|
sidebar_label: Svn
|
||||||
|
---
|
||||||
|
|
||||||
|
## What
|
||||||
|
|
||||||
|
Display svn information when in a svn repository. Also works for subfolders. For maximum compatibility,
|
||||||
|
make sure your `svn` executable is up-to-date (when branch or status information is incorrect for example).
|
||||||
|
|
||||||
|
Local changes can also be displayed which uses the following syntax:
|
||||||
|
|
||||||
|
- `+` added
|
||||||
|
- `!` conflicted
|
||||||
|
- `-` deleted
|
||||||
|
- `~` modified
|
||||||
|
- `>` moved
|
||||||
|
- `?` untracked
|
||||||
|
|
||||||
|
## Sample Configuration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "svn",
|
||||||
|
"style": "powerline",
|
||||||
|
"powerline_symbol": "\uE0B0",
|
||||||
|
"foreground": "#193549",
|
||||||
|
"background": "#ffeb3b",
|
||||||
|
"properties": {
|
||||||
|
"fetch_status": true,
|
||||||
|
"fetch_stash_count": true,
|
||||||
|
"fetch_upstream_icon": true,
|
||||||
|
"template": " \ue0a0{{ .Branch }} r{{ .BaseRev }} {{ .Working.String }} "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### Fetching information
|
||||||
|
|
||||||
|
As doing multiple svn calls can slow down the prompt experience, we do not fetch information by default.
|
||||||
|
You can set the following properties to `true` to enable fetching additional information (and populate the template).
|
||||||
|
|
||||||
|
- fetch_status: `boolean` - fetch the local changes - defaults to `false`
|
||||||
|
|
||||||
|
## Template ([info][templates])
|
||||||
|
|
||||||
|
:::note default template
|
||||||
|
|
||||||
|
``` template
|
||||||
|
\ue0a0{{.Branch}} r{{.BaseRev}} {{.Working.String}}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
- `.Working`: `SvnStatus` - changes in the worktree (see below)
|
||||||
|
- `.Branch`: `string` - current branch (releative URL reported by `svn info`)
|
||||||
|
- `.BaseRev`: `int` - the currently checked out revision number
|
||||||
|
|
||||||
|
### SvnStatus
|
||||||
|
|
||||||
|
- `.Untracked`: `int` - number of untracked files
|
||||||
|
- `.Deleted`: `int` - number of deleted files
|
||||||
|
- `.Added`: `int` - number of added files
|
||||||
|
- `.Tracked`: `int` - number of changed tracked files
|
||||||
|
- `.Conflicted`: `int` - number of changed tracked files with conflicts
|
||||||
|
- `.Changed`: `boolean` - if the status contains changes or not
|
||||||
|
- `.HasConflicts`: `boolean` - if the status contains conflicts or not
|
||||||
|
- `.String`: `string` - a string representation of the changes above
|
||||||
|
|
||||||
|
[templates]: /docs/config-templates
|
||||||
|
[hyperlinks]: /docs/config-templates#helper-functions
|
Loading…
Reference in a new issue