Bitbucket Cloud - How to programmatically list merged pull requests from a source branch during branch comparison
Platform Notice: Cloud Only - This article only applies to Atlassian products on the cloud platform.
Summary
When comparing branches, we can view the commits listed that are part of the source branch, not the destination branch.
Branch Compare Page View:

Previously, the Bitbucket branch compare page had a "merged pull requests" tab, displaying a list of pull requests merged into the source branch.

However, this tab has been deprecated, as noted in this Atlassian Community post. There are a couple of workarounds that could be used in place of the missing "merged pull requests" tab:
1. If deployments are configured on the repository: The Deployment card contains a separate tab for "Merged pull requests," allowing users to see the list of merged pull requests associated with the branch being deployed.
2. The Commit view page offers functionality to preview all pull requests associated with a given commit. By utilizing this functionality along with the list of commits from the Branch Compare page, a comprehensive list of pull requests associated with all commits between two branches can be constructed.
However, scenarios may arise where deployments are not configured in the repository, or the list of commits could be extensive (e.g., 30+ commits). Manually accessing each commit page to check associated pull requests can be a time-consuming process.
Solution
The List commits API with the include and exclude parameters, retrieves the commits that are reachable from the source branch but not reachable from the destination branch.
Example:
1
https://api.bitbucket.org/2.0/repositories/{workspaceID}/{reposlug}/commits/?include=development&exclude=master
Using include=development and exclude=master in the Bitbucket API call retrieves the commits that are reachable from the development branch but not reachable from the master branch.
The include and exclude parameters in the Bitbucket API are typically used for specifying commit ranges. They do not directly replicate the behavior of git diff A...B. However, you can use them to retrieve commits between two points in the commit history, similar to what git log COMMIT_A..COMMIT_B does.
Once the commits are listed, we can iterate through each commit and list pull requests containing that commit using the Commit Pull Requests API
Example:
1
https://api.bitbucket.org/2.0/repositories/{workspaceID}/{reposlug}/commit/{commit}/pullrequests
In summary, we list all the pull requests already part of the source branch using APIs, allowing us to filter merged pull requests with the same. Below is a sample Python script that automates fetching the commits using list commits API and listing pull requests associated with each diff commit using commoit pull requests API, export the final list of pull requests to a CSV file "report.csv".
Sample Python Script:
API Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import requests
from requests.auth import HTTPBasicAuth
import csv
import warnings
# Suppress urllib3 SSL warnings
warnings.filterwarnings("ignore", category=UserWarning, module='urllib3')
def fetch_commits_between_branches(username, password, workspace, repository, source_branch, destination_branch):
print(f"Fetching commits between {source_branch} and {destination_branch}...")
base_url = f"https://api.bitbucket.org/2.0/repositories/{workspace}/{repository}/commits"
params = {
'fields': 'values.hash,next',
'pagelen': 100,
'include': source_branch,
'exclude': destination_branch
}
auth = HTTPBasicAuth(username, password)
commits = []
next_page_url = base_url
while next_page_url is not None:
response = requests.get(next_page_url, auth=auth, params=params)
response.raise_for_status()
page_json = response.json()
commits.extend(page_json['values'])
next_page_url = page_json.get('next')
print(f"Fetched {len(commits)} commits so far...")
print("Completed fetch.")
return commits
def fetch_pull_requests_details(username, password, workspace, repository, pull_request_id):
url = f"https://api.bitbucket.org/2.0/repositories/{workspace}/{repository}/pullrequests/{pull_request_id}"
params = {'fields': 'state,author.display_name,updated_on'}
auth = HTTPBasicAuth(username, password)
print(f"Fetching details for pull request {pull_request_id}...")
response = requests.get(url, auth=auth, params=params)
response.raise_for_status()
return response.json()
def fetch_pull_requests_for_commit(username, password, workspace, repository, commit_hash):
url = f"https://api.bitbucket.org/2.0/repositories/{workspace}/{repository}/commit/{commit_hash}/pullrequests"
params = {'fields': 'values.id,next,values.links.self.href,values.title'}
auth = HTTPBasicAuth(username, password)
print(f"Fetching pull requests for commit {commit_hash}...")
response = requests.get(url, auth=auth, params=params)
response.raise_for_status()
page_json = response.json()
return page_json['values']
def fetch_pull_request_state(username, password, workspace, repository, pull_request_id):
url = f"https://api.bitbucket.org/2.0/repositories/{workspace}/{repository}/pullrequests/{pull_request_id}"
params = {'fields': 'state'}
auth = HTTPBasicAuth(username, password)
print(f"Fetching state for pull request {pull_request_id}...")
response = requests.get(url, auth=auth, params=params)
response.raise_for_status()
return response.json()['state']
def generate_csv_report(username, password, workspace, repository, source_branch, destination_branch):
commits = fetch_commits_between_branches(username, password, workspace, repository, source_branch, destination_branch)
commit_hashes = [commit['hash'] for commit in commits]
csv_rows = []
for commit_hash in commit_hashes:
pull_requests = fetch_pull_requests_for_commit(username, password, workspace, repository, commit_hash)
for pr in pull_requests:
pr_id = pr['id']
title = pr['title']
commit_state = fetch_pull_request_state(username, password, workspace, repository, pr_id)
# Fetch pull request details to get author_display_name and updated_on
pr_details = fetch_pull_requests_details(username, password, workspace, repository, pr_id)
author_display_name = pr_details['author']['display_name']
updated_on = pr_details['updated_on']
csv_rows.append([commit_hash, pr_id, title, commit_state, author_display_name, updated_on])
with open('report.csv', 'w', newline='') as csvfile:
csv_writer = csv.writer(csvfile)
csv_writer.writerow(['Commits', 'Pull Request', 'Title', 'State', 'Author', 'Updated On'])
csv_writer.writerows(csv_rows)
print("CSV report generated successfully.")
# API Authentication
username = '' # use your bitbucket username
password = '' # use your app password
workspace = '' # eg: 'my-workspace'
repository = '' # eg: 'test'
source_branch = '' # eg: 'development'
destination_branch = '' # eg: 'master'
generate_csv_report(username, password, workspace, repository, source_branch, destination_branch)
Sample CSV Output:
Commits | Pull Request | Title | State | Author | Updated On |
26f6bff57d3b2d04cee46ab7ebcf0ee1dae4d00d | 110 | ABC TEST1 | MERGED | Suhas | 2024-06-06T13:20:22.788867+00:00 |
ee1dae57d3b204cee46ab7ebcf0e0ee1dae4d00 | 111 | ABC TEST2 | OPEN | Suhas | 2024-06-06T13:20:22.788867+00:00 |
5711992332b3741189bb66bd36d3332b374118 | 112 | ABC TEST3 | MERGED | Suhas | 2024-06-06T13:20:22.788867+00:00 |
The above CSV output contains all the pull requests (with statuses such as open, merged, and others) associated with the source branch commits. We can filter the merged pull requests if required. This solution also enables other integrations where this data needs to be fetched programmatically. The API helps facilitate this process.
Was this helpful?