2022-01-16 05:46:53 +02:00
|
|
|
package controllers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type SyncController struct {
|
2022-02-05 05:42:56 +02:00
|
|
|
baseController
|
2022-02-06 06:54:26 +02:00
|
|
|
*controllerCommon
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ types.IController = &SyncController{}
|
|
|
|
|
|
|
|
func NewSyncController(
|
2022-02-06 06:54:26 +02:00
|
|
|
common *controllerCommon,
|
2022-01-16 05:46:53 +02:00
|
|
|
) *SyncController {
|
|
|
|
return &SyncController{
|
2022-02-06 06:54:26 +02:00
|
|
|
baseController: baseController{},
|
|
|
|
controllerCommon: common,
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-05 01:31:07 +02:00
|
|
|
func (self *SyncController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
2022-01-16 05:46:53 +02:00
|
|
|
bindings := []*types.Binding{
|
|
|
|
{
|
2022-02-05 01:31:07 +02:00
|
|
|
Key: opts.GetKey(opts.Config.Universal.PushFiles),
|
|
|
|
Handler: opts.Guards.NoPopupPanel(self.HandlePush),
|
2022-01-16 05:46:53 +02:00
|
|
|
Description: self.c.Tr.LcPush,
|
|
|
|
},
|
|
|
|
{
|
2022-02-05 01:31:07 +02:00
|
|
|
Key: opts.GetKey(opts.Config.Universal.PullFiles),
|
|
|
|
Handler: opts.Guards.NoPopupPanel(self.HandlePull),
|
2022-01-16 05:46:53 +02:00
|
|
|
Description: self.c.Tr.LcPull,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return bindings
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) Context() types.Context {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) HandlePush() error {
|
|
|
|
return self.branchCheckedOut(self.push)()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) HandlePull() error {
|
|
|
|
return self.branchCheckedOut(self.pull)()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) branchCheckedOut(f func(*models.Branch) error) func() error {
|
|
|
|
return func() error {
|
2022-02-06 06:54:26 +02:00
|
|
|
currentBranch := self.helpers.Refs.GetCheckedOutRef()
|
2022-01-16 05:46:53 +02:00
|
|
|
if currentBranch == nil {
|
|
|
|
// need to wait for branches to refresh
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return f(currentBranch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) push(currentBranch *models.Branch) error {
|
|
|
|
// if we have pullables we'll ask if the user wants to force push
|
|
|
|
if currentBranch.IsTrackingRemote() {
|
2022-03-27 02:22:16 +02:00
|
|
|
opts := pushOpts{}
|
2022-01-16 05:46:53 +02:00
|
|
|
if currentBranch.HasCommitsToPull() {
|
|
|
|
return self.requestToForcePush(opts)
|
|
|
|
} else {
|
|
|
|
return self.pushAux(opts)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if self.git.Config.GetPushToCurrent() {
|
|
|
|
return self.pushAux(pushOpts{setUpstream: true})
|
|
|
|
} else {
|
2022-04-08 17:06:07 +02:00
|
|
|
return self.helpers.Upstream.PromptForUpstream(currentBranch, func(upstream string) error {
|
|
|
|
upstreamRemote, upstreamBranch, err := self.helpers.Upstream.ParseUpstream(upstream)
|
2022-03-27 02:22:16 +02:00
|
|
|
if err != nil {
|
|
|
|
return self.c.Error(err)
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return self.pushAux(pushOpts{
|
2022-03-27 02:22:16 +02:00
|
|
|
setUpstream: true,
|
2022-01-16 05:46:53 +02:00
|
|
|
upstreamRemote: upstreamRemote,
|
|
|
|
upstreamBranch: upstreamBranch,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) pull(currentBranch *models.Branch) error {
|
|
|
|
action := self.c.Tr.Actions.Pull
|
|
|
|
|
|
|
|
// if we have no upstream branch we need to set that first
|
|
|
|
if !currentBranch.IsTrackingRemote() {
|
2022-04-08 17:06:07 +02:00
|
|
|
return self.helpers.Upstream.PromptForUpstream(currentBranch, func(upstream string) error {
|
2022-03-27 02:22:16 +02:00
|
|
|
if err := self.setCurrentBranchUpstream(upstream); err != nil {
|
|
|
|
return self.c.Error(err)
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|
|
|
|
|
2022-03-27 02:22:16 +02:00
|
|
|
return self.PullAux(PullFilesOptions{Action: action})
|
2022-01-16 05:46:53 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-27 02:22:16 +02:00
|
|
|
return self.PullAux(PullFilesOptions{Action: action})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) setCurrentBranchUpstream(upstream string) error {
|
2022-04-08 17:06:07 +02:00
|
|
|
upstreamRemote, upstreamBranch, err := self.helpers.Upstream.ParseUpstream(upstream)
|
2022-03-27 02:22:16 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := self.git.Branch.SetCurrentBranchUpstream(upstreamRemote, upstreamBranch); err != nil {
|
|
|
|
if strings.Contains(err.Error(), "does not exist") {
|
|
|
|
return fmt.Errorf(
|
|
|
|
"upstream branch %s/%s not found.\nIf you expect it to exist, you should fetch (with 'f').\nOtherwise, you should push (with 'shift+P')",
|
|
|
|
upstreamRemote, upstreamBranch,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-16 05:46:53 +02:00
|
|
|
type PullFilesOptions struct {
|
|
|
|
UpstreamRemote string
|
|
|
|
UpstreamBranch string
|
|
|
|
FastForwardOnly bool
|
|
|
|
Action string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) PullAux(opts PullFilesOptions) error {
|
|
|
|
return self.c.WithLoaderPanel(self.c.Tr.PullWait, func() error {
|
|
|
|
return self.pullWithLock(opts)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) pullWithLock(opts PullFilesOptions) error {
|
|
|
|
self.c.LogAction(opts.Action)
|
|
|
|
|
|
|
|
err := self.git.Sync.Pull(
|
|
|
|
git_commands.PullOptions{
|
|
|
|
RemoteName: opts.UpstreamRemote,
|
|
|
|
BranchName: opts.UpstreamBranch,
|
|
|
|
FastForwardOnly: opts.FastForwardOnly,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2022-02-06 06:54:26 +02:00
|
|
|
return self.helpers.MergeAndRebase.CheckMergeOrRebase(err)
|
2022-01-16 05:46:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type pushOpts struct {
|
|
|
|
force bool
|
|
|
|
upstreamRemote string
|
|
|
|
upstreamBranch string
|
|
|
|
setUpstream bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) pushAux(opts pushOpts) error {
|
|
|
|
return self.c.WithLoaderPanel(self.c.Tr.PushWait, func() error {
|
|
|
|
self.c.LogAction(self.c.Tr.Actions.Push)
|
|
|
|
err := self.git.Sync.Push(git_commands.PushOpts{
|
|
|
|
Force: opts.force,
|
|
|
|
UpstreamRemote: opts.upstreamRemote,
|
|
|
|
UpstreamBranch: opts.upstreamBranch,
|
|
|
|
SetUpstream: opts.setUpstream,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
if !opts.force && strings.Contains(err.Error(), "Updates were rejected") {
|
|
|
|
forcePushDisabled := self.c.UserConfig.Git.DisableForcePushing
|
|
|
|
if forcePushDisabled {
|
|
|
|
_ = self.c.ErrorMsg(self.c.Tr.UpdatesRejectedAndForcePushDisabled)
|
|
|
|
return nil
|
|
|
|
}
|
2022-03-30 08:48:29 +02:00
|
|
|
_ = self.c.Confirm(types.ConfirmOpts{
|
2022-01-16 05:46:53 +02:00
|
|
|
Title: self.c.Tr.ForcePush,
|
|
|
|
Prompt: self.c.Tr.ForcePushPrompt,
|
|
|
|
HandleConfirm: func() error {
|
|
|
|
newOpts := opts
|
|
|
|
newOpts.force = true
|
|
|
|
|
|
|
|
return self.pushAux(newOpts)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
_ = self.c.Error(err)
|
|
|
|
}
|
|
|
|
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *SyncController) requestToForcePush(opts pushOpts) error {
|
|
|
|
forcePushDisabled := self.c.UserConfig.Git.DisableForcePushing
|
|
|
|
if forcePushDisabled {
|
|
|
|
return self.c.ErrorMsg(self.c.Tr.ForcePushDisabled)
|
|
|
|
}
|
|
|
|
|
2022-03-30 08:48:29 +02:00
|
|
|
return self.c.Confirm(types.ConfirmOpts{
|
2022-01-16 05:46:53 +02:00
|
|
|
Title: self.c.Tr.ForcePush,
|
|
|
|
Prompt: self.c.Tr.ForcePushPrompt,
|
|
|
|
HandleConfirm: func() error {
|
2022-03-27 02:22:16 +02:00
|
|
|
opts.force = true
|
2022-01-16 05:46:53 +02:00
|
|
|
return self.pushAux(opts)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|