1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-06 03:53:59 +02:00

add gone branches status

This commit is contained in:
Jesse Duffield 2022-03-24 17:49:25 +11:00
parent 13a9bbb984
commit dde30fa104
8 changed files with 121 additions and 52 deletions

View File

@ -106,6 +106,49 @@ outer:
return branches, nil return branches, nil
} }
// Obtain branch information from parsed line output of getRawBranches()
// split contains the '|' separated tokens in the line of output
func obtainBranch(split []string) *models.Branch {
name := strings.TrimPrefix(split[1], "heads/")
branch := &models.Branch{
Name: name,
Pullables: "?",
Pushables: "?",
Head: split[0] == "*",
}
upstreamName := split[2]
if upstreamName == "" {
// if we're here then it means we do not have a local version of the remote.
// The branch might still be tracking a remote though, we just don't know
// how many commits ahead/behind it is
return branch
}
track := split[3]
if track == "[gone]" {
branch.UpstreamGone = true
} else {
re := regexp.MustCompile(`ahead (\d+)`)
match := re.FindStringSubmatch(track)
if len(match) > 1 {
branch.Pushables = match[1]
} else {
branch.Pushables = "0"
}
re = regexp.MustCompile(`behind (\d+)`)
match = re.FindStringSubmatch(track)
if len(match) > 1 {
branch.Pullables = match[1]
} else {
branch.Pullables = "0"
}
}
return branch
}
func (self *BranchLoader) obtainBranches() []*models.Branch { func (self *BranchLoader) obtainBranches() []*models.Branch {
output, err := self.getRawBranches() output, err := self.getRawBranches()
if err != nil { if err != nil {
@ -128,40 +171,7 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
continue continue
} }
name := strings.TrimPrefix(split[1], "heads/") branch := obtainBranch(split)
branch := &models.Branch{
Name: name,
Pullables: "?",
Pushables: "?",
Head: split[0] == "*",
}
upstreamName := split[2]
if upstreamName == "" {
// if we're here then it means we do not have a local version of the remote.
// The branch might still be tracking a remote though, we just don't know
// how many commits ahead/behind it is
branches = append(branches, branch)
continue
}
track := split[3]
re := regexp.MustCompile(`ahead (\d+)`)
match := re.FindStringSubmatch(track)
if len(match) > 1 {
branch.Pushables = match[1]
} else {
branch.Pushables = "0"
}
re = regexp.MustCompile(`behind (\d+)`)
match = re.FindStringSubmatch(track)
if len(match) > 1 {
branch.Pullables = match[1]
} else {
branch.Pullables = "0"
}
branches = append(branches, branch) branches = append(branches, branch)
} }

View File

@ -0,0 +1,52 @@
package loaders
// "*|feat/detect-purge|origin/feat/detect-purge|[ahead 1]"
import (
"testing"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/stretchr/testify/assert"
)
func TestObtainBanch(t *testing.T) {
type scenario struct {
testName string
input []string
expectedBranch *models.Branch
}
scenarios := []scenario{
{
testName: "TrimHeads",
input: []string{"", "heads/a_branch", "", ""},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: false},
},
{
testName: "NoUpstream",
input: []string{"", "a_branch", "", ""},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: false},
},
{
testName: "IsHead",
input: []string{"*", "a_branch", "", ""},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "?", Pullables: "?", Head: true},
},
{
testName: "IsBehindAndAhead",
input: []string{"", "a_branch", "a_remote/a_branch", "[behind 2, ahead 3]"},
expectedBranch: &models.Branch{Name: "a_branch", Pushables: "3", Pullables: "2", Head: false},
},
{
testName: "RemoteBranchIsGone",
input: []string{"", "a_branch", "a_remote/a_branch", "[gone]"},
expectedBranch: &models.Branch{Name: "a_branch", UpstreamGone: true, Pushables: "?", Pullables: "?", Head: false},
},
}
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
branch := obtainBranch(s.input)
assert.EqualValues(t, s.expectedBranch, branch)
})
}
}

View File

