Skip to content

Commit dde27d3

Browse files
committed
set merge action explicitly
1 parent 674fb99 commit dde27d3

8 files changed

Lines changed: 85 additions & 51 deletions

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ No Makefile, no code generation, no external linter config. Standard Go toolchai
1717

1818
- `cmd/`: One Cobra command per file. Each exports `<Name>Cmd(cfg *config.Config)` with logic in `run<Name>()`.
1919
- `internal/git/`: `Ops` interface (52 methods) wrapping git CLI. `MockOps` for tests. Package-level functions delegate to swappable `ops` variable.
20-
- `internal/github/`: `ClientOps` interface (18 methods) for GitHub API. `MockClient` for tests. Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`); merges use the async merge API (`/repos/{owner}/{repo}/pulls/{n}/merge-async`), which routes to a direct merge or the base branch's merge queue automatically.
20+
- `internal/github/`: `ClientOps` interface (18 methods) for GitHub API. `MockClient` for tests. Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`); merges use the async merge API (`/repos/{owner}/{repo}/pulls/{n}/merge-async`) with an explicit `merge_action` (`direct_merge` or `merge_queue`) selected from the base branch's merge-queue detection.
2121
- `internal/config/`: `Config` struct passed to all commands. Holds I/O, colors, and test hooks (`SelectFn`, `ConfirmFn`, `InputFn`, `GitHubClientOverride`).
2222
- `internal/stack/`: Stack file (`.git/gh-stack`, JSON) management with file locking.
2323
- `internal/tui/`: bubbletea views (`stackview`, `modifyview`).

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ if errors.As(err, &exitErr) { ... }
109109
### Key interfaces
110110

111111
- **`git.Ops`** (`internal/git/gitops.go`): 52 methods wrapping git CLI calls. The production implementation uses `cli/go-gh`'s `client.Command()` via `run()` and `runSilent()` helpers. Package-level functions (e.g., `git.CurrentBranch()`) delegate to a swappable package-level `ops` variable.
112-
- **`github.ClientOps`** (`internal/github/client_interface.go`): 17 methods for GitHub API (PRs, stacks, merges). Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`): `ListStacks`, `FindStackForPR`, `GetStack`, `CreateStack`, `AddToStack` (delta append), `Unstack`. Async stack merges use `RepoMergeConfig` (GraphQL: allowed merge methods + viewer's default), `MergeStackAsync`, and `GetAsyncMergeResult` (`/repos/{owner}/{repo}/pulls/{n}/merge-async`). Injected via `cfg.GitHubClientOverride` in tests.
112+
- **`github.ClientOps`** (`internal/github/client_interface.go`): 18 methods for GitHub API (PRs, stacks, merges). Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`): `ListStacks`, `FindStackForPR`, `GetStack`, `CreateStack`, `AddToStack` (delta append), `Unstack`. Async stack merges use `RepoMergeConfig` (GraphQL: allowed merge methods + viewer's default), `BaseBranchUsesMergeQueue` (GraphQL: detects a base-branch merge queue to select the explicit `merge_action`), `MergeStackAsync`, and `GetAsyncMergeResult` (`/repos/{owner}/{repo}/pulls/{n}/merge-async`). Injected via `cfg.GitHubClientOverride` in tests.
113113
- **`config.Config`** (`internal/config/config.go`): Central configuration passed to all commands. Holds I/O streams, color functions, and test hook fields (`SelectFn`, `ConfirmFn`, `InputFn`, `RepoOverride`).
114114

115115
### Stack file

cmd/merge.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ func runMergeInteractive(cfg *config.Config, client github.ClientOps, stackNumbe
313313
}
314314
}
315315

316-
submit, poll := mergeFuncs(client)
316+
submit, poll := mergeFuncs(client, mergeActionFor(usesMergeQueue))
317317

