Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pkg/commands/git_commands/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func NewSyncCommands(gitCommon *GitCommon) *SyncCommands {
// Push pushes to a branch
type PushOpts struct {
Force bool
ForceWithLease bool
UpstreamRemote string
UpstreamBranch string
SetUpstream bool
Expand All @@ -30,7 +31,8 @@ func (self *SyncCommands) PushCmdObj(task gocui.Task, opts PushOpts) (oscommands
}

cmdArgs := NewGitCmd("push").
ArgIf(opts.Force, "--force-with-lease").
ArgIf(opts.Force, "--force").
ArgIf(opts.ForceWithLease, "--force-with-lease").
ArgIf(opts.SetUpstream, "--set-upstream").
ArgIf(opts.UpstreamRemote != "", opts.UpstreamRemote).
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
Expand Down
22 changes: 15 additions & 7 deletions pkg/commands/git_commands/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,32 @@ func TestSyncPush(t *testing.T) {
scenarios := []scenario{
{
testName: "Push with force disabled",
opts: PushOpts{Force: false},
opts: PushOpts{ForceWithLease: false},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push"})
assert.NoError(t, err)
},
},
{
testName: "Push with force-with-lease enabled",
opts: PushOpts{ForceWithLease: true},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
assert.NoError(t, err)
},
},
{
testName: "Push with force enabled",
opts: PushOpts{Force: true},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force"})
assert.NoError(t, err)
},
},
{
testName: "Push with force disabled, upstream supplied",
opts: PushOpts{
Force: false,
ForceWithLease: false,
UpstreamRemote: "origin",
UpstreamBranch: "master",
},
Expand All @@ -47,7 +55,7 @@ func TestSyncPush(t *testing.T) {
{
testName: "Push with force disabled, setting upstream",
opts: PushOpts{
Force: false,
ForceWithLease: false,
UpstreamRemote: "origin",
UpstreamBranch: "master",
SetUpstream: true,
Expand All @@ -58,9 +66,9 @@ func TestSyncPush(t *testing.T) {
},
},
{
testName: "Push with force enabled, setting upstream",
testName: "Push with force-with-lease enabled, setting upstream",
opts: PushOpts{
Force: true,
ForceWithLease: true,
UpstreamRemote: "origin",
UpstreamBranch: "master",
SetUpstream: true,
Expand All @@ -73,7 +81,7 @@ func TestSyncPush(t *testing.T) {
{
testName: "Push with remote branch but no origin",
opts: PushOpts{
Force: true,
ForceWithLease: true,
UpstreamRemote: "",
UpstreamBranch: "master",
SetUpstream: true,
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/models/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (b *Branch) IsBehindForPull() bool {
}

func (b *Branch) IsBehindForPush() bool {
return b.BehindForPush != "" && b.BehindForPush != "0"
return b.RemoteBranchStoredLocally() && b.BehindForPush != "0"
}

// for when we're in a detached head state
Expand Down
34 changes: 30 additions & 4 deletions pkg/gui/controllers/sync_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (self *SyncController) branchCheckedOut(f func(*models.Branch) error) func(
func (self *SyncController) push(currentBranch *models.Branch) error {
// if we are behind our upstream branch we'll ask if the user wants to force push
if currentBranch.IsTrackingRemote() {
opts := pushOpts{}
opts := pushOpts{remoteBranchStoredLocally: currentBranch.RemoteBranchStoredLocally()}
if currentBranch.IsBehindForPush() {
return self.requestToForcePush(currentBranch, opts)
} else {
Expand Down Expand Up @@ -180,9 +180,16 @@ func (self *SyncController) pullWithLock(task gocui.Task, opts PullFilesOptions)

type pushOpts struct {
force bool
forceWithLease bool
upstreamRemote string
upstreamBranch string
setUpstream bool

// If this is false, we can't tell ahead of time whether a force-push will
// be necessary, so we start with a normal push and offer to force-push if
// the server rejected. If this is true, we don't offer to force-push if the
// server rejected, but rather ask the user to fetch.
remoteBranchStoredLocally bool
}

func (self *SyncController) pushAux(currentBranch *models.Branch, opts pushOpts) error {
Expand All @@ -192,13 +199,32 @@ func (self *SyncController) pushAux(currentBranch *models.Branch, opts pushOpts)
task,
git_commands.PushOpts{
Force: opts.force,
ForceWithLease: opts.forceWithLease,
UpstreamRemote: opts.upstreamRemote,
UpstreamBranch: opts.upstreamBranch,
SetUpstream: opts.setUpstream,
})
if err != nil {
if strings.Contains(err.Error(), "Updates were rejected") {
return errors.New(self.c.Tr.UpdatesRejected)
if !opts.force && !opts.forceWithLease && strings.Contains(err.Error(), "Updates were rejected") {
if opts.remoteBranchStoredLocally {
return errors.New(self.c.Tr.UpdatesRejected)
}

forcePushDisabled := self.c.UserConfig.Git.DisableForcePushing
if forcePushDisabled {
return errors.New(self.c.Tr.UpdatesRejectedAndForcePushDisabled)
}
_ = self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.ForcePush,
Prompt: self.forcePushPrompt(),
HandleConfirm: func() error {
newOpts := opts
newOpts.force = true

return self.pushAux(currentBranch, newOpts)
},
})
return nil
}
return err
}
Expand All @@ -216,7 +242,7 @@ func (self *SyncController) requestToForcePush(currentBranch *models.Branch, opt
Title: self.c.Tr.ForcePush,
Prompt: self.forcePushPrompt(),
HandleConfirm: func() error {
opts.force = true
opts.forceWithLease = true
return self.pushAux(currentBranch, opts)
},
})
Expand Down
Loading