diff --git a/pkg/gui/controllers/sync_controller.go b/pkg/gui/controllers/sync_controller.go index 8501c5484..38f8db733 100644 --- a/pkg/gui/controllers/sync_controller.go +++ b/pkg/gui/controllers/sync_controller.go @@ -1,6 +1,7 @@ package controllers import ( + "errors" "fmt" "strings" @@ -74,13 +75,8 @@ func (self *SyncController) branchCheckedOut(f func(*models.Branch) error) func( 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() { - opts := pushOpts{ - force: false, - upstreamRemote: currentBranch.UpstreamRemote, - upstreamBranch: currentBranch.UpstreamBranch, - } + opts := pushOpts{} if currentBranch.HasCommitsToPull() { - opts.force = true return self.requestToForcePush(opts) } else { return self.pushAux(opts) @@ -90,21 +86,15 @@ func (self *SyncController) push(currentBranch *models.Branch) error { return self.pushAux(pushOpts{setUpstream: true}) } else { return self.promptForUpstream(currentBranch, func(upstream string) error { - var upstreamBranch, upstreamRemote string - split := strings.Split(upstream, " ") - if len(split) == 2 { - upstreamRemote = split[0] - upstreamBranch = split[1] - } else { - upstreamRemote = upstream - upstreamBranch = "" + upstreamRemote, upstreamBranch, err := self.parseUpstream(upstream) + if err != nil { + return self.c.Error(err) } return self.pushAux(pushOpts{ - force: false, + setUpstream: true, upstreamRemote: upstreamRemote, upstreamBranch: upstreamBranch, - setUpstream: true, }) }) } @@ -117,27 +107,46 @@ func (self *SyncController) pull(currentBranch *models.Branch) error { // if we have no upstream branch we need to set that first if !currentBranch.IsTrackingRemote() { return self.promptForUpstream(currentBranch, func(upstream string) error { - var upstreamBranch, upstreamRemote string - split := strings.Split(upstream, " ") - if len(split) != 2 { - return self.c.ErrorMsg(self.c.Tr.InvalidUpstream) + if err := self.setCurrentBranchUpstream(upstream); err != nil { + return self.c.Error(err) } - upstreamRemote = split[0] - upstreamBranch = split[1] - - if err := self.git.Branch.SetCurrentBranchUpstream(upstreamRemote, upstreamBranch); err != nil { - errorMessage := err.Error() - if strings.Contains(errorMessage, "does not exist") { - errorMessage = fmt.Sprintf("upstream branch %s not found.\nIf you expect it to exist, you should fetch (with 'f').\nOtherwise, you should push (with 'shift+P')", upstream) - } - return self.c.ErrorMsg(errorMessage) - } - return self.PullAux(PullFilesOptions{UpstreamRemote: upstreamRemote, UpstreamBranch: upstreamBranch, Action: action}) + return self.PullAux(PullFilesOptions{Action: action}) }) } - return self.PullAux(PullFilesOptions{UpstreamRemote: currentBranch.UpstreamRemote, UpstreamBranch: currentBranch.UpstreamBranch, Action: action}) + return self.PullAux(PullFilesOptions{Action: action}) +} + +func (self *SyncController) setCurrentBranchUpstream(upstream string) error { + upstreamRemote, upstreamBranch, err := self.parseUpstream(upstream) + 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 +} + +func (self *SyncController) parseUpstream(upstream string) (string, string, error) { + var upstreamBranch, upstreamRemote string + split := strings.Split(upstream, " ") + if len(split) != 2 { + return "", "", errors.New(self.c.Tr.InvalidUpstream) + } + + upstreamRemote = split[0] + upstreamBranch = split[1] + + return upstreamRemote, upstreamBranch, nil } func (self *SyncController) promptForUpstream(currentBranch *models.Branch, onConfirm func(string) error) error { @@ -229,6 +238,7 @@ func (self *SyncController) requestToForcePush(opts pushOpts) error { Title: self.c.Tr.ForcePush, Prompt: self.c.Tr.ForcePushPrompt, HandleConfirm: func() error { + opts.force = true return self.pushAux(opts) }, }) diff --git a/test/integration/forcePushMultiple/expected/.git_keep/COMMIT_EDITMSG b/test/integration/forcePushMultiple/expected/.git_keep/COMMIT_EDITMSG new file mode 100644 index 000000000..51be8ec3d --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/COMMIT_EDITMSG @@ -0,0 +1 @@ +myfile4 diff --git a/test/integration/forcePushMultiple/expected/.git_keep/FETCH_HEAD b/test/integration/forcePushMultiple/expected/.git_keep/FETCH_HEAD new file mode 100644 index 000000000..d56304e09 --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/FETCH_HEAD @@ -0,0 +1,2 @@ +b82ed4a67bef9ef50807adf409f103ef7b0832ab branch 'master' of ../actual_remote +d3708eeec2b9d69acbe87862330e844e85f77de1 not-for-merge branch 'other_branch' of ../actual_remote diff --git a/test/integration/forcePushMultiple/expected/.git_keep/HEAD b/test/integration/forcePushMultiple/expected/.git_keep/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/test/integration/forcePushMultiple/expected/.git_keep/ORIG_HEAD b/test/integration/forcePushMultiple/expected/.git_keep/ORIG_HEAD new file mode 100644 index 000000000..3774ff3d1 --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/ORIG_HEAD @@ -0,0 +1 @@ +d3708eeec2b9d69acbe87862330e844e85f77de1 diff --git a/test/integration/forcePushMultiple/expected/.git_keep/config b/test/integration/forcePushMultiple/expected/.git_keep/config new file mode 100644 index 000000000..740ff301f --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/config @@ -0,0 +1,21 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true +[user] + email = CI@example.com + name = CI +[push] + default = matching +[remote "origin"] + url = ../actual_remote + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "master"] + remote = origin + merge = refs/heads/master +[branch "other_branch"] + remote = origin + merge = refs/heads/other_branch diff --git a/test/integration/forcePushMultiple/expected/.git_keep/description b/test/integration/forcePushMultiple/expected/.git_keep/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/test/integration/forcePushMultiple/expected/.git_keep/index b/test/integration/forcePushMultiple/expected/.git_keep/index new file mode 100644 index 000000000..375819d60 Binary files /dev/null and b/test/integration/forcePushMultiple/expected/.git_keep/index differ diff --git a/test/integration/forcePushMultiple/expected/.git_keep/info/exclude b/test/integration/forcePushMultiple/expected/.git_keep/info/exclude new file mode 100644 index 000000000..8e9f2071f --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/info/exclude @@ -0,0 +1,7 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ +.DS_Store diff --git a/test/integration/forcePushMultiple/expected/.git_keep/logs/HEAD b/test/integration/forcePushMultiple/expected/.git_keep/logs/HEAD new file mode 100644 index 000000000..476f234fd --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/logs/HEAD @@ -0,0 +1,10 @@ +0000000000000000000000000000000000000000 16d8875e19987b16f1991a41fd3f4536d16f7cb4 CI 1648340487 +1100 commit (initial): myfile1 +16d8875e19987b16f1991a41fd3f4536d16f7cb4 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 commit: myfile2 +42d408cffcc087da21115f9ebc29e9765a2beb83 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 checkout: moving from master to other_branch +42d408cffcc087da21115f9ebc29e9765a2beb83 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 checkout: moving from other_branch to master +42d408cffcc087da21115f9ebc29e9765a2beb83 b82ed4a67bef9ef50807adf409f103ef7b0832ab CI 1648340487 +1100 commit: myfile3 +b82ed4a67bef9ef50807adf409f103ef7b0832ab 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 reset: moving to HEAD^ +42d408cffcc087da21115f9ebc29e9765a2beb83 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 checkout: moving from master to other_branch +42d408cffcc087da21115f9ebc29e9765a2beb83 d3708eeec2b9d69acbe87862330e844e85f77de1 CI 1648340487 +1100 commit: myfile4 +d3708eeec2b9d69acbe87862330e844e85f77de1 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 reset: moving to HEAD^ +42d408cffcc087da21115f9ebc29e9765a2beb83 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 checkout: moving from other_branch to master diff --git a/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/heads/master b/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/heads/master new file mode 100644 index 000000000..15de170c8 --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/heads/master @@ -0,0 +1,4 @@ +0000000000000000000000000000000000000000 16d8875e19987b16f1991a41fd3f4536d16f7cb4 CI 1648340487 +1100 commit (initial): myfile1 +16d8875e19987b16f1991a41fd3f4536d16f7cb4 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 commit: myfile2 +42d408cffcc087da21115f9ebc29e9765a2beb83 b82ed4a67bef9ef50807adf409f103ef7b0832ab CI 1648340487 +1100 commit: myfile3 +b82ed4a67bef9ef50807adf409f103ef7b0832ab 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 reset: moving to HEAD^ diff --git a/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/heads/other_branch b/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/heads/other_branch new file mode 100644 index 000000000..78ea80b06 --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/heads/other_branch @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 branch: Created from HEAD +42d408cffcc087da21115f9ebc29e9765a2beb83 d3708eeec2b9d69acbe87862330e844e85f77de1 CI 1648340487 +1100 commit: myfile4 +d3708eeec2b9d69acbe87862330e844e85f77de1 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 reset: moving to HEAD^ diff --git a/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/remotes/origin/master b/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/remotes/origin/master new file mode 100644 index 000000000..dd0a1b736 --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/remotes/origin/master @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 fetch origin: storing head +42d408cffcc087da21115f9ebc29e9765a2beb83 b82ed4a67bef9ef50807adf409f103ef7b0832ab CI 1648340487 +1100 update by push +b82ed4a67bef9ef50807adf409f103ef7b0832ab 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340489 +1100 update by push diff --git a/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/remotes/origin/other_branch b/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/remotes/origin/other_branch new file mode 100644 index 000000000..ca4e1389e --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/logs/refs/remotes/origin/other_branch @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340487 +1100 fetch origin: storing head +42d408cffcc087da21115f9ebc29e9765a2beb83 d3708eeec2b9d69acbe87862330e844e85f77de1 CI 1648340487 +1100 update by push +d3708eeec2b9d69acbe87862330e844e85f77de1 42d408cffcc087da21115f9ebc29e9765a2beb83 CI 1648340489 +1100 update by push diff --git a/test/integration/forcePushMultiple/expected/.git_keep/objects/0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52 b/test/integration/forcePushMultiple/expected/.git_keep/objects/0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52 new file mode 100644 index 000000000..7f2ebf4ee Binary files /dev/null and b/test/integration/forcePushMultiple/expected/.git_keep/objects/0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52 differ diff --git a/test/integration/forcePushMultiple/expected/.git_keep/objects/16/d8875e19987b16f1991a41fd3f4536d16f7cb4 b/test/integration/forcePushMultiple/expected/.git_keep/objects/16/d8875e19987b16f1991a41fd3f4536d16f7cb4 new file mode 100644 index 000000000..b72fd3fa1 --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/objects/16/d8875e19987b16f1991a41fd3f4536d16f7cb4 @@ -0,0 +1,2 @@ +xA +0@ѮsʌN'))c2!")#tyc-ei@,Č*XHFARN){OYsi8s^vhVZ 3rp%Btg='|3, \ No newline at end of file diff --git a/test/integration/forcePushMultiple/expected/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 b/test/integration/forcePushMultiple/expected/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 new file mode 100644 index 000000000..f74bf2335 Binary files /dev/null and b/test/integration/forcePushMultiple/expected/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 differ diff --git a/test/integration/forcePushMultiple/expected/.git_keep/objects/2b/173c861df433fa43ffad13f80c8b312c5c8bce b/test/integration/forcePushMultiple/expected/.git_keep/objects/2b/173c861df433fa43ffad13f80c8b312c5c8bce new file mode 100644 index 000000000..0a734f981 Binary files /dev/null and b/test/integration/forcePushMultiple/expected/.git_keep/objects/2b/173c861df433fa43ffad13f80c8b312c5c8bce differ diff --git a/test/integration/forcePushMultiple/expected/.git_keep/objects/42/d408cffcc087da21115f9ebc29e9765a2beb83 b/test/integration/forcePushMultiple/expected/.git_keep/objects/42/d408cffcc087da21115f9ebc29e9765a2beb83 new file mode 100644 index 000000000..31d3fad7a --- /dev/null +++ b/test/integration/forcePushMultiple/expected/.git_keep/objects/42/d408cffcc087da21115f9ebc29e9765a2beb83 @@ -0,0 +1,3 @@ +xA + E ɨJ!C &  myfile1 +git add . +git commit -am "myfile1" +echo test2 > myfile2 +git add . +git commit -am "myfile2" + +git checkout -b other_branch +git checkout master + +cd .. +git clone --bare ./actual actual_remote + +cd actual + +git remote add origin ../actual_remote +git fetch origin +git branch --set-upstream-to=origin/master master +git branch --set-upstream-to=origin/other_branch other_branch + +echo test3 > myfile3 +git add . +git commit -am "myfile3" + +git push origin master +git reset --hard HEAD^ + +git checkout other_branch + +echo test4 > myfile4 +git add . +git commit -am "myfile4" + +git push origin other_branch +git reset --hard HEAD^ + +git checkout master + +# at this point, both branches have diverged from their remote counterparts, meaning if you +# attempt to push either, it'll ask if you want to force push. diff --git a/test/integration/forcePushMultiple/test.json b/test/integration/forcePushMultiple/test.json new file mode 100644 index 000000000..f939494e9 --- /dev/null +++ b/test/integration/forcePushMultiple/test.json @@ -0,0 +1,4 @@ +{ + "description": "Force push to multiple branches because the user hasn't configured git to do otherwise", + "speed": 10 +}