This is the equivalent to git log master..my-cool-branch using Octokit - you want to see if there are any commits on your branch which are not in master:

var head = "my-cool-branch";
var baseBranch = "master";
var compareBaseToHead = await client.Repository.Commit.Compare(owner, repo, baseBranch, head);

if (compareBaseToHead.TotalCommits == 0)
{
    Console.WriteLine("Branch {0} has been merged into {1}", head, baseBranch);
}
else
{
    Console.WriteLine("Branch {0} has NOT been merged into {1}", head, baseBranch);
}
Answer from Brendan Forster on Stack Overflow
🌐
GitHub
docs.github.com › en › repositories › configuring-branches-and-merges-in-your-repository › configuring-pull-request-merges › managing-a-merge-queue
Managing a merge queue - GitHub Docs
The merge queue creates a temporary branch with the prefix of main/pr-2 that contains code changes from the target branch, pull request #1, and pull request #2, and dispatches webhooks. When the GitHub API receives successful CI responses for merge_group branches main/pr-1 and main/pr-2, the temporary branch main/pr-2 will be merged in to the target branch.
🌐
GitHub
docs.github.com › en › rest › branches › branches
REST API endpoints for branches - GitHub Docs
{ "message": "Successfully fetched and fast-forwarded from upstream defunkt:main", "merge_type": "fast-forward", "base_branch": "defunkt:main" } This endpoint works with the following fine-grained token types: ... curl -L \ -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer <YOUR-TOKEN>" \ -H "X-GitHub-Api-Version: 2026-03-10" \ https://api.github.com/repos/OWNER/REPO/merges \ -d '{"base":"master","head":"cool_feature","commit_message":"Shipped cool_feature!"}'
🌐
Jamie Tanna
jvt.me › posts › 2023 › 07 › 13 › github-merge-api-manual
Merging a branch in GitHub - the hard way · Jamie Tanna | Software Engineer
July 13, 2023 - curl -L \ -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GITHUB_TOKEN"\ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/jamietanna/github-merge-api-manual/git/commits \ -d '{ "author": { "date": "2023-07-13T18:34:35+01:00", "email": "example@example.com", "name": "The Merge Bot" }, "message": "Merge branch feature/something into shared-branch", "parents": [ "0bda88fbc64fe82f8630cd301f78230132d9eccb", "cd67b7454723133d33c72e445e943b11c0240a11" ], "signature": "", "tree": "27e7cf23f961cf4c405b72fdc10a12d079e5e045" }'
Top answer
1 of 3
5

This is the equivalent to git log master..my-cool-branch using Octokit - you want to see if there are any commits on your branch which are not in master:

var head = "my-cool-branch";
var baseBranch = "master";
var compareBaseToHead = await client.Repository.Commit.Compare(owner, repo, baseBranch, head);

if (compareBaseToHead.TotalCommits == 0)
{
    Console.WriteLine("Branch {0} has been merged into {1}", head, baseBranch);
}
else
{
    Console.WriteLine("Branch {0} has NOT been merged into {1}", head, baseBranch);
}
2 of 3
2

I actually just finished a script that incorporates this kind of check. The overall goal of my script is to look at all repos in our org and clean up branches that have been merged. The script is written in groovy and uses kohsuke's github-api java client. There was one feature missing from the java client which I have a PR open for. Until that PR is merged and a new version released, you'll have to either omit the branch protection check or do what I did (pull the code yourself and build it with the feature added).

The logic checking whether a branch has been merged and has no new commits to merge is in the cleanupBranches method. Basically, I loop through all CLOSED pull requests (PR) for a given repo. If the pull request was merged, I then do a compare of the PR's head branch against the repo's default branch (not all of ours are named master). If the PR's head branch has 0 commits that haven't been merged to the default branch, it's ok to delete the branch.

I included my entire script just for context. You will notice that I did change some configuration property defaults.

@GrabResolver(name='binrepo', root='https://binrepo.myCompany.com')
@Grab(group='org.kohsuke', module='github-api', version='SNAPSHOT')

import org.kohsuke.github.*

