Missing Commits in Bitbucket Server

Platform Notice: Data Center Only - This article only applies to Atlassian products on the Data Center platform.

Note that this KB was created for the Data Center version of the product. Data Center KBs for non-Data-Center-specific features may also work for Server versions of the product, however they have not been tested. Support for Server* products ended on February 15th 2024. If you are running a Server product, you can visit the Atlassian Server end of support announcement to review your migration options.

*Except Fisheye and Crucible

Summary

Problem

You are not seeing previously available commits in your repository. These missing commits are usually a result of a force push that has rewritten history in the Git repository.

Another, less common, cause for missing commits is after a migration to a new filesystem using rsync without the --delete option (see Missing commits in Bitbucket after a filesystem migration for details).

Force pushing and rewriting history is native to Git, therefore Atlassian Support cannot guarantee that we can provide support to help recover missing commits after this type of action.

We recommend establishing Branch Permissions to prevent rewriting history and protecting important branches, see Using branch permissions for more information.

Diagnosis

Gather all ref changes

The first step to recovering from a force push is to identify the commit hashes that changed. Typically this means that a branch pointer was moved to a previous commit in time which leaves all children of that commit as dangling commits and will effectively show as missing in the Bitbucket Server UI. The following SQL query can be used to identify all ref changes for a branch. Changes to refs are normal operations so many of the results returned will be expected.

1 2 3 4 5 6 7 8 9 10 11 select p.project_key, r.slug, pr.ref_id, pr.change_type, pr.from_hash, pr.to_hash, nu.name from sta_repo_push_ref pr join sta_repo_activity ra on ra.activity_id = pr.activity_id join repository r on r.id = ra.repository_id join project p on p.id = r.project_id join sta_activity a on a.id = pr.activity_id join sta_normal_user nu on nu.user_id = a.user_id where p.project_key = '[PROJECT KEY]'  and r.slug = '[REPO SLUG]' and pr.ref_id = 'refs/heads/[BRANCH_NAME]' order by a.created_timestamp desc;

The project_key should be uppercase. For example, the following query can be used to identify all ref changes on from the scratch repository in the PROJ project on the master branch:

1 2 3 4 5 6 7 8 9 10 11 select p.project_key, r.slug, pr.ref_id, pr.change_type, pr.from_hash, pr.to_hash, nu.name from sta_repo_push_ref pr join sta_repo_activity ra on ra.activity_id = pr.activity_id join repository r on r.id = ra.repository_id join project p on p.id = r.project_id join sta_activity a on a.id = pr.activity_id join sta_normal_user nu on nu.user_id = a.user_id where p.project_key = 'PROJ' and r.slug = 'scratch' and pr.ref_id = 'refs/heads/master' order by a.created_timestamp desc;

This returns the following rows (truncated to the top 3):

1 2 3 4 project_key slug ref_id change_type from_hash to_hash name PROJ scratch refs/heads/master 3 d63f3332e507e1e7939852a906d49809d47e9847 e168c777948c36f81ee7c9e3515bc0635f4cfec3 user PROJ scratch refs/heads/master 3 b66a493fc92bd1a8337e9b5566563f4915bb44db d63f3332e507e1e7939852a906d49809d47e9847 user PROJ scratch refs/heads/master 3 c783a47d91646ea924d5347412f809da92ff6bc4 b66a493fc92bd1a8337e9b5566563f4915bb44db user

Identify the ref change that removed commits

Using the /rest/api/latest/projects/PROJ/repos/REPO/commits?until=FROM_HASH&since=TO_HASH for each row above, we can identify when the force push occurred. This endpoint asks Bitbucket Server "Which commits are reachable from FROM_HASH that are not on TO_HASH?" During normal operations, no commits will be returned, but if commits are returned, it indicates that a force push has occurred.

From the example above, the following REST calls would be run:

1 2 3 curl -u jeff -H "Accept: application/json" "https://bitbucket.company.com/rest/api/latest/projects/PROJ/repos/scratch/commits?until=d63f3332e507e1e7939852a906d49809d47e9847&since=e168c777948c36f81ee7c9e3515bc0635f4cfec3" {"values":[],"size":0,"isLastPage":true,"start":0,"limit":25,"nextPageStart":null}
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 curl -u jeff -H "Accept: application/json" "https://bitbucket.company.com/rest/api/latest/projects/PROJ/repos/scratch/commits?until=b66a493fc92bd1a8337e9b5566563f4915bb44db&since=d63f3332e507e1e7939852a906d49809d47e9847" { "values":[ { "id":"b66a493fc92bd1a8337e9b5566563f4915bb44db", "displayId":"b66a493fc92", "author": { "name":"user", "emailAddress":"user@company.com", "id":51, "displayName":"Jeff", "active":true, "slug":"user", "type":"NORMAL", "links": { "self":[ { "href":"https://bitbucket.company.com/users/user" }] } }, "authorTimestamp":1462808100000, "message":"commit on master", "parents":[ { "id":"c783a47d91646ea924d5347412f809da92ff6bc4", "displayId":"c783a47d916" }] }, { "id":"c783a47d91646ea924d5347412f809da92ff6bc4", "displayId":"c783a47d916", "author": { "name":"user", "emailAddress":"user@company.com", "id":51, "displayName":"Jeff", "active":true, "slug":"user", "type":"NORMAL", "links": { "self":[ { "href":"https://bitbucket.company.com/users/user" }] } }, "authorTimestamp":1459809480000, "message":"test", "parents":[ { "id":"d63f3332e507e1e7939852a906d49809d47e9847", "displayId":"d63f3332e50" }] }], "size":2, "isLastPage":true, "start":0, "limit":25, "nextPageStart":null }
1 2 3 curl -u jeff -H "Accept: application/json" "https://bitbucket.company.com/rest/api/latest/projects/PROJ/repos/scratch/commits?until=c783a47d91646ea924d5347412f809da92ff6bc4&since=b66a493fc92bd1a8337e9b5566563f4915bb44db" {"values":[],"size":0,"isLastPage":true,"start":0,"limit":25,"nextPageStart":null}

The second cURL command shows that the push to master which moved the pointer from b66a493fc92bd1a8337e9b5566563f4915bb44db to d63f3332e507e1e7939852a906d49809d47e9847 was a force push and left two dangling commits.

Cause

A force push (push -f) was used to update the repository, altering the repository history.

Solution

Resolution

Before making any changes to the repository, create a copy of the folder that stores the bare repository on the server.

The location of this folder can be found in Bitbucket Server's web interface, on the Repository Settings page, next to: Location on disk.

The following steps need to be performed directly on the server where the missing commits need to be recovered.

  1. Clone the bare repository to a temporary location to work with

    1 git clone /var/atlassian/application-data/bitbucket/shared/data/repositories/<REPO_ID> temp-clone
  2. Checkout the branch that the force push was performed on

    1 git checkout <BRANCH>
  3. Merge the from_hash into the current tip of the branch, which essentially restores all of the commits that were removed while retaining any new commits

    1 git merge <FROM_HASH>
  4. Resolve any conflicts and commit the merge

  5. Add the Bitbucket Server repository as a local remote

    1 git remote add recovery https://bitbucket.company.com/scm/<PROJ>/<REPO>.git
  6. Push the new changes to Bitbucket Server

    1 git push recovery <BRANCH>
Updated on April 2, 2025

Still need help?

The Atlassian Community is here for you.