Finding outdated mergeable branches with GitHub CLI and PowerShell in GitHub
Discover a quick method to easily retrieve outdated mergeable branches from your open pull requests

You've probably been there before: you open your favorite GitHub repository, select the open pull request, and filter on your name, trying to figure out which ones are still up-to-date and which ones are just… hanging around to be picked up.
Perhaps your fellow contributors have opened a PR, and it gets merged instantly. But your branches are left behind and you've to press that button again to get the latest changes from main.
As an active contributor, I wanted to solve this by running a simple command. Instead of wasting time manually opening the browser, I built a simple solution using the GitHub CLI, incorporating a touch of PowerShell. And the best part? It's a script you can adapt to your own workflow.
In this short blog post, you'll learn how you can use the GitHub CLI and PowerShell to create rich objects that can be used later.
Prerequisites
If you want to test out the script yourself, you're going to need:
- GitHub CLI v2.80 or above.
Why GitHub CLI with PowerShell
You could argue already: isn't GitHub's UI sufficient? Sort of, but it's not efficient if you have multiple pull requests open (like I do). The GitHub CLI gives you structured data in JSON format. Pair that with PowerShell, and you get the best of both worlds. This scriptable toolchain can:
- Fetch all open pull requests from a repository.
- Query whether the branch behind each PR is mergeable.
- Present the results in a clear, customizable format.
PowerShell is good with JSON. It can filter the data you need and even export the results to various formats, such as CSV or Markdown.
In other words, instead of wasting time through the UI, you can run one command in seconds.
Background
The heart of the solution is to query using the GitHub CLI (gh
) to check each PR's mergeability. GitHub documented each status (MergeStateStatus
and MergeableState
) based on a set of enums, so you know what will always be returned by the API. Here's the detailed status information for the MergeStateStatus
field returned by the CLI:
Status | Description |
---|---|
BEHIND |
The head ref is out of date |
BLOCKED |
The merge is blocked |
CLEAN |
Mergeable and passing commit status |
DIRTY |
The merge commit cannot be cleanly created |
DRAFT |
The merge is blocked due to the pull request being a draft |
HAS_HOOKS |
Mergeable with passing commit status and pre-receive hooks |
UNKNOWN |
The state cannot currently be determined |
UNSTABLE |
Mergeable with non-passing commit status |
For the MergeableState
, it only returns 3 values that can be checked:
State | Description |
---|---|
CONFLICTING |
The pull request cannot be merged due to merge conflicts |
MERGEABLE |
The pull request can be merged |
UNKNOWN |
The mergeability of the pull request is still being calculated |
Combine this with a strongly-typed PowerShell class and you get the best of both worlds. Let's see what this looks like.
The script in action
To make the output both reliable and easy to work with, I started defining a PowerShell class. This class models the properties I care about for each pull request:
- The pull request number
- Title
- Merge state
- The mergeability status
With this approach, you don't have to work with raw JSON, but instead, get back objects you can filter or sort on.
Here's the class definition:
# Find-MergeableGitHubPullRequests
class GitHubPullRequest
{
[int]$Number
[string]$Title
[string]$Url
[string]$MergeableState
[string]$MergeStateStatus
[bool]$IsMergeable
[string]$StatusDescription
[string]$MergeableDescription
GitHubPullRequest([int]$number, [string]$title, [string]$url, [string]$mergeableState, [string]$mergeStateStatus)
{
$this.Number = $number
$this.Title = $title
$this.Url = $url
$this.MergeableState = $mergeableState
$this.MergeStateStatus = $mergeStateStatus
$this.IsMergeable = ($mergeableState -eq 'MERGEABLE')
$this.StatusDescription = switch ($mergeStateStatus)
{
'BEHIND' { 'The head ref is out of date' }
'BLOCKED' { 'The merge is blocked' }
'CLEAN' { 'Mergeable and passing commit status' }
'DIRTY' { 'The merge commit cannot be cleanly created' }
'DRAFT' { 'The merge is blocked due to the pull request being a draft' }
'HAS_HOOKS' { 'Mergeable with passing commit status and pre-receive hooks' }
'UNKNOWN' { 'The state cannot currently be determined' }
'UNSTABLE' { 'Mergeable with non-passing commit status' }
default { 'Unknown status' }
}
$this.MergeableDescription = switch ($mergeableState)
{
'CONFLICTING' { 'The pull request cannot be merged due to merge conflicts' }
'MERGEABLE' { 'The pull request can be merged' }
'UNKNOWN' { 'The mergeability of the pull request is still being calculated' }
default { 'Unknown mergeable state' }
}
}
}
With this class in place, the next step is a small helper function to take the raw JSON returned by the GitHub CLI and turn it into an instance of GitHubPullRequest
:
function New-PullRequest
{
param (
[int]$number,
[string]$title,
[string]$url,
[string]$mergeableState,
[string]$mergeStateStatus
)
return [GitHubPullRequest]::new($number, $title, $url, $mergeableState, $mergeStateStatus)
}
Finally, the main function ties everything together. It calls gh pr list
with the right fields to return. Then, it leverages the helper function to convert the output to PowerShell objects:
function Find-MergeableGitHubPullRequests
{
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$Owner,
[Parameter(Mandatory = $true, Position = 1)]
[string]$Name
)
begin
{
Write-Verbose -Message "Starting $($MyInvocation.MyCommand.Name)"
$repo = "$Owner/$Name"
# the fields we want to retrieve from GitHub
$jsonProps = "url,title,mergeStateStatus,mergeable,number"
}
process
{
Write-Verbose -Message "Finding mergeable pull requests in repository '$repo'"
$pullRequests = gh pr list --repo $repo --author "@me" --state open --json $jsonProps | ConvertFrom-Json
if (-not $pullRequests -or $pullRequests.Count -eq 0) { return }
foreach ($pr in $pullRequests)
{
[GitHubPullRequest]::new($pr.number,
$pr.title,
$pr.url,
$pr.mergeable,
$pr.mergeStateStatus
)
}
}
end
{
Write-Verbose -Message "Ending $($MyInvocation.MyCommand.Name)"
}
}
To see the script in action, make sure you first use the gh auth login
and then execute the command seen in image below:
This way, you can instantly spot which pull request can be rebased, only with a merge commit, or isn't even mergeable without clicking through the GitHub UI.
Summary
By combining the GitHub CLI and PowerShell, you don't have to rely on the GitHub UI. You get a fast, scriptable way to surface exactly what you need.
The approach of using a strongly typed PowerShell class ensures reliable output. Can you take it a step further and schedule it as a recurring task to receive notifications? Give it a try!
I've published the whole script in my GitHub repository so you can adapt it to your own needs.