// store some things here so they are available everywhere
class Config {
  static Boolean doDelete = Boolean.valueOf(System.getProperty('branch.cleanup.doDelete', 'false'))
  static String  orgName = System.getProperty('branch.cleanup.gitOrgName', 'myOrg')
  static String  gitApiUrl = System.getProperty('branch.cleanup.gitApiUrl', 'https://git.myCompany.com/api/v3')
  static String  apiKey
}

def executeOnShell(String command, boolean log = false) {
  File workingDir = new File(System.properties.'user.dir')
  def process = new ProcessBuilder(addShellPrefix(command))
      .directory(workingDir)
      .redirectErrorStream(true)
      .start()
  if (log) {
    process.inputStream.eachLine { println it }
  }

  process.waitFor();
}

def addShellPrefix(String command) {
  def commandArray = new String[3]
  commandArray[0] = "sh"
  commandArray[1] = "-c"
  commandArray[2] = command
  return commandArray
}

def allRepos(GHOrganization org, String...repoPrefixes) {
  println "Fetching all repositories under the ${Config.orgName} org that match prefix(es) ${repoPrefixes}"
  return org.getRepositories().entrySet().findAll{ entry ->
    if (repoPrefixes) {
      return repoPrefixes.any{ prefix -> entry.key.startsWith(prefix) }
    } else {
      return true
    }
  }
}

def cleanupBranches(repo) {
  def defaultBranchName = repo.getDefaultBranch()
  def defaultBranch = repo.getBranch(defaultBranchName)
  def deletedBranchNames = []
  def branchesByName = repo.getBranches().entrySet().findAll{ !it.key.equals(defaultBranchName) && !it.value.isProtected() }.collectEntries{[it.key, it.value]}
  def pullRequests = repo.queryPullRequests().base(defaultBranchName).state(GHIssueState.CLOSED).list().withPageSize(100).each{ pr ->
    // loop thru all pull requests that have been closed and also merged
    if (pr.isMerged()) {
      def branch = branchesByName.get(pr.getHead().getRef())
      if (branch) {
        // the branch still exists and has been merged by this PR
        // make sure it doesn't have any unmerged commits
        def compare = repo.getCompare(defaultBranch, branch)
        if (compare.getTotalCommits() == 0) {
          // branch has been merged and there are 0 commits since merge. delete it
          println "Branch ${repo.getName()}/${branch.getName()} has 0 commits not merged to ${defaultBranchName}. Delete it. PR ${pr.getNumber()} : ${pr.getTitle()}"
          if (Config.doDelete) {
            deleteBranch(repo, branch)
          }

          // remove from internal map of branches since the branch has now been deleted in git
          branchesByName.remove(branch.getName())
          deletedBranchNames.push "${repo.getName()}/${branch.getName()}"
        }
      }
    }
  }
  return deletedBranchNames
}

def deleteBranch(repo, branch) {
  // use a simple curl here because the kohsuke library way of doing it requires 2 api calls when just 1 will do here  
  String cmd = "curl -X DELETE -H \"Authorization: token ${Config.apiKey}\" ${Config.gitApiUrl}/repos/${Config.orgName}/${repo.getName()}/git/refs/heads/${branch.getName()}"
  executeOnShell(cmd)
}

if (args.size() < 1) {
  println "Usage: cleanupRepoBranches.groovy <oauthToken> <optionalRepo-name>"
  System.exit(1)
}

Config.apiKey = args[0]

