mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-20 05:19:24 +02:00
better upstream tracking and allow renaming a branch
This commit is contained in:
parent
2169b5109f
commit
1be0ff8da7
@ -3,8 +3,9 @@ package commands
|
|||||||
// Branch : A git branch
|
// Branch : A git branch
|
||||||
// duplicating this for now
|
// duplicating this for now
|
||||||
type Branch struct {
|
type Branch struct {
|
||||||
Name string
|
Name string
|
||||||
Recency string
|
Recency string
|
||||||
Pushables string
|
Pushables string
|
||||||
Pullables string
|
Pullables string
|
||||||
|
UpstreamName string
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// context:
|
// context:
|
||||||
@ -36,86 +34,94 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *GitCommand) (*BranchLis
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BranchListBuilder) obtainCurrentBranch() *Branch {
|
func (b *BranchListBuilder) obtainCurrentBranchName() string {
|
||||||
branchName, err := b.GitCommand.CurrentBranchName()
|
branchName, err := b.GitCommand.CurrentBranchName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
|
return branchName
|
||||||
return &Branch{Name: strings.TrimSpace(branchName)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BranchListBuilder) obtainReflogBranches() []*Branch {
|
func (b *BranchListBuilder) obtainBranches() []*Branch {
|
||||||
branches := make([]*Branch, 0)
|
cmdStr := `git branch --format="%(refname:short)|%(upstream:short)|%(upstream:track)"`
|
||||||
// if we directly put this string in RunCommandWithOutput the compiler complains because it thinks it's a format string
|
output, err := b.GitCommand.OSCommand.RunCommandWithOutput(cmdStr)
|
||||||
unescaped := "git reflog --date=relative --pretty='%gd|%gs' --grep-reflog='checkout: moving' HEAD"
|
|
||||||
rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput(unescaped)
|
|
||||||
if err != nil {
|
|
||||||
return branches
|
|
||||||
}
|
|
||||||
|
|
||||||
branchLines := utils.SplitLines(rawString)
|
|
||||||
for _, line := range branchLines {
|
|
||||||
recency, branchName := branchInfoFromLine(line)
|
|
||||||
if branchName == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
branch := &Branch{Name: branchName, Recency: recency}
|
|
||||||
branches = append(branches, branch)
|
|
||||||
}
|
|
||||||
return uniqueByName(branches)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BranchListBuilder) obtainSafeBranches() []*Branch {
|
|
||||||
branches := make([]*Branch, 0)
|
|
||||||
|
|
||||||
bIter, err := b.GitCommand.Repo.Branches()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
_ = bIter.ForEach(func(b *plumbing.Reference) error {
|
|
||||||
name := b.Name().Short()
|
trimmedOutput := strings.TrimSpace(output)
|
||||||
branches = append(branches, &Branch{Name: name})
|
outputLines := strings.Split(trimmedOutput, "\n")
|
||||||
return nil
|
branches := make([]*Branch, len(outputLines))
|
||||||
})
|
for i, line := range outputLines {
|
||||||
|
split := strings.Split(line, SEPARATION_CHAR)
|
||||||
|
|
||||||
|
name := split[0]
|
||||||
|
branches[i] = &Branch{
|
||||||
|
Name: name,
|
||||||
|
Pullables: "?",
|
||||||
|
Pushables: "?",
|
||||||
|
}
|
||||||
|
upstreamName := split[1]
|
||||||
|
if upstreamName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
branches[i].UpstreamName = upstreamName
|
||||||
|
|
||||||
|
track := split[2]
|
||||||
|
re := regexp.MustCompile(`ahead (\d+)`)
|
||||||
|
match := re.FindStringSubmatch(track)
|
||||||
|
if len(match) > 1 {
|
||||||
|
branches[i].Pushables = match[1]
|
||||||
|
} else {
|
||||||
|
branches[i].Pushables = "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
re = regexp.MustCompile(`behind (\d+)`)
|
||||||
|
match = re.FindStringSubmatch(track)
|
||||||
|
if len(match) > 1 {
|
||||||
|
branches[i].Pullables = match[1]
|
||||||
|
} else {
|
||||||
|
branches[i].Pullables = "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return branches
|
return branches
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existingBranches []*Branch, included bool) []*Branch {
|
|
||||||
for _, newBranch := range newBranches {
|
|
||||||
if included == branchIncluded(newBranch.Name, existingBranches) {
|
|
||||||
finalBranches = append(finalBranches, newBranch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return finalBranches
|
|
||||||
}
|
|
||||||
|
|
||||||
func sanitisedReflogName(reflogBranch *Branch, safeBranches []*Branch) string {
|
|
||||||
for _, safeBranch := range safeBranches {
|
|
||||||
if strings.EqualFold(safeBranch.Name, reflogBranch.Name) {
|
|
||||||
return safeBranch.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reflogBranch.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the list of branches for the current repo
|
// Build the list of branches for the current repo
|
||||||
func (b *BranchListBuilder) Build() []*Branch {
|
func (b *BranchListBuilder) Build() []*Branch {
|
||||||
branches := make([]*Branch, 0)
|
currentBranchName := b.obtainCurrentBranchName()
|
||||||
head := b.obtainCurrentBranch()
|
branches := b.obtainBranches()
|
||||||
safeBranches := b.obtainSafeBranches()
|
|
||||||
|
|
||||||
reflogBranches := b.obtainReflogBranches()
|
reflogBranches := b.obtainReflogBranches()
|
||||||
for i, reflogBranch := range reflogBranches {
|
|
||||||
reflogBranches[i].Name = sanitisedReflogName(reflogBranch, safeBranches)
|
// loop through reflog branches. If there is a match, merge them, then remove it from the branches and keep it in the reflog branches
|
||||||
|
branchesWithRecency := make([]*Branch, 0)
|
||||||
|
outer:
|
||||||
|
for _, reflogBranch := range reflogBranches {
|
||||||
|
for j, branch := range branches {
|
||||||
|
if strings.EqualFold(reflogBranch.Name, branch.Name) {
|
||||||
|
branch.Recency = reflogBranch.Recency
|
||||||
|
branchesWithRecency = append(branchesWithRecency, branch)
|
||||||
|
branches = append(branches[0:j], branches[j+1:]...)
|
||||||
|
continue outer
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
branches = b.appendNewBranches(branches, reflogBranches, safeBranches, true)
|
branches = append(branchesWithRecency, branches...)
|
||||||
branches = b.appendNewBranches(branches, safeBranches, branches, false)
|
|
||||||
|
|
||||||
if len(branches) == 0 || branches[0].Name != head.Name {
|
// if it's the current branch we need to pull it up to the top
|
||||||
branches = append([]*Branch{head}, branches...)
|
for i, branch := range branches {
|
||||||
|
if branch.Name == currentBranchName {
|
||||||
|
branches = append(branches[0:i], branches[i+1:]...)
|
||||||
|
branches = append([]*Branch{branch}, branches...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(branches) == 0 || branches[0].Name != currentBranchName {
|
||||||
|
branches = append([]*Branch{{Name: currentBranchName}}, branches...)
|
||||||
}
|
}
|
||||||
|
|
||||||
branches[0].Recency = " *"
|
branches[0].Recency = " *"
|
||||||
@ -123,26 +129,6 @@ func (b *BranchListBuilder) Build() []*Branch {
|
|||||||
return branches
|
return branches
|
||||||
}
|
}
|
||||||
|
|
||||||
func branchIncluded(branchName string, branches []*Branch) bool {
|
|
||||||
for _, existingBranch := range branches {
|
|
||||||
if strings.EqualFold(existingBranch.Name, branchName) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func uniqueByName(branches []*Branch) []*Branch {
|
|
||||||
finalBranches := make([]*Branch, 0)
|
|
||||||
for _, branch := range branches {
|
|
||||||
if branchIncluded(branch.Name, finalBranches) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
finalBranches = append(finalBranches, branch)
|
|
||||||
}
|
|
||||||
return finalBranches
|
|
||||||
}
|
|
||||||
|
|
||||||
// A line will have the form '10 days ago master' so we need to strip out the
|
// A line will have the form '10 days ago master' so we need to strip out the
|
||||||
// useful information from that into timeNumber, timeUnit, and branchName
|
// useful information from that into timeNumber, timeUnit, and branchName
|
||||||
func branchInfoFromLine(line string) (string, string) {
|
func branchInfoFromLine(line string) (string, string) {
|
||||||
@ -172,3 +158,30 @@ func abbreviatedTimeUnit(timeUnit string) string {
|
|||||||
}
|
}
|
||||||
return timeUnitMap[timeUnit]
|
return timeUnitMap[timeUnit]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BranchListBuilder) obtainReflogBranches() []*Branch {
|
||||||
|
branches := make([]*Branch, 0)
|
||||||
|
// if we directly put this string in RunCommandWithOutput the compiler complains because it thinks it's a format string
|
||||||
|
unescaped := "git reflog --date=relative --pretty='%gd|%gs' --grep-reflog='checkout: moving' HEAD"
|
||||||
|
rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput(unescaped)
|
||||||
|
if err != nil {
|
||||||
|
return branches
|
||||||
|
}
|
||||||
|
|
||||||
|
branchNameMap := map[string]bool{}
|
||||||
|
|
||||||
|
branchLines := utils.SplitLines(rawString)
|
||||||
|
for _, line := range branchLines {
|
||||||
|
recency, branchName := branchInfoFromLine(line)
|
||||||
|
if branchName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := branchNameMap[branchName]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
branchNameMap[branchName] = true
|
||||||
|
branch := &Branch{Name: branchName, Recency: recency}
|
||||||
|
branches = append(branches, branch)
|
||||||
|
}
|
||||||
|
return branches
|
||||||
|
}
|
||||||
|
@ -343,7 +343,7 @@ func (c *GitCommand) CurrentBranchName() (string, error) {
|
|||||||
match := re.FindStringSubmatch(output)
|
match := re.FindStringSubmatch(output)
|
||||||
branchName = match[1]
|
branchName = match[1]
|
||||||
}
|
}
|
||||||
return utils.TrimTrailingNewline(branchName), nil
|
return strings.TrimSpace(branchName), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBranch delete branch
|
// DeleteBranch delete branch
|
||||||
@ -1163,3 +1163,7 @@ func (c *GitCommand) GetPager(width int) string {
|
|||||||
func (c *GitCommand) colorArg() string {
|
func (c *GitCommand) colorArg() string {
|
||||||
return c.Config.GetUserConfig().GetString("git.paging.colorArg")
|
return c.Config.GetUserConfig().GetString("git.paging.colorArg")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *GitCommand) RenameBranch(oldName string, newName string) error {
|
||||||
|
return c.OSCommand.RunCommand("git branch --move %s %s", oldName, newName)
|
||||||
|
}
|
||||||
|
@ -334,6 +334,7 @@ keybinding:
|
|||||||
checkoutBranchByName: 'c'
|
checkoutBranchByName: 'c'
|
||||||
forceCheckoutBranch: 'F'
|
forceCheckoutBranch: 'F'
|
||||||
rebaseBranch: 'r'
|
rebaseBranch: 'r'
|
||||||
|
renameBranch: 'R'
|
||||||
mergeIntoCurrentBranch: 'M'
|
mergeIntoCurrentBranch: 'M'
|
||||||
viewGitFlowOptions: 'i'
|
viewGitFlowOptions: 'i'
|
||||||
fastForward: 'f'
|
fastForward: 'f'
|
||||||
|
@ -41,9 +41,6 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
|||||||
}
|
}
|
||||||
branch := gui.getSelectedBranch()
|
branch := gui.getSelectedBranch()
|
||||||
v.FocusPoint(0, gui.State.Panels.Branches.SelectedLine)
|
v.FocusPoint(0, gui.State.Panels.Branches.SelectedLine)
|
||||||
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := gui.OSCommand.ExecutableFromString(
|
cmd := gui.OSCommand.ExecutableFromString(
|
||||||
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
|
gui.GitCommand.GetBranchGraphCmdStr(branch.Name),
|
||||||
@ -54,24 +51,6 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) RenderSelectedBranchUpstreamDifferences() error {
|
|
||||||
return gui.newTask("branches", func(stop chan struct{}) error {
|
|
||||||
branch := gui.getSelectedBranch()
|
|
||||||
branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-stop:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
branchesView := gui.getBranchesView()
|
|
||||||
displayStrings := presentation.GetBranchListDisplayStrings(gui.State.Branches, gui.currentViewName() == "branches", gui.State.Panels.Branches.SelectedLine)
|
|
||||||
gui.renderDisplayStrings(branchesView, displayStrings)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// gui.refreshStatus is called at the end of this because that's when we can
|
// gui.refreshStatus is called at the end of this because that's when we can
|
||||||
// be sure there is a state.Branches array to pick the current branch from
|
// be sure there is a state.Branches array to pick the current branch from
|
||||||
func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
||||||
@ -106,9 +85,8 @@ func (gui *Gui) renderLocalBranchesWithSelection() error {
|
|||||||
branchesView := gui.getBranchesView()
|
branchesView := gui.getBranchesView()
|
||||||
|
|
||||||
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
|
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
|
||||||
if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
|
displayStrings := presentation.GetBranchListDisplayStrings(gui.State.Branches, gui.State.ScreenMode != SCREEN_NORMAL)
|
||||||
return err
|
gui.renderDisplayStrings(branchesView, displayStrings)
|
||||||
}
|
|
||||||
if gui.g.CurrentView() == branchesView {
|
if gui.g.CurrentView() == branchesView {
|
||||||
if err := gui.handleBranchSelect(gui.g, branchesView); err != nil {
|
if err := gui.handleBranchSelect(gui.g, branchesView); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -375,7 +353,7 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.GitCommand.FastForward(branch.Name, remoteName, remoteBranchName); err != nil {
|
if err := gui.GitCommand.FastForward(branch.Name, remoteName, remoteBranchName); err != nil {
|
||||||
_ = gui.createErrorPanel(gui.g, err.Error())
|
_ = gui.createErrorPanel(gui.g, err.Error())
|
||||||
}
|
}
|
||||||
_ = gui.RenderSelectedBranchUpstreamDifferences()
|
_ = gui.refreshBranches(gui.g)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = gui.closeConfirmationPrompt(gui.g, true)
|
_ = gui.closeConfirmationPrompt(gui.g, true)
|
||||||
@ -407,7 +385,13 @@ func (gui *Gui) switchBranchesPanelContext(context string) error {
|
|||||||
|
|
||||||
branchesView.TabIndex = contextTabIndexMap[context]
|
branchesView.TabIndex = contextTabIndexMap[context]
|
||||||
|
|
||||||
switch context {
|
return gui.refreshBranchesViewWithSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) refreshBranchesViewWithSelection() error {
|
||||||
|
branchesView := gui.getBranchesView()
|
||||||
|
|
||||||
|
switch branchesView.Context {
|
||||||
case "local-branches":
|
case "local-branches":
|
||||||
return gui.renderLocalBranchesWithSelection()
|
return gui.renderLocalBranchesWithSelection()
|
||||||
case "remotes":
|
case "remotes":
|
||||||
@ -457,3 +441,41 @@ func (gui *Gui) onBranchesPanelSearchSelect(selectedLine int) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleRenameBranch(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
branch := gui.getSelectedBranch()
|
||||||
|
if branch == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
promptForNewName := func() error {
|
||||||
|
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("NewBranchNamePrompt")+" "+branch.Name+":", "", func(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
newName := gui.trimmedContent(v)
|
||||||
|
if err := gui.GitCommand.RenameBranch(branch.Name, newName); err != nil {
|
||||||
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
|
}
|
||||||
|
// need to checkout so that the branch shows up in our reflog and therefore
|
||||||
|
// doesn't get lost among all the other branches when we switch to something else
|
||||||
|
if err := gui.GitCommand.Checkout(newName, false); err != nil {
|
||||||
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.refreshBranches(gui.g)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// I could do an explicit check here for whether the branch is tracking a remote branch
|
||||||
|
// but if we've selected it we'll already know that via Pullables and Pullables.
|
||||||
|
// Bit of a hack but I'm lazy.
|
||||||
|
notTrackingRemote := branch.Pullables == "?"
|
||||||
|
if notTrackingRemote {
|
||||||
|
return promptForNewName()
|
||||||
|
}
|
||||||
|
return gui.createConfirmationPanel(gui.g, v, true, gui.Tr.SLocalize("renameBranch"), gui.Tr.SLocalize("RenameBranchWarning"), func(_g *gocui.Gui, _v *gocui.View) error {
|
||||||
|
return promptForNewName()
|
||||||
|
}, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) currentBranch() *commands.Branch {
|
||||||
|
return gui.State.Branches[0]
|
||||||
|
}
|
||||||
|
@ -399,24 +399,20 @@ func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
|
|||||||
|
|
||||||
func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error {
|
||||||
// if we have no upstream branch we need to set that first
|
// if we have no upstream branch we need to set that first
|
||||||
_, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
|
currentBranch := gui.currentBranch()
|
||||||
currentBranchName, err := gui.GitCommand.CurrentBranchName()
|
if currentBranch.Pullables == "?" {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if pullables == "?" {
|
|
||||||
// see if we have this branch in our config with an upstream
|
// see if we have this branch in our config with an upstream
|
||||||
conf, err := gui.GitCommand.Repo.Config()
|
conf, err := gui.GitCommand.Repo.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.createErrorPanel(gui.g, err.Error())
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
}
|
}
|
||||||
for branchName, branch := range conf.Branches {
|
for branchName, branch := range conf.Branches {
|
||||||
if branchName == currentBranchName {
|
if branchName == currentBranch.Name {
|
||||||
return gui.pullFiles(v, fmt.Sprintf("%s %s", branch.Remote, branchName))
|
return gui.pullFiles(v, fmt.Sprintf("%s %s", branch.Remote, branchName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin/"+currentBranchName, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin/"+currentBranch.Name, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
upstream := gui.trimmedContent(v)
|
upstream := gui.trimmedContent(v)
|
||||||
if err := gui.GitCommand.SetUpstreamBranch(upstream); err != nil {
|
if err := gui.GitCommand.SetUpstreamBranch(upstream); err != nil {
|
||||||
errorMessage := err.Error()
|
errorMessage := err.Error()
|
||||||
@ -467,28 +463,24 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool, upstr
|
|||||||
|
|
||||||
func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
|
||||||
// if we have pullables we'll ask if the user wants to force push
|
// if we have pullables we'll ask if the user wants to force push
|
||||||
_, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
|
currentBranch := gui.currentBranch()
|
||||||
currentBranchName, err := gui.GitCommand.CurrentBranchName()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if pullables == "?" {
|
if currentBranch.Pullables == "?" {
|
||||||
// see if we have this branch in our config with an upstream
|
// see if we have this branch in our config with an upstream
|
||||||
conf, err := gui.GitCommand.Repo.Config()
|
conf, err := gui.GitCommand.Repo.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.createErrorPanel(gui.g, err.Error())
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
}
|
}
|
||||||
for branchName, branch := range conf.Branches {
|
for branchName, branch := range conf.Branches {
|
||||||
if branchName == currentBranchName {
|
if branchName == currentBranch.Name {
|
||||||
return gui.pushWithForceFlag(g, v, false, "", fmt.Sprintf("%s %s", branch.Remote, branchName))
|
return gui.pushWithForceFlag(g, v, false, "", fmt.Sprintf("%s %s", branch.Remote, branchName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin "+currentBranchName, func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("EnterUpstream"), "origin "+currentBranch.Name, func(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.pushWithForceFlag(g, v, false, gui.trimmedContent(v), "")
|
return gui.pushWithForceFlag(g, v, false, gui.trimmedContent(v), "")
|
||||||
})
|
})
|
||||||
} else if pullables == "0" {
|
} else if currentBranch.Pullables == "0" {
|
||||||
return gui.pushWithForceFlag(g, v, false, "", "")
|
return gui.pushWithForceFlag(g, v, false, "", "")
|
||||||
}
|
}
|
||||||
return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("ForcePush"), gui.Tr.SLocalize("ForcePushPrompt"), func(g *gocui.Gui, v *gocui.View) error {
|
return gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("ForcePush"), gui.Tr.SLocalize("ForcePushPrompt"), func(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||||
"github.com/jesseduffield/lazygit/pkg/tasks"
|
"github.com/jesseduffield/lazygit/pkg/tasks"
|
||||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||||
@ -278,6 +277,10 @@ func (gui *Gui) nextScreenMode(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.refreshCommitsViewWithSelection(); err != nil {
|
if err := gui.refreshCommitsViewWithSelection(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// same with branches
|
||||||
|
if err := gui.refreshBranchesViewWithSelection(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -288,6 +291,10 @@ func (gui *Gui) prevScreenMode(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err := gui.refreshCommitsViewWithSelection(); err != nil {
|
if err := gui.refreshCommitsViewWithSelection(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// same with branches
|
||||||
|
if err := gui.refreshBranchesViewWithSelection(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -386,12 +393,6 @@ func (gui *Gui) onFocusLost(v *gocui.View, newView *gocui.View) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch v.Name() {
|
switch v.Name() {
|
||||||
case "branches":
|
|
||||||
if v.Context == "local-branches" {
|
|
||||||
// This stops the branches panel from showing the upstream/downstream changes to the selected branch, when it loses focus
|
|
||||||
displayStrings := presentation.GetBranchListDisplayStrings(gui.State.Branches, false, -1)
|
|
||||||
gui.renderDisplayStrings(gui.getBranchesView(), displayStrings)
|
|
||||||
}
|
|
||||||
case "main":
|
case "main":
|
||||||
// if we have lost focus to a first-class panel, we need to do some cleanup
|
// if we have lost focus to a first-class panel, we need to do some cleanup
|
||||||
gui.changeMainViewsContext("normal")
|
gui.changeMainViewsContext("normal")
|
||||||
|
@ -575,6 +575,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||||||
Handler: gui.handleCreateResetToBranchMenu,
|
Handler: gui.handleCreateResetToBranchMenu,
|
||||||
Description: gui.Tr.SLocalize("viewResetOptions"),
|
Description: gui.Tr.SLocalize("viewResetOptions"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ViewName: "branches",
|
||||||
|
Contexts: []string{"local-branches"},
|
||||||
|
Key: gui.getKey("branches.renameBranch"),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.handleRenameBranch,
|
||||||
|
Description: gui.Tr.SLocalize("viewResetOptions"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ViewName: "branches",
|
ViewName: "branches",
|
||||||
Contexts: []string{"tags"},
|
Contexts: []string{"tags"},
|
||||||
|
@ -10,25 +10,38 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetBranchListDisplayStrings(branches []*commands.Branch, isFocused bool, selectedLine int) [][]string {
|
func GetBranchListDisplayStrings(branches []*commands.Branch, fullDescription bool) [][]string {
|
||||||
lines := make([][]string, len(branches))
|
lines := make([][]string, len(branches))
|
||||||
|
|
||||||
for i := range branches {
|
for i := range branches {
|
||||||
showUpstreamDifferences := isFocused && i == selectedLine
|
lines[i] = getBranchDisplayStrings(branches[i], fullDescription)
|
||||||
lines[i] = getBranchDisplayStrings(branches[i], showUpstreamDifferences)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBranchDisplayStrings returns the display string of branch
|
// getBranchDisplayStrings returns the display string of branch
|
||||||
func getBranchDisplayStrings(b *commands.Branch, showUpstreamDifferences bool) []string {
|
func getBranchDisplayStrings(b *commands.Branch, fullDescription bool) []string {
|
||||||
displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name))
|
displayName := utils.ColoredString(b.Name, GetBranchColor(b.Name))
|
||||||
if showUpstreamDifferences && b.Pushables != "" && b.Pullables != "" {
|
if b.Pushables != "" && b.Pullables != "" && b.Pushables != "?" && b.Pullables != "?" {
|
||||||
displayName = fmt.Sprintf("%s ↑%s↓%s", displayName, b.Pushables, b.Pullables)
|
trackColor := color.FgYellow
|
||||||
|
if b.Pushables == "0" && b.Pullables == "0" {
|
||||||
|
trackColor = color.FgGreen
|
||||||
|
}
|
||||||
|
track := utils.ColoredString(fmt.Sprintf("↑%s↓%s", b.Pushables, b.Pullables), trackColor)
|
||||||
|
displayName = fmt.Sprintf("%s %s", displayName, track)
|
||||||
}
|
}
|
||||||
|
|
||||||
return []string{b.Recency, displayName}
|
recencyColor := color.FgCyan
|
||||||
|
if b.Recency == " *" {
|
||||||
|
recencyColor = color.FgGreen
|
||||||
|
}
|
||||||
|
|
||||||
|
if fullDescription {
|
||||||
|
return []string{utils.ColoredString(b.Recency, recencyColor), displayName, utils.ColoredString(b.UpstreamName, color.FgYellow)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{utils.ColoredString(b.Recency, recencyColor), displayName}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBranchColor branch color
|
// GetBranchColor branch color
|
||||||
|
@ -36,6 +36,9 @@ func (gui *Gui) createResetMenu(ref string) error {
|
|||||||
if err := gui.refreshFiles(); err != nil {
|
if err := gui.refreshFiles(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := gui.refreshBranches(gui.g); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := gui.resetOrigin(gui.getCommitsView()); err != nil {
|
if err := gui.resetOrigin(gui.getCommitsView()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,20 @@ func (gui *Gui) refreshStatus(g *gocui.Gui) error {
|
|||||||
// contents end up cleared
|
// contents end up cleared
|
||||||
g.Update(func(*gocui.Gui) error {
|
g.Update(func(*gocui.Gui) error {
|
||||||
v.Clear()
|
v.Clear()
|
||||||
|
// TODO: base this off of the current branch
|
||||||
state.pushables, state.pullables = gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
|
state.pushables, state.pullables = gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
|
||||||
if err := gui.updateWorkTreeState(); err != nil {
|
if err := gui.updateWorkTreeState(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
status := fmt.Sprintf("↑%s↓%s", state.pushables, state.pullables)
|
|
||||||
|
trackColor := color.FgYellow
|
||||||
|
if state.pushables == "0" && state.pullables == "0" {
|
||||||
|
trackColor = color.FgGreen
|
||||||
|
} else if state.pushables == "?" && state.pullables == "?" {
|
||||||
|
trackColor = color.FgRed
|
||||||
|
}
|
||||||
|
|
||||||
|
status := utils.ColoredString(fmt.Sprintf("↑%s↓%s", state.pushables, state.pullables), trackColor)
|
||||||
branches := gui.State.Branches
|
branches := gui.State.Branches
|
||||||
|
|
||||||
if gui.State.WorkingTreeState != "normal" {
|
if gui.State.WorkingTreeState != "normal" {
|
||||||
|
@ -1017,6 +1017,15 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "Keybindings",
|
ID: "Keybindings",
|
||||||
Other: "Keybindings",
|
Other: "Keybindings",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "renameBranch",
|
||||||
|
Other: "rename branch",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "NewBranchNamePrompt",
|
||||||
|
Other: "Enter new branch name for branch",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "RenameBranchWarning",
|
||||||
|
Other: "This branch is tracking a remote. This action will only rename the local branch name, not the name of the remote branch. Continue?",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user