mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 21:46:49 -04:00
Generalize the push command to take a remote and a list of refspecs
PushOpts can only describe a push of the current branch. It takes the branch's name and the upstream to push it to, and builds a single refspec from them. Pushing several branches in one command needs one refspec per branch, so let callers pass the refspecs themselves. The sync controller, the only caller so far, builds the same single refspec that PushCmdObj built before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
fa427c32b3
commit
6378618544
@@ -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/<local branch>:<remote ref>".
|
||||
// If empty, git decides what to push based on push.default and
|
||||
// remote.<name>.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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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") {
|
||||
|
||||
Reference in New Issue
Block a user