Software companies
Migrating between GitHub, GitLab and Bitbucket: keeping pull request review history
By SourceX Editorial · Updated
Short answer
When you migrate between GitHub, GitLab and Bitbucket, git history moves cleanly but pull request review history depends on the importer. Descriptions and comments often transfer, while approvals, review states, inline comment positions, CI results and user identities are frequently lost or altered. Archive every pull request through the source platform's API before switching the old platform off.
Key takeaways
- A git clone carries commits, branches and tags, never pull requests or review threads.
- Importer coverage differs by source, destination and edition, so test one repository and compare counts.
- Inline review comments can lose their position when the new platform rebuilds the diff.
- Map users before importing, or review history may be attributed to a single migration account.
- Keep an API export of pull requests, comments, reviews and statuses as a read-only archive.
What does a repository migration actually move?#
A repository migration moves two different layers, and only one of them lives in git. The git layer, meaning commits, branches and tags, travels with any mirror clone or push. The platform layer, meaning pull requests, review comments, approvals, status checks and links, lives in the hosting platform's database and moves only if an importer carries it.
The table shows how each record typically behaves. Treat it as a checklist for your test import rather than a description of any specific tool, because importer coverage varies by direction and changes over time.
| Record | In git itself? | Typical importer behavior | What to check |
|---|---|---|---|
| Commits, branches and tags | Yes | Moved by mirror push or importer | Branch protection and signing settings, which are platform settings |
| Pull request titles and descriptions | No | Often imported | Links to old issue keys and URLs |
| General pull request comments | No | Often imported, sometimes under a migration account | Author attribution |
| Inline review comments | No | Imported by some tools; positions can shift or detach | Comments marked outdated or orphaned |
| Approvals and review states | No | Frequently dropped or flattened into comments | Whether approved or changes requested survives |
| CI status checks and build logs | No | Usually not imported | Export from the CI system separately |
| Closed and declined pull requests | No | Varies by tool | Counts by state, source against destination |
| Images and files attached in comments | No | May still point to the old platform | Broken images after the old platform closes |
Why review history is worth the extra work#
Review history is worth the extra work because it records why code changed, not just what changed. A pull request thread shows the bug a reviewer caught, the design a senior engineer pushed back on and the approach that was abandoned, which a commit message rarely captures.
Engineering teams use that history during incident analysis, audits and onboarding. AI developers building coding assistants value the same review threads, because they pair code with expert critique and an outcome: merged, revised or rejected.
- 01Your work records
- 02Personal details removed
- 03You approve the release
- 04Licensed to AI developers
Questions to ask any importer before you trust it#
Importers for GitHub, GitLab and Bitbucket each support some sources, some destinations and some object types, and the details depend on cloud versus self-managed editions. Read the importer's current documentation for the objects it carries, then confirm with a test on one busy repository.
Self-managed sources such as Bitbucket Data Center or a GitLab server add practical steps: network access from the destination, tokens with admin scope and enough time to import large histories without hitting rate limits.
- Which pull request states does it import: open, merged, closed and declined?
- Does it carry inline review comments, and what happens when the diff cannot be rebuilt?
- Are approvals and review states imported as records, as comments or not at all?
- How are users matched, and what appears when a user cannot be matched?
- Are attachments, images and reactions copied or left as links to the old platform?
- Does it rewrite links between pull requests and issues, or leave old URLs in place?
What each platform documents about its own export tools#
Each platform documents its own export tools more clearly than cross-platform importers, and those documents show where review history is at risk. Read them before trusting any migration path, because a tool built for moving projects is not always built for keeping history.
GitLab is unusually direct. Its project file export includes merge requests, issues with comments, labels, milestones and releases, but GitLab says these files should not be used as backups because not all items are exported, and after an import only the latest diff version and latest pipeline of each merge request remain visible. That is one way inline review comments made on earlier versions of a merge request can lose their context.
| Platform route | What the vendor documents | Review history risk |
|---|---|---|
| Git mirror clone and push, any platform | Commits, branches and tags only | No pull requests, reviews or comments at all |
| GitLab project file export and import | Merge requests, issues with comments, labels, milestones, releases; not a backup | Only the latest diff version and pipeline stay visible on imported merge requests |
| GitHub.com migration archive via the API | Selected repositories exported as an archive for import into GitHub Enterprise Server | Needs owner permissions and a token with repo and admin:org scopes |
| Bitbucket Cloud issue tracker export | Issues as a zip with a db-2.0.json file and optional attachments; spam-flagged issues left out | Atlassian announced removal of the issue tracker and wiki on August 20, 2026, so links to them may already be dead |
| Your own API export of pull requests | Whatever you request and save as raw JSON | Rate limits set the pace on large repositories |
Archive before you switch: the export step#
The export step creates a platform-independent archive of every pull request before the old platform is switched off. Even when the importer works well, the archive is your evidence of what existed, and it protects the record if a later cleanup on the new platform deletes stale pull requests.
Use the source platform's REST or GraphQL API and save raw JSON, so nothing is reshaped by the export. Keep commit hashes on every record, because they tie review comments back to the exact code reviewed. Budget time for rate limits: GitHub's REST API allows 5,000 requests per hour with a personal access token, and a GitHub App installed on a GitHub Enterprise Cloud organization gets 15,000, so a busy repository's review history can take hours to pull.
- Step 1: inventory repositories with pull request counts by state.
- Step 2: take a mirror clone of each repository for the full git history.
- Step 3: export pull requests, general comments, inline review comments, reviews and status checks through the API.
- Step 4: download images and files attached in comments, since their links will break.
- Step 5: export CI logs separately if build outcomes matter to your history.
- Step 6: store the archive read-only with checksums and reconcile counts against the source.
Keeping identities and links intact#
Identities and links decide whether an imported review history still makes sense to a reader. Without a user mapping, every comment from a former employee can collapse into a single placeholder account, and the thread loses the distinction between reviewer and author.
Build the mapping by email before importing, include former employees and bots, and keep the original usernames in the archive. For links, decide whether to rewrite old pull request and issue URLs or keep a mapping table from old identifiers to new ones; either works, as long as the choice is written down.
Issue keys need the same care when the issue tracker moves too. A pull request titled with a Jira key stays meaningful only while that key resolves somewhere, so if projects are renamed or the team moves to GitLab issues, record old and new keys side by side. A reader of an archived thread can then still find the bug it fixed.
Illustrative: an inventory software company consolidates on GitLab#
Illustrative: a fictional restaurant inventory software company on self-managed GitLab acquired a smaller competitor whose code lived in Bitbucket Cloud. The CTO wanted one platform and asked for a test import of the acquired company's busiest repository.
The test showed approvals missing and some inline comments detached on pull requests that had been rebased before merging. The CTO kept the plan to consolidate but added an archive step first: a mirror clone and a full API export of every Bitbucket pull request, with commit hashes and attachments. Active repositories were imported, Bitbucket stayed read-only until counts reconciled, and the acquired team's review history remained searchable in the archive.
How SourceX approaches code review archives#
SourceX approaches code review archives as engineering records whose value depends on linkage: pull requests tied to issues, reviews tied to commits, and outcomes recorded. A complete archive made before a migration often preserves more of that linkage than the imported copy on the new platform.
Licensing review history follows the SourceX five-step transaction, Supply, Rights, Preparation, Approval and Delivery, with engineering-specific work at two points. The Rights step separates customer-owned code and contractor work, and Preparation adds secret scanning and the removal of personal details from comments. The SourceX Evidence Packet then notes which export method produced the archive, on what date and with which known gaps.
Frequently asked questions
Will commit hashes change during the migration?
Not if the repository is mirrored without rewriting history. Hashes change when history is rewritten, for example to strip large files or old secrets, and that breaks the link between archived review comments and the code they discuss. Decide on any rewrite before exporting, and keep a mapping of old to new hashes if you do rewrite.
Do we need to keep closed and declined pull requests?
On the new platform, often not. In the archive, yes. Declined and abandoned pull requests show approaches the team rejected and why, which is useful for future design decisions and is part of the reasoning AI developers value in review history. Keep them with their final state and the reason they were closed, where one was given.
What about secrets in old pull request comments?
Old comments and diffs can contain passwords, API keys and tokens that engineers pasted while debugging. Open-source scanners such as gitleaks detect secrets in git repositories and files, and similar scanning can be run over exported comment text. Rotate anything live, and redact it from archive copies used for review.
Can we keep the old platform read-only instead?
Yes, for a period, and many teams do while the archive is verified. Read-only access still costs licenses or hosting, and acquired platforms tend to be cancelled during cost reviews. Treat read-only mode as a bridge to a verified archive, not a substitute for one.
Does a migration change who owns the review history?
No. The company owns its review history wherever it is hosted. What can limit later use is the content: customer-owned code, contractor agreements and personal details in comments. Those questions belong in a rights review, assessed with counsel, before review history is used outside the company.
Sources
- Gitleaks is an MIT-licensed tool for detecting secrets such as passwords, API keys and tokens in git repositories, files and stdin. Source
- GitLab's file-export migration includes project and wiki repositories, uploads, issues with comments, merge requests, labels, milestones, snippets and releases; GitLab says export files should not be used to back up data because not all items are exported, and after import or export only the latest diff version and latest pipeline in merge requests are visible. Source
- Migration data can be exported from an organization on GitHub.com by using the API to select repositories and generate a migration archive for import into GitHub Enterprise Server, which requires owner permissions and a token with the repo and admin:org scopes. Source
- Atlassian announced that the Bitbucket Cloud issue tracker and wiki would be removed on August 20, 2026, with issues exportable as a zip file or migratable to Jira. Source
- Bitbucket Cloud issue export packages are ZIP files containing a db-2.0.json file and optionally an attachments directory; issues flagged as spam are not exported. Source
- GitHub's REST API allows 5,000 requests per hour for requests made with a personal access token, while a GitHub App installation on a GitHub Enterprise Cloud organization gets 15,000 requests per hour. Source
Related resources
See if your company qualifies
A short company assessment. No data uploads are needed.