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:
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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})
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user