From a89fa1ebed812c0941868da7bd370e91e82dd932 Mon Sep 17 00:00:00 2001 From: Neel Redkar Date: Thu, 24 Sep 2020 16:26:35 -0700 Subject: [PATCH] add ability to force checkout --- pkg/cmd/pr/checkout/checkout.go | 26 +++++++++++++++-- pkg/cmd/pr/checkout/checkout_test.go | 42 ++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/pr/checkout/checkout.go b/pkg/cmd/pr/checkout/checkout.go index 34db96f87..fc5002b0f 100644 --- a/pkg/cmd/pr/checkout/checkout.go +++ b/pkg/cmd/pr/checkout/checkout.go @@ -30,6 +30,7 @@ type CheckoutOptions struct { SelectorArg string RecurseSubmodules bool + Force bool } func NewCmdCheckout(f *cmdutil.Factory, runF func(*CheckoutOptions) error) *cobra.Command { @@ -61,6 +62,7 @@ func NewCmdCheckout(f *cmdutil.Factory, runF func(*CheckoutOptions) error) *cobr } cmd.Flags().BoolVarP(&opts.RecurseSubmodules, "recurse-submodules", "", false, "Update all active submodules (recursively)") + cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Force merge into local branch") return cmd } @@ -116,7 +118,12 @@ func checkoutRun(opts *CheckoutOptions) error { // local branch already exists if _, err := git.ShowRefs("refs/heads/" + newBranchName); err == nil { cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName}) - cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", fmt.Sprintf("refs/remotes/%s", remoteBranch)}) + // If forced reset to remote + if opts.Force { + cmdQueue = append(cmdQueue, []string{"git", "reset", "--hard", fmt.Sprintf("refs/remotes/%s", remoteBranch)}) + } else { + cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", fmt.Sprintf("refs/remotes/%s", remoteBranch)}) + } } else { cmdQueue = append(cmdQueue, []string{"git", "checkout", "-b", newBranchName, "--no-track", remoteBranch}) cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.remote", newBranchName), headRemote.Name}) @@ -139,11 +146,24 @@ func checkoutRun(opts *CheckoutOptions) error { ref := fmt.Sprintf("refs/pull/%d/head", pr.Number) if newBranchName == currentBranch { // PR head matches currently checked out branch + cmdQueue = append(cmdQueue, []string{"git", "fetch", baseURLOrName, ref}) - cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", "FETCH_HEAD"}) + + // If forced reset to remote + if opts.Force { + cmdQueue = append(cmdQueue, []string{"git", "reset", "--hard", "FETCH_HEAD"}) + } else { + cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", "FETCH_HEAD"}) + } } else { // create a new branch - cmdQueue = append(cmdQueue, []string{"git", "fetch", baseURLOrName, fmt.Sprintf("%s:%s", ref, newBranchName)}) + + // If forced reset to remote + if opts.Force { + cmdQueue = append(cmdQueue, []string{"git", "fetch", baseURLOrName, fmt.Sprintf("%s:%s", ref, newBranchName), "--force"}) + } else { + cmdQueue = append(cmdQueue, []string{"git", "fetch", baseURLOrName, fmt.Sprintf("%s:%s", ref, newBranchName)}) + } cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName}) } diff --git a/pkg/cmd/pr/checkout/checkout_test.go b/pkg/cmd/pr/checkout/checkout_test.go index 5042c0c84..aa68d0b9d 100644 --- a/pkg/cmd/pr/checkout/checkout_test.go +++ b/pkg/cmd/pr/checkout/checkout_test.go @@ -646,3 +646,45 @@ func TestPRCheckout_recurseSubmodules(t *testing.T) { assert.Equal(t, "git submodule sync --recursive", strings.Join(ranCommands[3], " ")) assert.Equal(t, "git submodule update --init --recursive", strings.Join(ranCommands[4], " ")) } + +func TestPRCheckout_force(t *testing.T) { + http := &httpmock.Registry{} + + http.Register(httpmock.GraphQL(`query PullRequestByNumber\b`), httpmock.StringResponse(` + { "data": { "repository": { "pullRequest": { + "number": 123, + "headRefName": "feature", + "headRepositoryOwner": { + "login": "hubot" + }, + "headRepository": { + "name": "REPO" + }, + "isCrossRepository": false, + "maintainerCanModify": false + } } } } + `)) + + ranCommands := [][]string{} + //nolint:staticcheck // SA1019 TODO: rewrite to use run.Stub + restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable { + switch strings.Join(cmd.Args, " ") { + case "git show-ref --verify -- refs/heads/feature": + return &test.OutputStub{} + default: + ranCommands = append(ranCommands, cmd.Args) + return &test.OutputStub{} + } + }) + defer restoreCmd() + + output, err := runCommand(http, nil, "master", `123 --force`) + + assert.NoError(t, err) + assert.Equal(t, "", output.String()) + + assert.Equal(t, len(ranCommands), 3) + assert.Equal(t, "git fetch origin +refs/heads/feature:refs/remotes/origin/feature", strings.Join(ranCommands[0], " ")) + assert.Equal(t, "git checkout feature", strings.Join(ranCommands[1], " ")) + assert.Equal(t, "git reset --hard refs/remotes/origin/feature", strings.Join(ranCommands[2], " ")) +}