def branchesDeleted = []
def errors = []
GitHub github = GitHub.connectToEnterprise(Config.gitApiUrl, Config.apiKey)
if (args.size() > 1) {
  String repoName = args[1]
  GHRepository repo = github.getRepository("${Config.orgName}/${repoName}")
  branchesDeleted = cleanupBranches(repo)
} else {
  def repoPrefixes = System.getProperty('branch.cleanup.repoPrefixes', 'pref-,pref2-').split(',')
  def answer = System.console().readLine "You have not specified a repoName. If you proceed, this script will list ${Config.doDelete ? 'and delete ' : ''}all branches with a merged pull request and 0 commits left to merge for all repos starting with ${repoPrefixes.join(', ')} in the ${Config.orgName} org. Are you sure? (y/n) "
  if (answer == 'y') {
    println 'ok! here we go!'
    allRepos(github.getOrganization(Config.orgName), repoPrefixes).each { entry ->
      try {
        branchesDeleted += cleanupBranches(entry.value)
      } catch (Exception e) {
        errors.push([ message: "Error processing branches for ${entry.key} repo", ex: e ])
      }
    }
  }
}
println "${branchesDeleted.size()} Branches deleted..."
branchesDeleted.each{ branch -> println branch }
println "${errors.size()} errors..."
errors.each{ error ->
  println error.message
  error.ex.printStackTrace()
  println
}
🌐
GitHub
docs.github.com › en › enterprise-cloud@latest › rest › guides › using-the-rest-api-to-interact-with-your-git-database
Using the REST API to interact with your Git database - GitHub Enterprise Cloud Docs
A test merge commit is created when you view the pull request in the UI and the "Merge" button is displayed, or when you get, create, or edit a pull request using the REST API. Without this request, the merge Git refs will fall out of date until the next time someone views the pull request. If you are currently using polling methods that produce outdated merge Git refs, then GitHub recommends using the following steps to get the latest changes from the default branch:
🌐
GitHub
docs.github.com › en › rest › pulls
REST API endpoints for pull requests - GitHub Docs
REST API endpoints for pull requests · List pull requests · Create a pull request · Get a pull request · Update a pull request · List commits on a pull request · List pull requests files · Check if a pull request has been merged · Merge a pull request · Update a pull request branch ·
🌐
GitHub
docs.github.com › articles › merging-a-pull-request
Merging a pull request - GitHub Docs
You can't merge a draft pull request. For more information about draft pull requests, see About pull requests. The repository may be configured so that the head branch for a pull request is automatically deleted when you merge a pull request.
🌐
GitHub
github.blog › home › changelogs › pull request merge queue (public beta): api support and recent fixes
Pull request merge queue (public beta): API support and recent fixes - GitHub Changelog
March 22, 2025 - You can now interact with merge queue programmatically using new GraphQL APIs. Add or remove a pull request from the queue, see which pull requests are queued, get details about a queued pull request, and more. For example: Call the enqueuePullRequest mutation to add a pull request to the queue or dequeuePullRequest to remove a pull request. Use the mergeQueue field on Repository to list its contents or configuration.
🌐
GitHub
docs.github.com › en › rest › branches
REST API endpoints for branches and their settings - GitHub Docs
REST API endpoints for branches · List branches · Get a branch · Rename a branch · Sync a fork branch with the upstream repository · Merge a branch · REST API endpoints for protected branches · Get branch protection · Update branch protection · Delete branch protection ·
Find elsewhere
🌐
Git
git-scm.com › docs › api-merge
Git - api-merge Documentation
The merge API helps a program to reconcile two competing sets of improvements to some files (e.g., unregistered changes from the work tree versus changes involved in switching to a new branch), reporting conflicts if found.
🌐
Educative
educative.io › home › courses › managing your codebase with the github api in python › branches
Manage GitHub Repository Branches with API in Python
Learn how to list, get, rename, and merge branches in GitHub repositories using the GitHub API with Python for better code management.
🌐
Apache JIRA
issues.apache.org › jira › browse › ARROW-16602
[ARROW-16602] [Dev] Use GitHub API to merge pull request - ASF Jira
January 11, 2023 - See https://github.com/apache/arrow/pull/13180 for example. I used GitHub API to merge the pull request. And we don't need to create a local branch on local repository to merge a pull request.
Top answer
1 of 2
2

TL;DR

Don't use a REST API for this.1 Git doesn't have one. Use git branch --merged. But be aware of what it actually does.


1If you do use one, be aware that it almost certainly just uses git branch --merged, or an internal equivalent. Also, be aware that the REST API on hosting server A is different from the one on hosting server B, which is different from the one on hosting server C. Hosting server D doesn't have an API for it. But Git can do it natively, and this will work regardless of your hosting server.


Long

Branches, in Git, don't exist.

OK, that's obviously not true: they do exist. But in a very important way, they don't matter. Only commits really matter. Branch names mainly serve to find commits. Merging is done by merging commits, even if you use a branch name in the process. So the trick is to ask the question about commits first. Only later, after you have that answer, should you start wondering about branch names.