318318
model := mergeview.New(mergeview.Options{
319319
PRs: candidates,
@@ -373,7 +373,7 @@ func runMergeHeadless(cfg *config.Config, client github.ClientOps, base string,
373373
cfg.Printf("Merging %s into %s via %s...", list, base, method)
374374
}
375375

376-
res, err := client.MergeStackAsync(targetPR, method)
376+
res, err := client.MergeStackAsync(targetPR, method, mergeActionFor(usesMergeQueue))
377377
if err != nil {
378378
if errors.Is(err, github.ErrAsyncMergeUnavailable) {
379379
warnAsyncMergeUnavailable(cfg)
@@ -439,10 +439,10 @@ func runMergeHeadless(cfg *config.Config, client github.ClientOps, base string,
439439
}
440440

441441
// baseBranchUsesMergeQueue reports whether the stack's base branch merges through
442-
// a merge queue. Detection only tailors the wizard (skipping the method step and
443-
// switching to enqueue wording), so a lookup failure falls back to the
444-
// direct-merge flow: the async merge always sends merge_action "default" and the
445-
// server still routes to the queue when the branch requires one.
442+
// a merge queue. Detection tailors the wizard (skipping the method step and
443+
// switching to enqueue wording) and selects the explicit merge_action. On a
444+
// lookup failure it falls back to the direct-merge flow (method step shown,
445+
// "direct_merge" sent), matching what the user is shown.
446446
func baseBranchUsesMergeQueue(client github.ClientOps, base string) bool {
447447
uses, err := client.BaseBranchUsesMergeQueue(base)
448448
if err != nil {
@@ -451,11 +451,23 @@ func baseBranchUsesMergeQueue(client github.ClientOps, base string) bool {
451451
return uses
452452
}
453453

454+
// mergeActionFor maps merge-queue detection to the explicit async-merge action: a
455+
// detected queue forces "merge_queue" (the server rejects it when the branch has
456+
// no queue, guarding against a wrong guess), and everything else forces a
457+
// "direct_merge". Being explicit ensures the merge matches the wizard the user
458+
// saw rather than letting the server choose the routing.
459+
func mergeActionFor(usesMergeQueue bool) string {
460+
if usesMergeQueue {
461+
return github.MergeActionMergeQueue
462+
}
463+
return github.MergeActionDirectMerge
464+
}
465+
454466
// mergeFuncs returns submit/poll closures that adapt the GitHub client to the
455-
// mergeview injection points.
456-
func mergeFuncs(client github.ClientOps) (mergeview.SubmitFunc, mergeview.PollFunc) {
467+
// mergeview injection points. The merge action is fixed for the session.
468+
func mergeFuncs(client github.ClientOps, mergeAction string) (mergeview.SubmitFunc, mergeview.PollFunc) {
457469
submit := func(targetPR int, method string) (mergeview.MergeStatus, error) {
458-
res, err := client.MergeStackAsync(targetPR, method)
470+
res, err := client.MergeStackAsync(targetPR, method, mergeAction)
459471
if err != nil {
460472
return mergeview.MergeStatus{}, err
461473
}

cmd/merge_test.go

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func TestRunMerge_NoArg_MergesWholeStack(t *testing.T) {
9191
RepoMergeConfigFn: func() (*github.RepoMergeConfig, error) {
9292
return &github.RepoMergeConfig{MergeAllowed: true, SquashAllowed: true, RebaseAllowed: true, DefaultMethod: "squash"}, nil
9393
},
94-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
94+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
9595
gotPR, gotMethod = pr, method
9696
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusPending, Details: github.AsyncMergeDetails{UUID: "u"}}, nil
9797
},
@@ -111,14 +111,15 @@ func TestRunMerge_NoArg_MergesWholeStack(t *testing.T) {
111111

112112
func TestRunMerge_StackNumberArg(t *testing.T) {
113113
var gotPR int
114+
gotAction := "unset"
114115
cfg, outR, errR := config.NewTestConfig()
115116
cfg.GitHubClientOverride = &github.MockClient{
116117
GetStackFn: func(n int) (*github.RemoteStack, error) {
117118
assert.Equal(t, 7, n)
118119
return remoteStack(7, "main", openStackPR(10, "a"), openStackPR(11, "b")), nil
119120
},
120-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
121-
gotPR = pr
121+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
122+
gotPR, gotAction = pr, mergeAction
122123
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusPending, Details: github.AsyncMergeDetails{UUID: "u"}}, nil
123124
},
124125
}
@@ -128,11 +129,12 @@ func TestRunMerge_StackNumberArg(t *testing.T) {
128129

129130
require.NoError(t, err)
130131
assert.Equal(t, 11, gotPR)
132+
assert.Equal(t, github.MergeActionDirectMerge, gotAction, "a non-queue base sends an explicit direct_merge action")
131133
assert.Contains(t, output, "Merged #10, #11 into main")
132134
}
133135

134136
func TestRunMerge_MergeQueue_Headless(t *testing.T) {
135-
gotMethod := "unset"
137+
gotMethod, gotAction := "unset", "unset"
136138
cfg, outR, errR := config.NewTestConfig()
137139
cfg.GitHubClientOverride = &github.MockClient{
138140
GetStackFn: func(n int) (*github.RemoteStack, error) {
@@ -142,8 +144,8 @@ func TestRunMerge_MergeQueue_Headless(t *testing.T) {
142144
assert.Equal(t, "main", base)
143145
return true, nil
144146
},
145-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
146-
gotMethod = method
147+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
148+
gotMethod, gotAction = method, mergeAction
147149
return &github.AsyncMergeResult{
148150
Status: github.AsyncMergeStatusEnqueued,
149151
Details: github.AsyncMergeDetails{Message: "Pull request was added to the merge queue."},
@@ -156,19 +158,20 @@ func TestRunMerge_MergeQueue_Headless(t *testing.T) {
156158

157159
require.NoError(t, err)
158160
assert.Equal(t, "", gotMethod, "a merge queue picks the method; none is sent")
161+
assert.Equal(t, github.MergeActionMergeQueue, gotAction, "an explicit merge_queue action is sent")
159162
assert.Contains(t, output, "merge queue")
160163
}
161164

162165
func TestRunMerge_MergeQueue_IgnoresMethodFlag(t *testing.T) {
163-
gotMethod := "unset"
166+
gotMethod, gotAction := "unset", "unset"
164167
cfg, outR, errR := config.NewTestConfig()
165168
cfg.GitHubClientOverride = &github.MockClient{
166169
GetStackFn: func(n int) (*github.RemoteStack, error) {
167170
return remoteStack(7, "main", openStackPR(10, "a"), openStackPR(11, "b")), nil
168171
},
169172
BaseBranchUsesMergeQueueFn: func(base string) (bool, error) { return true, nil },
170-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
171-
gotMethod = method
173+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
174+
gotMethod, gotAction = method, mergeAction
172175
return &github.AsyncMergeResult{
173176
Status: github.AsyncMergeStatusEnqueued,
174177
Details: github.AsyncMergeDetails{Message: "queued"},
@@ -183,11 +186,12 @@ func TestRunMerge_MergeQueue_IgnoresMethodFlag(t *testing.T) {
183186

184187
require.NoError(t, err)
185188
assert.Equal(t, "", gotMethod, "the requested method is ignored under a merge queue")
189+
assert.Equal(t, github.MergeActionMergeQueue, gotAction)
186190
assert.Contains(t, output, "ignoring the merge method")
187191
}
188192

189193
func TestRunMerge_MergeQueueDetectionError_FallsBackToDirect(t *testing.T) {
190-
gotMethod := "unset"
194+
gotMethod, gotAction := "unset", "unset"
191195
cfg, outR, errR := config.NewTestConfig()
192196
cfg.GitHubClientOverride = &github.MockClient{
193197
GetStackFn: func(n int) (*github.RemoteStack, error) {
@@ -199,8 +203,8 @@ func TestRunMerge_MergeQueueDetectionError_FallsBackToDirect(t *testing.T) {
199203
RepoMergeConfigFn: func() (*github.RepoMergeConfig, error) {
200204
return &github.RepoMergeConfig{MergeAllowed: true, DefaultMethod: "merge"}, nil
201205
},
202-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
203-
gotMethod = method
206+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
207+
gotMethod, gotAction = method, mergeAction
204208
return &github.AsyncMergeResult{
205209
Status: github.AsyncMergeStatusMerged,
206210
Details: github.AsyncMergeDetails{SHA: "abc1234"},
@@ -213,6 +217,7 @@ func TestRunMerge_MergeQueueDetectionError_FallsBackToDirect(t *testing.T) {
213217

214218
require.NoError(t, err)
215219
assert.Equal(t, "merge", gotMethod, "detection failure falls back to a direct merge with a method")
220+
assert.Equal(t, github.MergeActionDirectMerge, gotAction, "the direct-merge UI sends an explicit direct_merge action")
216221
assert.Contains(t, output, "Merged")
217222
}
218223

@@ -227,7 +232,7 @@ func TestRunMerge_PRNumberArg(t *testing.T) {
227232
assert.Equal(t, 2, n)
228233
return remoteStack(5, "main", openStackPR(1, "b1"), openStackPR(2, "b2"), openStackPR(3, "b3")), nil
229234
},
230-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
235+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
231236
gotPR = pr
232237
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusPending, Details: github.AsyncMergeDetails{UUID: "u"}}, nil
233238
},
@@ -249,7 +254,7 @@ func TestRunMerge_SquashFlag(t *testing.T) {
249254
GetStackFn: func(n int) (*github.RemoteStack, error) {
250255
return remoteStack(7, "main", openStackPR(1, "b1"), openStackPR(2, "b2")), nil
251256
},
252-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
257+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
253258
gotMethod = method
254259
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusPending, Details: github.AsyncMergeDetails{UUID: "u"}}, nil
255260
},
@@ -367,7 +372,7 @@ func TestRunMerge_WholeStackBlockedByDraft(t *testing.T) {
367372
RepoMergeConfigFn: func() (*github.RepoMergeConfig, error) {
368373
return &github.RepoMergeConfig{MergeAllowed: true, DefaultMethod: "merge"}, nil
369374
},
370-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
375+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
371376
submitCalled = true
372377
return nil, nil
373378
},
@@ -405,7 +410,7 @@ func TestRunMerge_SubmitNotMergeable(t *testing.T) {
405410
GetStackFn: func(n int) (*github.RemoteStack, error) {
406411
return remoteStack(7, "main", openStackPR(1, "b1"), openStackPR(2, "b2")), nil
407412
},
408-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
413+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
409414
return nil, errors.New("the stack can no longer be merged as requested; refresh and try again")
410415
},
411416
}
@@ -424,7 +429,7 @@ func TestRunMerge_PollFailedConflict(t *testing.T) {
424429
GetStackFn: func(n int) (*github.RemoteStack, error) {
425430
return remoteStack(7, "main", openStackPR(1, "b1"), openStackPR(2, "b2")), nil
426431
},
427-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
432+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
428433
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusPending, Details: github.AsyncMergeDetails{UUID: "u"}}, nil
429434
},
430435
GetAsyncMergeResultFn: func(pr int, uuid string) (*github.AsyncMergeResult, error) {
@@ -446,7 +451,7 @@ func TestRunMerge_AlreadyMergedOnSubmit(t *testing.T) {
446451
GetStackFn: func(n int) (*github.RemoteStack, error) {
447452
return remoteStack(7, "main", openStackPR(1, "b1"), openStackPR(2, "b2")), nil
448453
},
449-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
454+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
450455
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusMerged, Details: github.AsyncMergeDetails{SHA: "abc"}}, nil
451456
},
452457
}
@@ -464,7 +469,7 @@ func TestRunMerge_Enqueued(t *testing.T) {
464469
GetStackFn: func(n int) (*github.RemoteStack, error) {
465470
return remoteStack(7, "main", openStackPR(1, "b1"), openStackPR(2, "b2")), nil
466471
},
467-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
472+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
468473
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusPending, Details: github.AsyncMergeDetails{UUID: "u"}}, nil
469474
},
470475
GetAsyncMergeResultFn: func(pr int, uuid string) (*github.AsyncMergeResult, error) {
@@ -485,7 +490,7 @@ func TestRunMerge_EnqueuedOnSubmit(t *testing.T) {
485490
GetStackFn: func(n int) (*github.RemoteStack, error) {
486491
return remoteStack(7, "main", openStackPR(1, "b1"), openStackPR(2, "b2")), nil
487492
},
488-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
493+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
489494
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusEnqueued, Details: github.AsyncMergeDetails{Message: "Pull request was added to the merge queue."}}, nil
490495
},
491496
}
@@ -503,7 +508,7 @@ func TestRunMerge_AsyncMergeUnavailable(t *testing.T) {
503508
GetStackFn: func(n int) (*github.RemoteStack, error) {
504509
return remoteStack(7, "main", openStackPR(1, "b1"), openStackPR(2, "b2")), nil
505510
},
506-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
511+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
507512
return nil, github.ErrAsyncMergeUnavailable
508513
},
509514
}
@@ -554,7 +559,7 @@ func TestRunMerge_DefaultMethodFallsBackToAllowed(t *testing.T) {
554559
// Viewer default is a method the repo no longer allows.
555560
return &github.RepoMergeConfig{SquashAllowed: true, DefaultMethod: "merge"}, nil
556561
},
557-
MergeStackAsyncFn: func(pr int, method string) (*github.AsyncMergeResult, error) {
562+
MergeStackAsyncFn: func(pr int, method, mergeAction string) (*github.AsyncMergeResult, error) {
558563
gotMethod = method
559564
return &github.AsyncMergeResult{Status: github.AsyncMergeStatusPending, Details: github.AsyncMergeDetails{UUID: "u"}}, nil
560565
},

internal/github/client_interface.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type ClientOps interface {
1818
AddToStack(stackNumber int, prNumbers []int) (*RemoteStack, error)
1919
Unstack(stackNumber int) (*RemoteStack, bool, error)
2020
RepoMergeConfig() (*RepoMergeConfig, error)
21-
MergeStackAsync(prNumber int, method string) (*AsyncMergeResult, error)
21+
MergeStackAsync(prNumber int, method, mergeAction string) (*AsyncMergeResult, error)
2222
GetAsyncMergeResult(prNumber int, uuid string) (*AsyncMergeResult, error)
2323
PRTitles(numbers []int) (map[int]string, error)
2424
BaseBranchUsesMergeQueue(baseRef string) (bool, error)

internal/github/merge_async.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ const (
1919
MergeMethodRebase = "rebase"
2020
)
2121

22+
// Merge action values for the async merge API's merge_action field. "default"
23+
// lets the server choose between a direct merge and the base branch's merge
24+
// queue; "direct_merge" and "merge_queue" force the respective path. Sending an
25+
// explicit action makes the caller's merge-queue detection authoritative: the
26+
// server rejects "merge_queue" on a branch with no queue rather than silently
27+
// merging directly.
28+
const (
29+
MergeActionDefault = "default"
30+
MergeActionDirectMerge = "direct_merge"
31+
MergeActionMergeQueue = "merge_queue"
32+
)
33+
2234
// ErrAsyncMergeUnavailable indicates the async merge API is not available for
2335
// the repository (or the token lacks access). Surfaced on a 404 from the submit
2436
// endpoint.
@@ -196,21 +208,26 @@ func (c *Client) BaseBranchUsesMergeQueue(baseRef string) (bool, error) {
196208
// a stacked PR this merges all members of the stack up to and including
197209
// prNumber. A blank method lets the server apply its default.
198210
//
199-
// The merge_action is always "default", which lets the server route the stack
200-
// to a direct merge or the base branch's merge queue automatically, based on
201-
// the repository's rules and configuration.
211+
// mergeAction selects the routing: MergeActionDirectMerge or MergeActionMergeQueue
212+
// force the respective path, and MergeActionDefault (also the fallback for an
213+
// empty value) lets the server choose. Sending an explicit action makes the
214+
// caller's merge-queue detection authoritative — the server rejects
215+
// "merge_queue" on a branch with no queue instead of silently merging directly.
202216
//
203217
// On success the returned result is populated for the 200 (already merged) and
204218
// 202 (enqueued for background processing) responses. A 404 returns
205219
// ErrAsyncMergeUnavailable, a 409 (a request already exists) returns a clear
206220
// "already exists" error, and any other non-2xx status is returned as-is.
207-
func (c *Client) MergeStackAsync(prNumber int, method string) (*AsyncMergeResult, error) {
221+
func (c *Client) MergeStackAsync(prNumber int, method, mergeAction string) (*AsyncMergeResult, error) {
222+
if mergeAction == "" {
223+
mergeAction = MergeActionDefault
224+
}
208225
type reqBody struct {
209226
MergeMethod string `json:"merge_method,omitempty"`
210227
MergeAction string `json:"merge_action"`
211228
}
212229

213-
body, err := json.Marshal(reqBody{MergeMethod: method, MergeAction: "default"})
230+
body, err := json.Marshal(reqBody{MergeMethod: method, MergeAction: mergeAction})
214231
if err != nil {
215232
return nil, fmt.Errorf("marshaling request: %w", err)
216233
}

0 commit comments

Comments
 (0)