Linking Bitbucket work to code, reliably
Bitbucket's link to your tracker is built from issue keys in branch names and commits. Here is what Cloud and Data Center each give you, where the link silently drops, and how to close the gap.
You need to know which tickets a release contains. You have a list of merged pull requests and a board full of closed issues, and joining them takes a person and an afternoon, because roughly a third of the pull requests carry no issue key at all.
Bitbucket is unusual in this series because the work item almost never lives in Bitbucket. It lives in Jira, or in something else entirely, and Bitbucket’s job is to emit enough information for the other system to attach code to a work item. Understanding that split is most of the fix, because it tells you which side of the boundary each failure is on.
The link is text, and the text is an issue key
The link is text, and the text is an issue key. When a branch is created, a commit lands, or a pull request opens, Bitbucket sends development information to the connected tracker, which looks for a key in that text and attaches the code to the matching issue. Nothing scans your repository on a schedule. If the key is not in the text at the moment the event fires, no link is created and nothing reports an error.
Three places the key can live, in ascending order of durability:
- Commit messages. Per commit, and vulnerable to squash.
- Branch name. Created once, survives everything that happens inside the branch.
- Pull request title. The one that survives a squash merge, because a squashed commit message is generally derived from the pull request rather than from the commits it replaced.
Put it in the branch name and the pull request title and you have belt and braces.
Start the branch from the work item. With Jira connected, you can create a branch directly from the issue, and the branch arrives with a correct key in its name without anyone typing it. This is the highest-compliance route available and it is used far less than it should be. Where the action appears differs between Cloud and Data Center and between integration versions, so look for it on the issue itself in your own instance.
Commit-message commands. Text in a commit message can comment on, transition or log time against a Jira issue. Atlassian calls these smart commits. Availability depends on your deployment and configuration, so confirm what your instance accepts before writing it into a team convention.
Merge checks and hooks, which is where Cloud and Data Center diverge sharply.
- Bitbucket Cloud gives you configurable merge checks on a pull request and Pipelines for anything custom. There are no custom server-side git hooks, so enforcement happens either as a merge check or as a pipeline step that fails the build. Which merge checks are available depends on your plan, so check what your workspace actually has rather than assuming.
- Bitbucket Data Center does support server-side hooks, installed as apps or written yourself, which means you can reject a push whose branch name has no key. That is a genuinely stronger control than anything Cloud offers, and it is also the one most likely to make you unpopular.
Webhooks exist on both and fire on repository and pull request activity. They are the right foundation if you need to feed a system that is not your tracker. Event names and payload shapes differ between Cloud and Data Center and between versions, so build against the payload documentation for your specific deployment and log a real event before writing any parsing code.
Bitbucket Cloud
- No custom server-side git hooks
- Merge checks, availability varies by plan
- Pipelines as the enforcement point
- Lightweight per-repository issue tracker exists
Bitbucket Data Center
- Server-side hooks can reject a push outright
- Enforcement possible before history exists
- Hook management is per repository or per project
- No built-in issue tracker to fall back on
Squash merges eat the key
Squash merges. Keys in individual commits vanish into a generated message. If the pull request title has no key, the trunk has no key, and this is the most common single cause of a dropped link.
Repositories nobody connected. The integration is configured per workspace or project, and a repository created outside that path emits nothing. Linking rate for that repository is zero and no screen anywhere says so.
Forks and mirrors. Development information from a fork does not necessarily reach the tracker the way it does from the main repository. If any part of your workflow runs through forks, test it explicitly rather than assuming.
Bitbucket’s own issue tracker. Bitbucket Cloud includes a lightweight per-repository tracker. It is fine for a small project and it does not roll up: there is no cross-repository view, no portfolio layer, and nothing to report on above the repository. Teams that started there and grew usually discover this at exactly the wrong moment.
Automation accounts. Dependency updates and generated changes merge without keys. They set a floor on your ratio that discipline cannot raise.
Get the number from git, not from a dashboard
Get the number, per repository, from git rather than from any UI:
git log --format='%s' origin/main --since='90 days ago' | wc -l
git log --format='%s' origin/main --since='90 days ago' \
| grep -cE '[A-Z][A-Z0-9]+-[0-9]+'
Two numbers, one ratio. Run it against your five busiest repositories. That takes fifteen minutes and it replaces every opinion in the room.
Then, cheapest first:
- Create branches from the work item. No configuration, no policy, and it removes the failure mode rather than detecting it.
- Require the key in the pull request title. Squash-proof, and it costs nothing.
- Add a pipeline step or merge check that fails when neither the branch name nor the pull request title matches your key pattern, with your service accounts allowlisted and a documented exception route. On Data Center, consider the server-side hook instead, but understand that rejecting a push is a much harsher experience than failing a check, and it will be experienced as such.
- Audit which repositories are connected to the tracker at all, and put that audit on a recurring calendar entry. This is the boring one and it is regularly the one that finds the biggest gap.
For a single Jira project against Bitbucket repositories that are all connected, that is the complete answer.
Where this breaks down
A server-side hook rejects work in progress. The Data Center hook is the strongest control here and it fires at push time, which is when someone is trying to back up an experiment at eleven at night. Scope it to protected branches or you will be reverting it within a fortnight.
Enforcement produces keys, not meaning. Gate the merge on a key and you will get keys. Some of them will point at a parent epic or a ticket created two minutes earlier, and both satisfy the check completely while telling you nothing.
Cloud and Data Center guidance is not interchangeable. Half the instructions on the internet for this problem are written for the deployment you do not have. Confirm which one you are on before following anything, including this.
Linking rate is hygiene. It measures whether a key was typed. It says nothing about whether the change did what the ticket asked, and presenting it as a quality metric will get you caught by the first person who reads it carefully.
The takeaway
Bitbucket’s link to your tracker is a string in a branch name and a pull request title, sent at the moment of an event, with no error state when it is missing. Put the key where squash cannot eat it, make branch creation start from the work item, and audit which repositories are connected at all.
If you take one thing into next week: run those two git commands across your busiest repositories and check whether any of them return a ratio of zero. A zero is not a discipline problem, it is a repository nobody wired up, and it is the cheapest fix on this page.