Because of this, it's in general impossible to say which branch was merged. But it is easy to tell if some particular commit is merged. And, a branch name, like master or develop, always identifies exactly one commit. It's just that the specific commit that master identifies changes over time. So does the specific commit that develop identifies, and so does the specific commit that feature2 identifies, and so on.

Pictorially, these things may look like this:

       I--J   <-- feature1
      /
...--H   <-- master
      \
       K--L   <-- feature2

Here, neither feature1 nor feature2 is merged to master. This is because the commit identified by the name feature1, i.e., commit JJ stands in for its actual hash ID, which is some big ugly string of letters and digits—is not an ancestor of commit H, which is the commit identified by the name master. However, master is merged into feature1, because commit H is an ancestor of commit J. Similarly, master is merged into feature2.

If we now use:

git checkout master
git merge --ff-only feature1
git merge --no-ff feature2

on the command line, and everything works, we get a new merge commit M, so that the name master now points to this merge commit:

       I--J   <-- feature1
      /    \
...--H      M   <-- master
      \    /
       K--L   <-- feature2

Note how all we did was add a commit to the drawing: new commit M points back to existing commits J and L. The branch name master now identifies commit M, rather than commit H.

Since J and L are both ancestors of M, but M is not an ancestor of either, branches feature1 and feature2 are now merged into master. Note that at this point, we can delete the name feature1:

       I--J
      /    \
...--H      M   <-- master
      \    /
       K--L   <-- feature2

Commit J is still an ancestor of commit M, so the commit is merged. But branch feature1 no longer exists. It doesn't matter for using Git, but now, the list of branch names that identify commits that are ancestors of M no longer includes feature1. We can do the same with feature2:

       I--J
      /    \
...--H      M   <-- master
      \    /
       K--L

We can now create a new branch name, such as feature/short, that points to commit J:

       I--J   <-- feature/short
      /    \
...--H      M   <-- master
      \    /
       K--L

Commit J is still an ancestor of commit M, so branch feature/short is merged to branch master.

Git itself—not GitHub and not GitLab, just plain old Git—can tell you which branch names point to commits that are ancestors of some other branch name:

git branch --merged master

first finds commit M, then loops through all branch names—in this case, feature/short is the only other branch name left—to see if the commit that those branch names identify is an ancestor of commit M. At the moment, that is true of feature/short, so git branch --merged will list feature/short.

If we git checkout feature/short and put a new commit on it, though, we get this:

       I--J---N   <-- feature/short
      /    \
...--H      M   <-- master
      \    /
       K--L

Commit N is not an ancestor of commit M, so branch feature/short is not merged to branch master. Running git branch --merged master will no longer list feature/short.

Remote-tracking names

To do this from your own Git repository, rather than from some hosting-service API, you will need a clone of the Git repository from the hosting-service. When you make such a clone, unless you use --mirror,2 your Git will rename their branches. You will have, instead of branch names, a series of remote-tracking names, of the form origin/master, origin/develop, origin/feature1, and so on.

After copying their commits to your repository and renaming their branch names to your remote-tracking names, your own Git will create one branch name in your repository, pointing to one of the existing commits you got from them. By default, your Git will create your master pointing to the same commit as their origin/master. However, over time, their branch names will change: they will point to newer, better-to-use commits. You will need to run:

git fetch

or:

git fetch --prune

to get your Git to pick up their new commits and update your remote-tracking names. Using --prune will make sure that your Git removes old, stale remote-tracking names: for instance, after their Git has deleted their feature/short, your Git will continue to have origin/feature/short until you do a fetch with pruning turned on. Your Git will then notice that they don't have feature/short and remove your origin/feature/short.

What you'll want to do here is use:

git branch --merged -r origin/master

The -r option tells git branch to look at your remote-tracking names rather than your branch names. The origin/master tells your Git to use the commit that your origin/master—their master as of your last fetched update—identifies. So now you'll get a list like origin/feature1 and origin/master as remote-tracking names that point to commits that are ancestors of the commit to which your origin/master points.


2It's unlikely that you want to use --mirror.

2 of 2
0

We saw GitLab support API to get all merge requests for a project. However, we don't see a similar API for GitHub.

https://docs.gitlab.com/ee/api/merge_requests.html#list-project-merge-requests

