1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-28 16:02:01 +03:00

support multiple modes of git pull

This commit is contained in:
Jesse Duffield
2020-08-11 21:18:38 +10:00
parent 1c0da2967c
commit fcd5aea04e
7 changed files with 92 additions and 34 deletions

View File

@ -369,11 +369,26 @@ func (c *GitCommand) RebaseBranch(branchName string) error {
return c.OSCommand.RunPreparedCommand(cmd)
}
type FetchOptions struct {
PromptUserForCredential func(string) string
RemoteName string
BranchName string
}
// Fetch fetch git repo
func (c *GitCommand) Fetch(promptUserForCredential func(string) string, canPromptForCredential bool) error {
return c.OSCommand.DetectUnamePass("git fetch", func(question string) string {
if canPromptForCredential {
return promptUserForCredential(question)
func (c *GitCommand) Fetch(opts FetchOptions) error {
command := "git fetch"
if opts.RemoteName != "" {
command = fmt.Sprintf("%s %s", command, opts.RemoteName)
}
if opts.BranchName != "" {
command = fmt.Sprintf("%s %s", command, opts.BranchName)
}
return c.OSCommand.DetectUnamePass(command, func(question string) string {
if opts.PromptUserForCredential != nil {
return opts.PromptUserForCredential(question)
}
return "\n"
})
@ -430,10 +445,20 @@ func (c *GitCommand) ListStash() (string, error) {
return c.OSCommand.RunCommandWithOutput("git stash list")
}
type MergeOpts struct {
FastForwardOnly bool
}
// Merge merge
func (c *GitCommand) Merge(branchName string) error {
func (c *GitCommand) Merge(branchName string, opts MergeOpts) error {
mergeArgs := c.Config.GetUserConfig().GetString("git.merging.args")
return c.OSCommand.RunCommand("git merge --no-edit %s %s", mergeArgs, branchName)
command := fmt.Sprintf("git merge --no-edit %s %s", mergeArgs, branchName)
if opts.FastForwardOnly {
command = fmt.Sprintf("%s --ff-only", command)
}
return c.OSCommand.RunCommand(command)
}
// AbortMerge abort merge
@ -487,7 +512,7 @@ func (c *GitCommand) AmendHead() (*exec.Cmd, error) {
// Pull pulls from repo
func (c *GitCommand) Pull(args string, promptUserForCredential func(string) string) error {
return c.OSCommand.DetectUnamePass("git pull --no-edit "+args, promptUserForCredential)
return c.OSCommand.DetectUnamePass("git pull --no-edit --rebase ", promptUserForCredential)
}
// PullWithoutPasswordCheck assumes that the pull will not prompt the user for a password

View File

@ -703,7 +703,7 @@ func TestGitCommandMerge(t *testing.T) {
return exec.Command("echo")
}
assert.NoError(t, gitCmd.Merge("test"))
assert.NoError(t, gitCmd.Merge("test", MergeOpts{}))
}
// TestGitCommandUsingGpg is a function.

View File

@ -129,8 +129,12 @@ func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error {
return err
}
go func() {
err := gui.fetch(g, v, true)
gui.HandleCredentialsPopup(g, err)
err := gui.fetch(true)
gui.HandleCredentialsPopup(err)
if err == nil {
_ = gui.closeConfirmationPrompt(gui.g, true)
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
}
}()
return nil
}
@ -321,7 +325,7 @@ func (gui *Gui) mergeBranchIntoCheckedOutBranch(branchName string) error {
return gui.createConfirmationPanel(gui.g, gui.getBranchesView(), true, gui.Tr.SLocalize("MergingTitle"), prompt,
func(g *gocui.Gui, v *gocui.View) error {
err := gui.GitCommand.Merge(branchName)
err := gui.GitCommand.Merge(branchName, commands.MergeOpts{})
return gui.handleGenericMergeCommandResult(err)
}, nil)
}

View File

@ -77,7 +77,7 @@ func (gui *Gui) handleCredentialsViewFocused(g *gocui.Gui, v *gocui.View) error
}
// HandleCredentialsPopup handles the views after executing a command that might ask for credentials
func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, cmdErr error) {
func (gui *Gui) HandleCredentialsPopup(cmdErr error) {
_, _ = gui.g.SetViewOnBottom("credentials")
if cmdErr != nil {
errMessage := cmdErr.Error()
@ -86,8 +86,5 @@ func (gui *Gui) HandleCredentialsPopup(g *gocui.Gui, cmdErr error) {
}
// we are not logging this error because it may contain a password
_ = gui.createSpecificErrorPanel(errMessage, gui.getFilesView(), false)
} else {
_ = gui.closeConfirmationPrompt(g, true)
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
}
}

View File

@ -446,7 +446,7 @@ func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error {
}
for branchName, branch := range conf.Branches {
if branchName == currentBranch.Name {
return gui.pullFiles(v, fmt.Sprintf("%s %s", branch.Remote, branchName))
return gui.pullFiles(PullFilesOptions{RemoteName: branch.Remote, BranchName: branch.Name})
}
}
@ -459,27 +459,49 @@ func (gui *Gui) handlePullFiles(g *gocui.Gui, v *gocui.View) error {
}
return gui.createErrorPanel(errorMessage)
}
return gui.pullFiles(v, "")
return gui.pullFiles(PullFilesOptions{})
})
}
return gui.pullFiles(v, "")
return gui.pullFiles(PullFilesOptions{})
}
func (gui *Gui) pullFiles(v *gocui.View, args string) error {
if err := gui.createLoaderPanel(gui.g, v, gui.Tr.SLocalize("PullWait")); err != nil {
type PullFilesOptions struct {
RemoteName string
BranchName string
}
func (gui *Gui) pullFiles(opts PullFilesOptions) error {
if err := gui.createLoaderPanel(gui.g, gui.g.CurrentView(), gui.Tr.SLocalize("PullWait")); err != nil {
return err
}
// we want to first fetch, handling username if it comes up, then either merge or rebase. If merging we might have a merge conflict, likewise if rebasing we might have a conflict too.
// we need a way of saying .then or .catch
// what if we had a struct which contained an array of functions to run, each of which return a function, or perhaps write to a channel when they're done, and if there is no error, we run the next thing. In this case we first want to fetch, potentially handling a credential popup, then we want to rebase.
strategy := gui.Config.GetUserConfig().GetString("git.pull.mode")
go func() {
err := gui.GitCommand.Pull(args, gui.promptUserForCredential)
// gui.handleGenericMergeCommandResult(err)
gui.HandleCredentialsPopup(gui.g, err)
err := gui.GitCommand.Fetch(
commands.FetchOptions{
PromptUserForCredential: gui.promptUserForCredential,
RemoteName: opts.RemoteName,
BranchName: opts.BranchName,
},
)
gui.HandleCredentialsPopup(err)
if err == nil {
switch strategy {
case "rebase":
err := gui.GitCommand.RebaseBranch("FETCH_HEAD")
_ = gui.handleGenericMergeCommandResult(err)
case "merge":
err := gui.GitCommand.Merge("FETCH_HEAD", commands.MergeOpts{})
_ = gui.handleGenericMergeCommandResult(err)
case "ff-only":
err := gui.GitCommand.Merge("FETCH_HEAD", commands.MergeOpts{FastForwardOnly: true})
_ = gui.handleGenericMergeCommandResult(err)
default:
_ = gui.createErrorPanel(fmt.Sprintf("git pull strategy '%s' unrecognised", strategy))
}
}
}()
return nil
@ -492,7 +514,11 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool, upstr
go func() {
branchName := gui.getCheckedOutBranch().Name
err := gui.GitCommand.Push(branchName, force, upstream, args, gui.promptUserForCredential)
gui.HandleCredentialsPopup(g, err)
gui.HandleCredentialsPopup(err)
if err == nil {
_ = gui.closeConfirmationPrompt(gui.g, true)
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
}
}()
return nil
}

View File

@ -6,6 +6,7 @@ import (
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -166,16 +167,21 @@ func (gui *Gui) handleInfoClick(g *gocui.Gui, v *gocui.View) error {
return nil
}
func (gui *Gui) fetch(g *gocui.Gui, v *gocui.View, canAskForCredentials bool) (err error) {
err = gui.GitCommand.Fetch(gui.promptUserForCredential, canAskForCredentials)
func (gui *Gui) fetch(canPromptForCredentials bool) (err error) {
fetchOpts := commands.FetchOptions{}
if canPromptForCredentials {
fetchOpts.PromptUserForCredential = gui.promptUserForCredential
}
if canAskForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") {
err = gui.GitCommand.Fetch(fetchOpts)
if canPromptForCredentials && err != nil && strings.Contains(err.Error(), "exit status 128") {
colorFunction := color.New(color.FgRed).SprintFunc()
coloredMessage := colorFunction(strings.TrimSpace(gui.Tr.SLocalize("PassUnameWrong")))
close := func(g *gocui.Gui, v *gocui.View) error {
return nil
}
_ = gui.createConfirmationPanel(g, v, true, gui.Tr.SLocalize("Error"), coloredMessage, close, close)
_ = gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), true, gui.Tr.SLocalize("Error"), coloredMessage, close, close)
}
gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, COMMITS, REMOTES, TAGS}, mode: ASYNC})

View File

@ -501,12 +501,12 @@ func (gui *Gui) startBackgroundFetch() {
if !isNew {
time.After(60 * time.Second)
}
err := gui.fetch(gui.g, gui.g.CurrentView(), false)
err := gui.fetch(false)
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
_ = gui.createConfirmationPanel(gui.g, gui.g.CurrentView(), true, gui.Tr.SLocalize("NoAutomaticGitFetchTitle"), gui.Tr.SLocalize("NoAutomaticGitFetchBody"), nil, nil)
} else {
gui.goEvery(time.Second*60, gui.stopChan, func() error {
err := gui.fetch(gui.g, gui.g.CurrentView(), false)
err := gui.fetch(false)
return err
})
}