mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-13 01:30:53 +02:00
fix issue where upstream origin and branch were quoted together
fix issue where upstream origin and branch were quoted together
This commit is contained in:
@ -3,27 +3,46 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/go-errors/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Push pushes to a branch
|
// Push pushes to a branch
|
||||||
func (c *GitCommand) Push(branchName string, force bool, upstream string, args string, promptUserForCredential func(string) string) error {
|
type PushOpts struct {
|
||||||
followTagsFlag := "--follow-tags"
|
Force bool
|
||||||
if c.GetConfigValue("push.followTags") == "false" {
|
UpstreamRemote string
|
||||||
followTagsFlag = ""
|
UpstreamBranch string
|
||||||
|
SetUpstream bool
|
||||||
|
PromptUserForCredential func(string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GitCommand) Push(opts PushOpts) error {
|
||||||
|
cmd := "git push"
|
||||||
|
|
||||||
|
if c.GetConfigValue("push.followTags") != "false" {
|
||||||
|
cmd += " --follow-tags"
|
||||||
}
|
}
|
||||||
|
|
||||||
forceFlag := ""
|
if opts.Force {
|
||||||
if force {
|
cmd += " --force-with-lease"
|
||||||
forceFlag = "--force-with-lease"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpstreamArg := ""
|
if opts.SetUpstream {
|
||||||
if upstream != "" {
|
cmd += " --set-upstream"
|
||||||
setUpstreamArg = "--set-upstream " + c.OSCommand.Quote(upstream)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := fmt.Sprintf("git push %s %s %s %s", followTagsFlag, forceFlag, setUpstreamArg, args)
|
if opts.UpstreamRemote != "" {
|
||||||
return c.OSCommand.DetectUnamePass(cmd, promptUserForCredential)
|
cmd += " " + c.OSCommand.Quote(opts.UpstreamRemote)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.UpstreamBranch != "" {
|
||||||
|
if opts.UpstreamRemote == "" {
|
||||||
|
return errors.New(c.Tr.MustSpecifyOriginError)
|
||||||
|
}
|
||||||
|
cmd += " " + c.OSCommand.Quote(opts.UpstreamBranch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.OSCommand.DetectUnamePass(cmd, opts.PromptUserForCredential)
|
||||||
}
|
}
|
||||||
|
|
||||||
type FetchOptions struct {
|
type FetchOptions struct {
|
||||||
|
@ -14,10 +14,14 @@ func TestGitCommandPush(t *testing.T) {
|
|||||||
testName string
|
testName string
|
||||||
getGitConfigValue func(string) (string, error)
|
getGitConfigValue func(string) (string, error)
|
||||||
command func(string, ...string) *exec.Cmd
|
command func(string, ...string) *exec.Cmd
|
||||||
forcePush bool
|
opts PushOpts
|
||||||
test func(error)
|
test func(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prompt := func(passOrUname string) string {
|
||||||
|
return "\n"
|
||||||
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"Push with force disabled, follow-tags on",
|
"Push with force disabled, follow-tags on",
|
||||||
@ -30,7 +34,7 @@ func TestGitCommandPush(t *testing.T) {
|
|||||||
|
|
||||||
return secureexec.Command("echo")
|
return secureexec.Command("echo")
|
||||||
},
|
},
|
||||||
false,
|
PushOpts{Force: false, PromptUserForCredential: prompt},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@ -46,7 +50,7 @@ func TestGitCommandPush(t *testing.T) {
|
|||||||
|
|
||||||
return secureexec.Command("echo")
|
return secureexec.Command("echo")
|
||||||
},
|
},
|
||||||
true,
|
PushOpts{Force: true, PromptUserForCredential: prompt},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@ -62,7 +66,7 @@ func TestGitCommandPush(t *testing.T) {
|
|||||||
|
|
||||||
return secureexec.Command("echo")
|
return secureexec.Command("echo")
|
||||||
},
|
},
|
||||||
false,
|
PushOpts{Force: false, PromptUserForCredential: prompt},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@ -77,11 +81,161 @@ func TestGitCommandPush(t *testing.T) {
|
|||||||
assert.EqualValues(t, []string{"push", "--follow-tags"}, args)
|
assert.EqualValues(t, []string{"push", "--follow-tags"}, args)
|
||||||
return secureexec.Command("test")
|
return secureexec.Command("test")
|
||||||
},
|
},
|
||||||
false,
|
PushOpts{Force: false, PromptUserForCredential: prompt},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Push with force disabled, follow-tags off, upstream supplied",
|
||||||
|
func(string) (string, error) {
|
||||||
|
return "false", nil
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"push", "origin", "master"}, args)
|
||||||
|
|
||||||
|
return secureexec.Command("echo")
|
||||||
|
},
|
||||||
|
PushOpts{
|
||||||
|
Force: false,
|
||||||
|
UpstreamRemote: "origin",
|
||||||
|
UpstreamBranch: "master",
|
||||||
|
PromptUserForCredential: prompt,
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Push with force disabled, follow-tags off, setting upstream",
|
||||||
|
func(string) (string, error) {
|
||||||
|
return "false", nil
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"push", "--set-upstream", "origin", "master"}, args)
|
||||||
|
|
||||||
|
return secureexec.Command("echo")
|
||||||
|
},
|
||||||
|
PushOpts{
|
||||||
|
Force: false,
|
||||||
|
UpstreamRemote: "origin",
|
||||||
|
UpstreamBranch: "master",
|
||||||
|
PromptUserForCredential: prompt,
|
||||||
|
SetUpstream: true,
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Push with force enabled, follow-tags off, setting upstream",
|
||||||
|
func(string) (string, error) {
|
||||||
|
return "false", nil
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"push", "--force-with-lease", "--set-upstream", "origin", "master"}, args)
|
||||||
|
|
||||||
|
return secureexec.Command("echo")
|
||||||
|
},
|
||||||
|
PushOpts{
|
||||||
|
Force: true,
|
||||||
|
UpstreamRemote: "origin",
|
||||||
|
UpstreamBranch: "master",
|
||||||
|
PromptUserForCredential: prompt,
|
||||||
|
SetUpstream: true,
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Push with remote branch but no origin",
|
||||||
|
func(string) (string, error) {
|
||||||
|
return "false", nil
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
PushOpts{
|
||||||
|
Force: true,
|
||||||
|
UpstreamRemote: "",
|
||||||
|
UpstreamBranch: "master",
|
||||||
|
PromptUserForCredential: prompt,
|
||||||
|
SetUpstream: true,
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.EqualValues(t, "Must specify a remote if specifying a branch", err.Error())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Push with force disabled, follow-tags off, upstream supplied",
|
||||||
|
func(string) (string, error) {
|
||||||
|
return "false", nil
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"push", "origin", "master"}, args)
|
||||||
|
|
||||||
|
return secureexec.Command("echo")
|
||||||
|
},
|
||||||
|
PushOpts{
|
||||||
|
Force: false,
|
||||||
|
UpstreamRemote: "origin",
|
||||||
|
UpstreamBranch: "master",
|
||||||
|
PromptUserForCredential: prompt,
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Push with force disabled, follow-tags off, setting upstream",
|
||||||
|
func(string) (string, error) {
|
||||||
|
return "false", nil
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"push", "--set-upstream", "origin", "master"}, args)
|
||||||
|
|
||||||
|
return secureexec.Command("echo")
|
||||||
|
},
|
||||||
|
PushOpts{
|
||||||
|
Force: false,
|
||||||
|
UpstreamRemote: "origin",
|
||||||
|
UpstreamBranch: "master",
|
||||||
|
PromptUserForCredential: prompt,
|
||||||
|
SetUpstream: true,
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Push with force enabled, follow-tags off, setting upstream",
|
||||||
|
func(string) (string, error) {
|
||||||
|
return "false", nil
|
||||||
|
},
|
||||||
|
func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"push", "--force-with-lease", "--set-upstream", "origin", "master"}, args)
|
||||||
|
|
||||||
|
return secureexec.Command("echo")
|
||||||
|
},
|
||||||
|
PushOpts{
|
||||||
|
Force: true,
|
||||||
|
UpstreamRemote: "origin",
|
||||||
|
UpstreamBranch: "master",
|
||||||
|
PromptUserForCredential: prompt,
|
||||||
|
SetUpstream: true,
|
||||||
|
},
|
||||||
|
func(err error) {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
@ -89,9 +243,7 @@ func TestGitCommandPush(t *testing.T) {
|
|||||||
gitCmd := NewDummyGitCommand()
|
gitCmd := NewDummyGitCommand()
|
||||||
gitCmd.OSCommand.Command = s.command
|
gitCmd.OSCommand.Command = s.command
|
||||||
gitCmd.getGitConfigValue = s.getGitConfigValue
|
gitCmd.getGitConfigValue = s.getGitConfigValue
|
||||||
err := gitCmd.Push("test", s.forcePush, "", "", func(passOrUname string) string {
|
err := gitCmd.Push(s.opts)
|
||||||
return "\n"
|
|
||||||
})
|
|
||||||
s.test(err)
|
s.test(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -730,14 +730,27 @@ func (gui *Gui) pullWithMode(mode string, opts PullFilesOptions) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) pushWithForceFlag(force bool, upstream string, args string) error {
|
type pushOpts struct {
|
||||||
|
force bool
|
||||||
|
upstreamRemote string
|
||||||
|
upstreamBranch string
|
||||||
|
setUpstream bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) push(opts pushOpts) error {
|
||||||
if err := gui.createLoaderPanel(gui.Tr.PushWait); err != nil {
|
if err := gui.createLoaderPanel(gui.Tr.PushWait); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go utils.Safe(func() {
|
go utils.Safe(func() {
|
||||||
branchName := gui.getCheckedOutBranch().Name
|
err := gui.GitCommand.WithSpan(gui.Tr.Spans.Push).Push(commands.PushOpts{
|
||||||
err := gui.GitCommand.WithSpan(gui.Tr.Spans.Push).Push(branchName, force, upstream, args, gui.promptUserForCredential)
|
Force: opts.force,
|
||||||
if err != nil && !force && strings.Contains(err.Error(), "Updates were rejected") {
|
UpstreamRemote: opts.upstreamRemote,
|
||||||
|
UpstreamBranch: opts.upstreamBranch,
|
||||||
|
SetUpstream: opts.setUpstream,
|
||||||
|
PromptUserForCredential: gui.promptUserForCredential,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil && !opts.force && strings.Contains(err.Error(), "Updates were rejected") {
|
||||||
forcePushDisabled := gui.Config.GetUserConfig().Git.DisableForcePushing
|
forcePushDisabled := gui.Config.GetUserConfig().Git.DisableForcePushing
|
||||||
if forcePushDisabled {
|
if forcePushDisabled {
|
||||||
_ = gui.createErrorPanel(gui.Tr.UpdatesRejectedAndForcePushDisabled)
|
_ = gui.createErrorPanel(gui.Tr.UpdatesRejectedAndForcePushDisabled)
|
||||||
@ -747,7 +760,10 @@ func (gui *Gui) pushWithForceFlag(force bool, upstream string, args string) erro
|
|||||||
title: gui.Tr.ForcePush,
|
title: gui.Tr.ForcePush,
|
||||||
prompt: gui.Tr.ForcePushPrompt,
|
prompt: gui.Tr.ForcePushPrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.pushWithForceFlag(true, upstream, args)
|
newOpts := opts
|
||||||
|
newOpts.force = true
|
||||||
|
|
||||||
|
return gui.push(newOpts)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@ -774,27 +790,48 @@ func (gui *Gui) pushFiles() error {
|
|||||||
if currentBranch.HasCommitsToPull() {
|
if currentBranch.HasCommitsToPull() {
|
||||||
return gui.requestToForcePush()
|
return gui.requestToForcePush()
|
||||||
} else {
|
} else {
|
||||||
return gui.pushWithForceFlag(false, "", "")
|
return gui.push(pushOpts{})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// see if we have an upstream for this branch in our config
|
// see if we have an upstream for this branch in our config
|
||||||
upstream, err := gui.upstreamForBranchInConfig(currentBranch.Name)
|
upstreamRemote, upstreamBranch, err := gui.upstreamForBranchInConfig(currentBranch.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if upstream != "" {
|
if upstreamBranch != "" {
|
||||||
return gui.pushWithForceFlag(false, "", upstream)
|
return gui.push(
|
||||||
|
pushOpts{
|
||||||
|
force: false,
|
||||||
|
upstreamRemote: upstreamRemote,
|
||||||
|
upstreamBranch: upstreamBranch,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gui.GitCommand.PushToCurrent {
|
if gui.GitCommand.PushToCurrent {
|
||||||
return gui.pushWithForceFlag(false, "", "--set-upstream")
|
return gui.push(pushOpts{setUpstream: true})
|
||||||
} else {
|
} else {
|
||||||
return gui.prompt(promptOpts{
|
return gui.prompt(promptOpts{
|
||||||
title: gui.Tr.EnterUpstream,
|
title: gui.Tr.EnterUpstream,
|
||||||
initialContent: "origin " + currentBranch.Name,
|
initialContent: "origin " + currentBranch.Name,
|
||||||
handleConfirm: func(upstream string) error {
|
handleConfirm: func(upstream string) error {
|
||||||
return gui.pushWithForceFlag(false, upstream, "")
|
var upstreamBranch, upstreamRemote string
|
||||||
|
split := strings.Split(upstream, " ")
|
||||||
|
if len(split) == 2 {
|
||||||
|
upstreamRemote = split[0]
|
||||||
|
upstreamBranch = split[1]
|
||||||
|
} else {
|
||||||
|
upstreamRemote = upstream
|
||||||
|
upstreamBranch = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return gui.push(pushOpts{
|
||||||
|
force: false,
|
||||||
|
upstreamRemote: upstreamRemote,
|
||||||
|
upstreamBranch: upstreamBranch,
|
||||||
|
setUpstream: true,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -811,24 +848,24 @@ func (gui *Gui) requestToForcePush() error {
|
|||||||
title: gui.Tr.ForcePush,
|
title: gui.Tr.ForcePush,
|
||||||
prompt: gui.Tr.ForcePushPrompt,
|
prompt: gui.Tr.ForcePushPrompt,
|
||||||
handleConfirm: func() error {
|
handleConfirm: func() error {
|
||||||
return gui.pushWithForceFlag(true, "", "")
|
return gui.push(pushOpts{force: true})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) upstreamForBranchInConfig(branchName string) (string, error) {
|
func (gui *Gui) upstreamForBranchInConfig(branchName string) (string, string, error) {
|
||||||
conf, err := gui.GitCommand.Repo.Config()
|
conf, err := gui.GitCommand.Repo.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
for configBranchName, configBranch := range conf.Branches {
|
for configBranchName, configBranch := range conf.Branches {
|
||||||
if configBranchName == branchName {
|
if configBranchName == branchName {
|
||||||
return fmt.Sprintf("%s %s", configBranch.Remote, configBranchName), nil
|
return configBranch.Remote, configBranchName, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", nil
|
return "", "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSwitchToMerge() error {
|
func (gui *Gui) handleSwitchToMerge() error {
|
||||||
|
@ -431,6 +431,7 @@ type TranslationSet struct {
|
|||||||
SelectConfigFile string
|
SelectConfigFile string
|
||||||
NoConfigFileFoundErr string
|
NoConfigFileFoundErr string
|
||||||
LcLoadingFileSuggestions string
|
LcLoadingFileSuggestions string
|
||||||
|
MustSpecifyOriginError string
|
||||||
Spans Spans
|
Spans Spans
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -956,6 +957,7 @@ func englishTranslationSet() TranslationSet {
|
|||||||
SelectConfigFile: "Select config file",
|
SelectConfigFile: "Select config file",
|
||||||
NoConfigFileFoundErr: "No config file found",
|
NoConfigFileFoundErr: "No config file found",
|
||||||
LcLoadingFileSuggestions: "loading file suggestions",
|
LcLoadingFileSuggestions: "loading file suggestions",
|
||||||
|
MustSpecifyOriginError: "Must specify a remote if specifying a branch",
|
||||||
Spans: Spans{
|
Spans: Spans{
|
||||||
// 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",
|
||||||
|
Reference in New Issue
Block a user