@ -5,11 +5,12 @@ package models
type Branch struct { type Branch struct {
Name string Name string
// the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf' // the displayname is something like '(HEAD detached at 123asdf)', whereas in that case the name would be '123asdf'
DisplayName string DisplayName string
Recency string Recency string
Pushables string Pushables string
Pullables string Pullables string
Head bool UpstreamGone bool
Head bool
// if we have a named remote locally this will be the name of that remote e.g. // if we have a named remote locally this will be the name of that remote e.g.
// 'origin' or 'tiwood'. If we don't have the remote locally it'll look like // 'origin' or 'tiwood'. If we don't have the remote locally it'll look like
// 'git@github.com:tiwood/lazygit.git' // 'git@github.com:tiwood/lazygit.git'

View File

@ -47,7 +47,7 @@ func (gui *Gui) branchesListContext() *context.BranchesContext {
func() []*models.Branch { return gui.State.Model.Branches }, func() []*models.Branch { return gui.State.Model.Branches },
gui.Views.Branches, gui.Views.Branches,
func(startIdx int, length int) [][]string { func(startIdx int, length int) [][]string {
return presentation.GetBranchListDisplayStrings(gui.State.Model.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref) return presentation.GetBranchListDisplayStrings(gui.State.Model.Branches, gui.State.ScreenMode != SCREEN_NORMAL, gui.State.Modes.Diffing.Ref, gui.Tr)
}, },
nil, nil,
OnFocusWrapper(gui.withDiffModeCheck(gui.branchesRenderToMain)), OnFocusWrapper(gui.withDiffModeCheck(gui.branchesRenderToMain)),

View File

@ -6,25 +6,26 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
var branchPrefixColorCache = make(map[string]style.TextStyle) var branchPrefixColorCache = make(map[string]style.TextStyle)
func GetBranchListDisplayStrings(branches []*models.Branch, fullDescription bool, diffName string) [][]string { func GetBranchListDisplayStrings(branches []*models.Branch, fullDescription bool, diffName string, tr *i18n.TranslationSet) [][]string {
lines := make([][]string, len(branches)) lines := make([][]string, len(branches))
for i := range branches { for i := range branches {
diffed := branches[i].Name == diffName diffed := branches[i].Name == diffName
lines[i] = getBranchDisplayStrings(branches[i], fullDescription, diffed) lines[i] = getBranchDisplayStrings(branches[i], fullDescription, diffed, tr)
} }
return lines return lines
} }
// getBranchDisplayStrings returns the display string of branch // getBranchDisplayStrings returns the display string of branch
func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool) []string { func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool, tr *i18n.TranslationSet) []string {
displayName := b.Name displayName := b.Name
if b.DisplayName != "" { if b.DisplayName != "" {
displayName = b.DisplayName displayName = b.DisplayName
@ -36,7 +37,7 @@ func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool
} }
coloredName := nameTextStyle.Sprint(displayName) coloredName := nameTextStyle.Sprint(displayName)
if b.IsTrackingRemote() { if b.IsTrackingRemote() {
coloredName = fmt.Sprintf("%s %s", coloredName, ColoredBranchStatus(b)) coloredName = fmt.Sprintf("%s %s", coloredName, ColoredBranchStatus(b, tr))
} }
recencyColor := style.FgCyan recencyColor := style.FgCyan
@ -77,18 +78,21 @@ func GetBranchTextStyle(name string) style.TextStyle {
} }
} }
func ColoredBranchStatus(branch *models.Branch) string { func ColoredBranchStatus(branch *models.Branch, tr *i18n.TranslationSet) string {
colour := style.FgYellow colour := style.FgYellow
if branch.MatchesUpstream() { if !branch.IsTrackingRemote() || branch.UpstreamGone {
colour = style.FgGreen
} else if !branch.IsTrackingRemote() {
colour = style.FgRed colour = style.FgRed
} else if branch.MatchesUpstream() {
colour = style.FgGreen
} }
return colour.Sprint(BranchStatus(branch)) return colour.Sprint(BranchStatus(branch, tr))
} }
func BranchStatus(branch *models.Branch) string { func BranchStatus(branch *models.Branch, tr *i18n.TranslationSet) string {
if branch.UpstreamGone {
return tr.UpstreamGone
}
return fmt.Sprintf("↑%s↓%s", branch.Pushables, branch.Pullables) return fmt.Sprintf("↑%s↓%s", branch.Pushables, branch.Pullables)
} }

View File

@ -534,7 +534,7 @@ func (gui *Gui) refreshStatus() {
status := "" status := ""
if currentBranch.IsRealBranch() { if currentBranch.IsRealBranch() {
status += presentation.ColoredBranchStatus(currentBranch) + " " status += presentation.ColoredBranchStatus(currentBranch, gui.Tr) + " "
} }
workingTreeState := gui.git.Status.WorkingTreeState() workingTreeState := gui.git.Status.WorkingTreeState()

View File

@ -41,7 +41,7 @@ func (gui *Gui) handleStatusClick() error {
} }
cx, _ := gui.Views.Status.Cursor() cx, _ := gui.Views.Status.Cursor()
upstreamStatus := presentation.BranchStatus(currentBranch) upstreamStatus := presentation.BranchStatus(currentBranch, gui.Tr)
repoName := utils.GetCurrentRepoName() repoName := utils.GetCurrentRepoName()
workingTreeState := gui.git.Status.WorkingTreeState() workingTreeState := gui.git.Status.WorkingTreeState()
switch workingTreeState { switch workingTreeState {

View File

@ -458,6 +458,7 @@ type TranslationSet struct {
RewordInEditorPrompt string RewordInEditorPrompt string
CheckoutPrompt string CheckoutPrompt string
HardResetAutostashPrompt string HardResetAutostashPrompt string
UpstreamGone string
Actions Actions Actions Actions
Bisect Bisect Bisect Bisect
} }
@ -1035,6 +1036,7 @@ func EnglishTranslationSet() TranslationSet {
RewordInEditorPrompt: "Are you sure you want to reword this commit in your editor?", RewordInEditorPrompt: "Are you sure you want to reword this commit in your editor?",
HardResetAutostashPrompt: "Are you sure you want to hard reset to '%s'? An auto-stash will be performed if necessary.", HardResetAutostashPrompt: "Are you sure you want to hard reset to '%s'? An auto-stash will be performed if necessary.",
CheckoutPrompt: "Are you sure you want to checkout '%s'?", CheckoutPrompt: "Are you sure you want to checkout '%s'?",
UpstreamGone: "↑gone",
Actions: Actions{ Actions: Actions{
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm) // TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
CheckoutCommit: "Checkout commit", CheckoutCommit: "Checkout commit",