Pre-Merge Gates in Bitbucket: Pull-Request Pipelines and Merge Checks
Run Bitbucket Pipelines on the merge result instead of after it lands: a pull-requests section, a required-build merge check, and the build-minute trade-off.
30 Aug 2026, 09:22 UTC

If your only automated build runs on main after a merge, every broken change is already in the shared branch by the time you learn about it. The fix is not more checks — it is moving the check onto the merge result and making it mandatory. In Bitbucket, that means a pull-requests pipeline plus a required-build merge check, with branch restrictions limiting who can bypass it.
What a pull-request pipeline actually builds
A pull-requests section in bitbucket-pipelines.yml triggers when a pull request is opened or updated. The important detail is what it builds: Bitbucket runs the pipeline against a merge of the source branch into the destination branch, not the source branch on its own. A green build therefore reflects the integrated result, so semantic merge conflicts — two changes that each pass alone but break together — surface before merge instead of after.
Patterns under pull-requests match the destination branch. A key of '**' gates every pull request; a key like 'main' scopes the gate to release-bound changes and leaves feature-to-feature pull requests cheap.
A minimal gate configuration
Commit this file at the repository root. You need push access to the repository; changing merge checks and branch restrictions later requires repository admin rights. The example assumes a Node project — swap the script lines for your own toolchain.
image: node:20
definitions:
caches:
npm: ~/.npm
pipelines:
pull-requests:
'main':
- step:
name: Verify merge result
caches:
- npm
script:
- npm ci
- npm run lint
- npm test
branches:
main:
- step:
name: Post-merge build
script:
- npm ci
- npm run build
The image value is a placeholder — pin whatever base image your team already uses. The caches entry reuses the npm cache between runs, which matters more here than in a single post-merge build because pull-request pipelines multiply the number of runs. The branches section is deliberately separate: the pull-request gate answers "is this safe to merge", while the post-merge build answers "what do we ship".
Default variables let a script adapt to context without extra configuration. BITBUCKET_PR_ID is set for pull-request pipelines, BITBUCKET_BRANCH names the branch, and BITBUCKET_COMMIT identifies the commit under test. They are useful for naming artifacts or skipping expensive steps on draft pull requests.
Making the gate mandatory
A pipeline that runs but blocks nothing is just a notification. Two repository settings turn it into a gate:
- Merge checks — conditions that must hold before the merge button works. A required-builds check lets you nominate a pipeline as mandatory, alongside a minimum number of approvals.
- Branch restrictions — limits on who may push to or merge into protected branches such as
main. Without them, anyone with write access can push directly and skip the gate entirely.
Exact check names, available options and plan entitlements differ between Bitbucket Cloud and Bitbucket Data Center, and they change over time. Confirm the labels in your own repository settings rather than copying them from a blog post — including this one. Treat the names above as categories, not literal UI strings.
The trade-off: build minutes and flaky tests
Pull-request pipelines multiply runs per change. Every push to a pull request can start a build, so a busy repository may consume several times the minutes of a single post-merge build. Three levers keep it affordable: scope triggers by destination branch, cache dependencies, and order steps so the fastest failing check runs first.
The bigger operational risk is flakiness. A required check that fails intermittently blocks merges for reasons unrelated to the change, and teams respond by disabling the gate — which is worse than never having one. A quarantine or retry policy for known-unstable tests usually buys more reliability than adding further checks.
One limitation worth stating plainly: pull requests from forks or external contributors run code you did not write. Scope secrets and deployment credentials so they are not exposed to those runs, and avoid deploying from a pull-request pipeline.
How to verify this in your own workspace
- Create a scratch repository with the YAML above and open a pull request. Confirm a pipeline appears attached to that pull request, not just to the branch.
- Open the repository's merge checks and branch restrictions settings pages and record the exact names available on your plan.
- Push a commit that deliberately fails the pipeline, then attempt the merge. The merge should be blocked while the required build is red.
- Inspect the pipeline log header or checkout output to confirm the build ran on a merge of the source branch into the destination branch.
- Compare workspace build-minutes usage before and after enabling the trigger to measure the real cost.
If step 3 does not block the merge, the pipeline is running but not required — the missing piece is the merge check, not the YAML.
Where to start
Gate one destination branch, not all of them. A single required build on main catches most integration failures at a fraction of the cost of gating every pull request, and you can widen the pattern once flakiness is under control.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.