# How gh-stack Integrates with the GitHub Merge Queue: Detection and Async Routing

> Learn how gh-stack integrates with GitHub merge queues. Discover its detection method and async routing using GraphQL and the merge-async endpoint for efficient merges.

- Repository: [GitHub/gh-stack](https://github.com/github/gh-stack)
- Tags: how-to-guide
- Published: 2026-08-02

---

**gh-stack detects whether the target branch uses a merge queue via GraphQL, then routes the merge request through the `/pulls/{n}/merge-async` endpoint with the `merge_action` parameter explicitly set to `"merge_queue"` when a queue is configured.**

The `github/gh-stack` CLI extension automates the management of stacked pull requests, handling the complex dependencies between branches. When your repository configures GitHub’s merge queue for branch protection, gh-stack automatically detects this setting and adapts its merge workflow to enqueue entire stacks rather than attempting direct merges, ensuring compliance with repository rules while preserving stack integrity.

## Detecting Merge Queue Configuration

Before initiating any merge operation, gh-stack queries repository metadata to determine if the base branch requires merge queue processing. This detection occurs early in the merge flow to decide which UI prompts to display and which API parameters to include.

### GraphQL Detection Logic

The detection implementation resides in [`internal/github/merge_async.go`](https://github.com/github/gh-stack/blob/main/internal/github/merge_async.go). The `baseBranchUsesMergeQueue` function queries the repository via GraphQL to check for the presence of a `mergeQueue` object or a `MERGE_QUEUE` branch protection rule:

```go
usesMergeQueue := baseBranchUsesMergeQueue(client, base)

```

According to the source code at lines 60-67, this function returns `true` if either condition is present, signaling that the base branch enforces merge queue requirements. This boolean result drives subsequent logic in [`cmd/merge.go`](https://github.com/github/gh-stack/blob/main/cmd/merge.go) within the `runMerge` orchestration function.

### Branch Rule Evaluation

The helper specifically examines the repository's branch protection settings through the GitHub GraphQL API. When the query identifies an active merge queue configuration, gh-stack skips the standard merge-method selection workflow because the queue itself will determine the final merge strategy.

## Routing the Async Merge Request

Once detection completes, gh-stack adjusts both the user interface and the underlying API request to respect the queue configuration.

### Selecting the Merge Action

In [`cmd/merge.go`](https://github.com/github/gh-stack/blob/main/cmd/merge.go), the `runMergeHeadless` function constructs the appropriate merge action based on the detection result:

```go
mergeAction := mergeActionFor(usesMergeQueue) // "merge_queue" when true, otherwise "default"
res, err := client.MergeStackAsync(targetPR, method, mergeAction)

```

The `mergeActionFor` helper maps the boolean to specific constants defined in [`internal/github/merge_async.go`](https://github.com/github/gh-stack/blob/main/internal/github/merge_async.go) (lines 28-33):

- **`MergeActionDefault`** – Delegates the decision to the server based on repository settings
- **`MergeActionDirectMerge`** – Forces immediate merge bypassing the queue
- **`MergeActionMergeQueue`** – Explicitly targets the merge queue for asynchronous processing

### Submitting to the Merge-Async Endpoint

The `MergeStackAsync` function submits a POST request to the REST endpoint `/pulls/{n}/merge-async` with a JSON payload containing:

1. The `merge_method` (when applicable)
2. The `merge_action` field set to `"merge_queue"` when the base branch uses a queue

When the GitHub API receives `merge_action: "merge_queue"`, it routes the entire stack into the configured merge queue rather than performing an immediate merge.

## User Experience and CLI Feedback

After submitting the async request, gh-stack provides distinct terminal feedback based on the detection result. The implementation in [`cmd/merge.go`](https://github.com/github/gh-stack/blob/main/cmd/merge.go) (lines 70-74) prints:

- **With merge queue:** `"Adding #12, #13, #14 to the merge queue for main..."`
- **Without merge queue:** `"Merging #12, #13, #14 into main via squash..."`

This differentiation ensures users understand whether their stack is entering the merge queue for asynchronous validation or completing immediately via direct merge.

## Implementation Examples

### Detecting Queue Configuration

```go
func usesMergeQueue(client *github.Client, base string) bool {
    // Returns true if the base branch has a merge queue or a MERGE_QUEUE rule.
    ok, _ := client.BaseBranchUsesMergeQueue(base)
    return ok
}

```

### Submitting with Queue Awareness

```go
func submitStackMerge(client *github.Client, pr int, method string, base string) error {
    // Determine if the base branch uses a merge queue.
    usesQueue, _ := client.BaseBranchUsesMergeQueue(base)

    // Choose the appropriate merge_action.
    action := github.MergeActionDefault
    if usesQueue {
        action = github.MergeActionMergeQueue
    }

    // Fire the async request.
    result, err := client.MergeStackAsync(pr, method, action)
    if err != nil {
        return err
    }

    // Handle the possible outcomes.
    switch {
    case result.IsMerged():
        fmt.Printf("Stack merged directly (SHA %v)\n", result.Details.SHA)
    case result.IsEnqueued():
        fmt.Println("Stack added to the merge queue")
    case result.IsFailed():
        fmt.Printf("Merge failed: %s\n", result.Details.Message)
    }
    return nil
}

```

### Command-Line Usage

```bash

# Base branch uses a merge queue → enqueue the entire stack

$ gh stack merge --yes
Adding #12, #13, #14 to the merge queue for main...

# Base branch does NOT use a merge queue → direct merge with chosen method

$ gh stack merge --squash
Merging #12, #13, #14 into main via squash...

```

## Summary

- **GraphQL Detection:** The `baseBranchUsesMergeQueue` function in [`internal/github/merge_async.go`](https://github.com/github/gh-stack/blob/main/internal/github/merge_async.go) queries for `mergeQueue` objects or `MERGE_QUEUE` branch rules to determine if the base branch requires queue processing.
- **Action Constants:** Three merge actions are defined in [`merge_async.go`](https://github.com/github/gh-stack/blob/main/merge_async.go): `MergeActionDefault`, `MergeActionDirectMerge`, and `MergeActionMergeQueue`.
- **API Routing:** When a queue is detected, gh-stack sends `merge_action: "merge_queue"` to the `/pulls/{n}/merge-async` endpoint, causing the server to enqueue rather than immediately merge the stack.
- **UI Adaptation:** The CLI skips merge-method selection and displays "Adding to merge queue" messages when `usesMergeQueue` returns `true`.

## Frequently Asked Questions

### How does gh-stack detect if my repository uses a merge queue?

gh-stack calls `baseBranchUsesMergeQueue` in [`internal/github/merge_async.go`](https://github.com/github/gh-stack/blob/main/internal/github/merge_async.go), which executes a GraphQL query to check if the base branch has an active `mergeQueue` configuration or a `MERGE_QUEUE` branch protection rule. If either exists, the function returns `true` and gh-stack adjusts its workflow to use the asynchronous merge queue path.

### What endpoint does gh-stack use to submit stacks to the merge queue?

When a merge queue is detected, gh-stack sends a POST request to the `/pulls/{n}/merge-async` REST endpoint with a JSON payload containing `merge_action: "merge_queue"`. This parameter instructs the GitHub API to enqueue the stack for asynchronous processing according to the repository's queue configuration.

### Why does gh-stack skip the merge method selection when a merge queue is present?

When a merge queue is configured, the queue itself determines whether to use merge, squash, or rebase based on repository settings. Therefore, gh-stack skips the interactive merge-method selection step in the wizard because the user's choice would be overridden by the queue configuration anyway.

### Can I force gh-stack to bypass the merge queue and merge directly?

The source code defines a `MergeActionDirectMerge` constant, but the standard `runMergeHeadless` flow in [`cmd/merge.go`](https://github.com/github/gh-stack/blob/main/cmd/merge.go) automatically selects the action based on branch detection results. Attempting to bypass the queue may violate branch protection rules, so gh-stack defaults to respecting the repository's merge queue configuration when detected.