curl -XGET --header "PRIVATE-TOKEN: XXX" "https://gitlab.com/api/v4/projects/6888/merge_requests"

https://github.community/t5/GitHub-API-Development-and/Get-all-merge-requests-for-a-project-OR-get-branches-merged/m-p/42869

🌐
GitHub
docs.github.com › en › enterprise-server@3.10 › rest › pulls › pulls
REST API endpoints for pull requests - GitHub Enterprise Server 3.10 Docs
When you get, create, or edit a pull request, GitHub Enterprise Server creates a merge commit to test whether the pull request can be automatically merged into the base branch. This test commit is not added to the base branch or the head branch.
Top answer
1 of 3
94

I use the following script:

git log main --first-parent --merges \
        --pretty=format:"%h %<(10,trunc)%aN %C(white)%<(15)%ar%Creset %C(red bold)%<(15)%D%Creset %s"

Explaining each argument:

  • main: the name of your main branch. Can be omitted, in which case the current branch will be used.
  • --first-parent: skips commits from merged branches. This removes the entries where someone merged master into their branches.
  • --merges: shows only "merge commits" (commits with more than 1 parent). Omit this argument if you want to see direct commits to your main branch.
  • --pretty-format: applies the following formatting:
    • %h: the commit short hash;
    • %<(10,trunc)%aN: author name, truncated at 10 chars;
    • %<(15)%ar: the relative commit time, padded to 15 chars;
    • %<(15)%D: the tag names, also padded to 15 chars;
    • %s: first line of the commit message.

The result is pretty satisfying:

2 of 3
18

Git exposes such feature through the git log command. This command accepts some switches that filter the rendered commits according to the number of parents commits.

One of them would fit your request:

  • --merges Print only merge commits. This is exactly the same as --min-parents=2.

The following shows the merge commits (ie. commits with more than one parent) reachable from the vNext branch of the LibGit2Sharp project

$ git log vNext --merges --oneline
090b2de Merge pull request #348 from jamill/credential_callback_fix
0332c35 Merge pull request #260 from ben/great-renaming
3191c1b Merge pull request #239 from ben/libgit2-upgrade-81eecc3
1d544e8 Merge branch 'vNext'
238d259 Merge remote-tracking branch 'origin/master'

Update

Leveraging the same output through the GitHub API is possible, but would be somewhat more complex.

This would require to retrieve all the commits from a branch, paginating through all the results (in order to retrieve all the commits meta data) while filtering out the ones that only expose only one parent node.

As a starting point, the following url shows the latest 30 commmits of the vNext branch.

  • https://api.github.com/repos/libgit2/libgit2sharp/commits?sha=vNext
🌐
GitHub
gist.github.com › ijse › bd768d1c71e1f6056afd8c9ae9bf8b5a
call github api to merge branches · GitHub
call github api to merge branches. GitHub Gist: instantly share code, notes, and snippets.
🌐
GitHub
docs.github.com › en › enterprise-cloud@latest › rest › branches
REST API endpoints for branches and their settings - GitHub Enterprise Cloud Docs
January 7, 2020 - REST API endpoints for branches · List branches · Get a branch · Rename a branch · Sync a fork branch with the upstream repository · Merge a branch · REST API endpoints for protected branches · Get branch protection · Update branch protection · Delete branch protection ·
🌐
GitHub
docs2.lfe.io › v3 › pulls
Pull Requests | GitHub API
i.github.com/users/octocat/received_events", "type": "User", "site_admin": false }, "merge_commit_sha": "e5bd3914e2e596debea16f433f57875b5b90bcd6", "merged": false, "mergeable": true, "merged_by": { "login": "octocat", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", "gravatar_id": "", "url": "https://api.github.com/users/octocat", "html_url": "https://github.com/octocat", "followers_url": "https://api.github.com/users/octocat/followers", "following_url": "https://api.github.com/users/octocat/following{/other_user}", "gists_url": "https://api.github.com/users/octocat
🌐
GitHub
docs.github.com › en › pull-requests › collaborating-with-pull-requests › incorporating-changes-from-a-pull-request › automatically-merging-a-pull-request
Automatically merging a pull request - GitHub Docs
For more information, see Managing a branch protection rule. People with write permissions to a repository can enable auto-merge for a pull request.