Skip to content

Commit a89fa1e

Browse files
neelrmislav
authored andcommitted
add ability to force checkout
1 parent 77cc378 commit a89fa1e

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

pkg/cmd/pr/checkout/checkout.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type CheckoutOptions struct {
3030

3131
SelectorArg string
3232
RecurseSubmodules bool
33+
Force bool
3334
}
3435

3536
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
6162
}
6263

6364
cmd.Flags().BoolVarP(&opts.RecurseSubmodules, "recurse-submodules", "", false, "Update all active submodules (recursively)")
65+
cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Force merge into local branch")
6466

6567
return cmd
6668
}
@@ -116,7 +118,12 @@ func checkoutRun(opts *CheckoutOptions) error {
116118
// local branch already exists
117119
if _, err := git.ShowRefs("refs/heads/" + newBranchName); err == nil {
118120
cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName})
119-
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", fmt.Sprintf("refs/remotes/%s", remoteBranch)})
121+
// If forced reset to remote
122+
if opts.Force {
123+
cmdQueue = append(cmdQueue, []string{"git", "reset", "--hard", fmt.Sprintf("refs/remotes/%s", remoteBranch)})
124+
} else {
125+
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", fmt.Sprintf("refs/remotes/%s", remoteBranch)})
126+
}
120127
} else {
121128
cmdQueue = append(cmdQueue, []string{"git", "checkout", "-b", newBranchName, "--no-track", remoteBranch})
122129
cmdQueue = append(cmdQueue, []string{"git", "config", fmt.Sprintf("branch.%s.remote", newBranchName), headRemote.Name})
@@ -139,11 +146,24 @@ func checkoutRun(opts *CheckoutOptions) error {
139146
ref := fmt.Sprintf("refs/pull/%d/head", pr.Number)
140147
if newBranchName == currentBranch {
141148
// PR head matches currently checked out branch
149+
142150
cmdQueue = append(cmdQueue, []string{"git", "fetch", baseURLOrName, ref})
143-
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", "FETCH_HEAD"})
151+
152+
// If forced reset to remote
153+
if opts.Force {
154+
cmdQueue = append(cmdQueue, []string{"git", "reset", "--hard", "FETCH_HEAD"})
155+
} else {
156+
cmdQueue = append(cmdQueue, []string{"git", "merge", "--ff-only", "FETCH_HEAD"})
157+
}
144158
} else {
145159
// create a new branch
146-
cmdQueue = append(cmdQueue, []string{"git", "fetch", baseURLOrName, fmt.Sprintf("%s:%s", ref, newBranchName)})
160+
161+
// If forced reset to remote
162+
if opts.Force {
163+
cmdQueue = append(cmdQueue, []string{"git", "fetch", baseURLOrName, fmt.Sprintf("%s:%s", ref, newBranchName), "--force"})
164+
} else {
165+
cmdQueue = append(cmdQueue, []string{"git", "fetch", baseURLOrName, fmt.Sprintf("%s:%s", ref, newBranchName)})
166+
}
147167
cmdQueue = append(cmdQueue, []string{"git", "checkout", newBranchName})
148168
}
149169

pkg/cmd/pr/checkout/checkout_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,3 +646,45 @@ func TestPRCheckout_recurseSubmodules(t *testing.T) {
646646
assert.Equal(t, "git submodule sync --recursive", strings.Join(ranCommands[3], " "))
647647
assert.Equal(t, "git submodule update --init --recursive", strings.Join(ranCommands[4], " "))
648648
}
649+
650+
func TestPRCheckout_force(t *testing.T) {
651+
http := &httpmock.Registry{}
652+
653+
http.Register(httpmock.GraphQL(`query PullRequestByNumber\b`), httpmock.StringResponse(`
654+
{ "data": { "repository": { "pullRequest": {
655+
"number": 123,
656+
"headRefName": "feature",
657+
"headRepositoryOwner": {
658+
"login": "hubot"
659+
},
660+
"headRepository": {
661+
"name": "REPO"
662+
},
663+
"isCrossRepository": false,
664+
"maintainerCanModify": false
665+
} } } }
666+
`))
667+
668+
ranCommands := [][]string{}
669+
//nolint:staticcheck // SA1019 TODO: rewrite to use run.Stub
670+
restoreCmd := run.SetPrepareCmd(func(cmd *exec.Cmd) run.Runnable {
671+
switch strings.Join(cmd.Args, " ") {
672+
case "git show-ref --verify -- refs/heads/feature":
673+
return &test.OutputStub{}
674+
default:
675+
ranCommands = append(ranCommands, cmd.Args)
676+
return &test.OutputStub{}
677+
}
678+
})
679+
defer restoreCmd()
680+
681+
output, err := runCommand(http, nil, "master", `123 --force`)
682+
683+
assert.NoError(t, err)
684+
assert.Equal(t, "", output.String())
685+
686+
assert.Equal(t, len(ranCommands), 3)
687+
assert.Equal(t, "git fetch origin +refs/heads/feature:refs/remotes/origin/feature", strings.Join(ranCommands[0], " "))
688+
assert.Equal(t, "git checkout feature", strings.Join(ranCommands[1], " "))
689+
assert.Equal(t, "git reset --hard refs/remotes/origin/feature", strings.Join(ranCommands[2], " "))
690+
}

0 commit comments

Comments
 (0)