diff --git a/pkg/commands/git_commands/sync.go b/pkg/commands/git_commands/sync.go index 0400b4e26..1a20a3293 100644 --- a/pkg/commands/git_commands/sync.go +++ b/pkg/commands/git_commands/sync.go @@ -1,8 +1,6 @@ package git_commands import ( - "fmt" - "github.com/go-errors/errors" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/gocui" @@ -18,18 +16,21 @@ func NewSyncCommands(gitCommon *GitCommon) *SyncCommands { } } -// Push pushes to a branch type PushOpts struct { Force bool ForceWithLease bool - CurrentBranch string - UpstreamRemote string - UpstreamBranch string SetUpstream bool + // The remote to push to. If empty, git picks it from its configuration, + // and Refspecs must be empty too. + Remote string + // What to push, each in the form "refs/heads/:". + // If empty, git decides what to push based on push.default and + // remote..push. + Refspecs []string } func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (*oscommands.CmdObj, error) { - if opts.UpstreamBranch != "" && opts.UpstreamRemote == "" { + if len(opts.Refspecs) > 0 && opts.Remote == "" { return nil, errors.New(self.Tr.MustSpecifyOriginError) } @@ -37,8 +38,8 @@ func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (*oscommand ArgIf(opts.Force, "--force"). ArgIf(opts.ForceWithLease, "--force-with-lease"). ArgIf(opts.SetUpstream, "--set-upstream"). - ArgIf(opts.UpstreamRemote != "", opts.UpstreamRemote). - ArgIf(opts.UpstreamBranch != "", fmt.Sprintf("refs/heads/%s:%s", opts.CurrentBranch, opts.UpstreamBranch)). + ArgIf(opts.Remote != "", opts.Remote). + Arg(opts.Refspecs...). ToArgv() cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest(task) diff --git a/pkg/commands/git_commands/sync_test.go b/pkg/commands/git_commands/sync_test.go index 6a7702586..82dcf2801 100644 --- a/pkg/commands/git_commands/sync_test.go +++ b/pkg/commands/git_commands/sync_test.go @@ -41,12 +41,11 @@ func TestSyncPush(t *testing.T) { }, }, { - testName: "Push with force disabled, upstream supplied", + testName: "Push with force disabled, refspec supplied", opts: PushOpts{ ForceWithLease: false, - CurrentBranch: "master", - UpstreamRemote: "origin", - UpstreamBranch: "master", + Remote: "origin", + Refspecs: []string{"refs/heads/master:master"}, }, test: func(cmdObj *oscommands.CmdObj, err error) { assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "refs/heads/master:master"}) @@ -57,9 +56,8 @@ func TestSyncPush(t *testing.T) { testName: "Push with force disabled, setting upstream", opts: PushOpts{ ForceWithLease: false, - CurrentBranch: "master-local", - UpstreamRemote: "origin", - UpstreamBranch: "master", + Remote: "origin", + Refspecs: []string{"refs/heads/master-local:master"}, SetUpstream: true, }, test: func(cmdObj *oscommands.CmdObj, err error) { @@ -71,9 +69,8 @@ func TestSyncPush(t *testing.T) { testName: "Push with force-with-lease enabled, setting upstream", opts: PushOpts{ ForceWithLease: true, - CurrentBranch: "master", - UpstreamRemote: "origin", - UpstreamBranch: "master", + Remote: "origin", + Refspecs: []string{"refs/heads/master:master"}, SetUpstream: true, }, test: func(cmdObj *oscommands.CmdObj, err error) { @@ -82,11 +79,23 @@ func TestSyncPush(t *testing.T) { }, }, { - testName: "Push with remote branch but no origin", + testName: "Push several refspecs", opts: PushOpts{ ForceWithLease: true, - UpstreamRemote: "", - UpstreamBranch: "master", + Remote: "origin", + Refspecs: []string{"refs/heads/a:refs/heads/a", "refs/heads/b:refs/heads/b"}, + }, + test: func(cmdObj *oscommands.CmdObj, err error) { + assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "origin", "refs/heads/a:refs/heads/a", "refs/heads/b:refs/heads/b"}) + assert.NoError(t, err) + }, + }, + { + testName: "Push with refspec but no remote", + opts: PushOpts{ + ForceWithLease: true, + Remote: "", + Refspecs: []string{"refs/heads/master:master"}, SetUpstream: true, }, test: func(cmdObj *oscommands.CmdObj, err error) { diff --git a/pkg/gui/controllers/sync_controller.go b/pkg/gui/controllers/sync_controller.go index 61b92747b..d787a54a8 100644 --- a/pkg/gui/controllers/sync_controller.go +++ b/pkg/gui/controllers/sync_controller.go @@ -195,15 +195,18 @@ type pushOpts struct { func (self *SyncController) pushAux(currentBranch *models.Branch, opts pushOpts) error { return self.c.WithInlineStatus(currentBranch, types.ItemOperationPushing, context.LOCAL_BRANCHES_CONTEXT_KEY, func(task gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.Push) + refspecs := []string{} + if opts.upstreamBranch != "" { + refspecs = append(refspecs, fmt.Sprintf("refs/heads/%s:%s", currentBranch.Name, opts.upstreamBranch)) + } err := self.c.Git().Sync.Push( task, git_commands.PushOpts{ Force: opts.force, ForceWithLease: opts.forceWithLease, - CurrentBranch: currentBranch.Name, - UpstreamRemote: opts.upstreamRemote, - UpstreamBranch: opts.upstreamBranch, SetUpstream: opts.setUpstream, + Remote: opts.upstreamRemote, + Refspecs: refspecs, }) if err != nil { if !opts.force && !opts.forceWithLease && strings.Contains(err.Error(), "Updates were rejected") {