GitHub → Azure DevOps
Import a GitHub repo into Azure Repos with a clone URL. You get commits, branches, tags, and files. Issues, pull request discussion, Actions secrets, and pipeline history stay in GitHub.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
GitHub
- The largest developer ecosystem combines collaboration, automation, and public discovery
- Repository-centered workflows keep code, review, and automation close together
- Enterprise planning and governance can require several overlapping GitHub products
- Advanced governance and automation can become configuration-heavy
Azure DevOps
- Repos, boards, pipelines, artifacts, and enterprise controls are tightly integrated
- Repository-centered workflows keep code, review, and automation close together
- The interface and administration model can feel heavy outside Microsoft-centric organizations
- Advanced governance and automation can become configuration-heavy
Azure DevOps: Repos, boards, pipelines, artifacts, and enterprise controls are tightly integrated. This removes a major source-side concern: Enterprise planning and governance can require several overlapping GitHub products.
What you lose: The largest developer ecosystem combines collaboration, automation, and public discovery. What you inherit: The interface and administration model can feel heavy outside Microsoft-centric organizations.
Jump to a section
Know the shape of the move.
This timeline assumes
- You are moving Git repos into Azure DevOps Services, and Repos is enabled on the project.
- The importer is a project admin, or has Git project-level Create repository set to Allow.
- Private GitHub repos can be read with a personal access token. The Azure CLI reads it from AZURE_DEVOPS_EXT_GIT_SOURCE_PASSWORD_OR_PAT.
- The site already documents GitHub → GitLab and GitHub → Bitbucket.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Git history | partial | critical | Azure DevOps imports a Git repo from a clone URL. Microsoft documents GitHub as a source, along with Bitbucket, GitLab, and other Git hosts. When the import finishes, a branch named main becomes the default. Otherwise the first branch in alphabetical order does. | Compare branches and a few commit SHAs after import. Rename the default branch if alphabetical order surprised you. |
| Private auth | manual | critical | For a private repo, az repos import create needs --requires-authorization and the token in AZURE_DEVOPS_EXT_GIT_SOURCE_PASSWORD_OR_PAT. The target repo must already exist and be empty. Microsoft says to prefer Microsoft Entra tokens when you can. | Use a fine-grained token that can read the repo. Revoke it after the import. |
| Issues and pull requests | lost | critical | Azure DevOps imports a Git clone URL. GitHub's backup doc treats a mirror clone as a backup of the Git repository. Issues and pull request conversation are not in that clone, so they stay on GitHub. | Export issues if you still need them and map them into Boards as its own project. Do not tell the team that history came with the clone. |
| Workflow files | partial | high | .github/workflows is just files, so it arrives with the repo. Azure Pipelines will not run those workflows until you create a pipeline. | Add a pipeline that points at the YAML you intend to keep, or translate it. |
| Actions secrets | lost | critical | GitHub's list-secrets API returns repository, environment, and organization secret names and does not reveal the encrypted values. Those values are not in the Git history Azure DevOps imports. | Recreate secrets in Azure Pipelines from the password manager. Do not try to extract them from GitHub. |
| Actions run history | lost | medium | Logs and artifacts from past workflow runs stay in GitHub. A clone does not include them. | Download any log you still need for an audit before you archive the GitHub repo. |
| Branch policies and permissions | manual | high | GitHub rulesets and required reviewers do not become Azure DevOps policies. Microsoft's import article is about Git data. Permissions on the new repo are whatever the project already applies. | Rebuild required reviewers and build validation before you let the team push. |
| GitHub Apps and webhooks | manual | high | Apps installed on the GitHub repo do not follow the clone. Service connections in Azure DevOps are new objects. | List webhooks. Recreate the ones that should fire on Azure Repos. |
| Wikis and packages | manual | medium | A GitHub wiki is its own Git repo when you use the wiki git endpoint. Packages in GitHub Packages are not commits in the source repo. | Import the wiki repo separately if you use it. Republish packages. |
| Empty-repo rule | manual | high | Import into an existing repo only works when that repo is empty. Import into a new repo is the other path, from Repos > Files > Import repository. | Create a new repo, or confirm the target has no commits. |
| People and licenses | manual | high | GitHub users are not Azure DevOps users. Microsoft says private-project readers need at least Basic access, and contributors need the Contributors group or equivalent. | Add the team and check access before you change the remote. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| Clone URL | Imported Azure Repo | automatic | Public repos can import from the URL. Private repos need a token. |
| Branch main | Default branch | automatic | Microsoft sets main as default when that branch exists. |
| Other branches and tags | Branches and tags | automatic | They are part of the Git import. Verify the list. |
| Commit | Commit | automatic | Compare a SHA from GitHub with Azure Repos. |
| .github/workflows file | File in the repo, not a running pipeline | transform | The YAML arrives with the Git data. Create the Azure Pipeline yourself. |
| Actions secret | Pipeline secret | manual | GitHub will not show you the value. Enter it from your vault. |
| Issue or pull request | Azure Boards work item | manual | Not included in the Git clone. |
| Ruleset | Branch policy | manual | Rebuild required checks. |
| Webhook | Service hook | manual | Recreate it and send a test event. |
Make the move recoverable.
Create the source-of-truth backup
Keep GitHub as the remote of record until Azure Repos matches the branches you need.
- List repos, default branches, and which ones are private.
- Write down rulesets, environments, and secret names. You cannot export secret values.
- Clone --mirror to a machine you control for the repos that matter.
- Export issues if Boards must carry them.
- Leave GitHub unlocked until developers have pushed once to Azure Repos.
Proof to capture: A mirror clone exists, and secret names are listed without values.
Repo import
Copy Git data into an empty Azure Repo.
- Create the Azure DevOps project and an empty repo if you are using the CLI.
- Paste the HTTPS clone URL. For a private repo, set the token env var and pass --requires-authorization.
- Wait until the import finishes.
- Check the default branch. If there was no main, Azure picks the first name alphabetically.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Pipeline and policy
Make the imported YAML actually run, with secrets and reviewers.
- Create a pipeline pointed at the YAML you are keeping.
- Add secrets from the vault. GitHub cannot display the old values.
- Add branch policies that match the rulesets you still want.
- Run the pipeline once on a non-production branch.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
The team thinks issues moved
The repo import succeeds and someone archives GitHub.
- Consequence
- Issue and pull request history is stuck in an archived GitHub repo.
- Mitigation
- Say, in the announcement, that Git data moved and issues did not.
Stop if: GitHub is about to be archived and Boards is empty.
Secrets are assumed to copy
The pipeline YAML references secret names that Azure does not have.
- Consequence
- The first run fails, or worse, someone pastes a secret into a yaml file.
- Mitigation
- Enter secrets in the pipeline UI. Fail the run on a staging branch first.
Stop if: A secret value is committed to the repo.
Default branch is not the one you ship
The repo has no branch named main.
- Consequence
- Azure DevOps checks out the alphabetically first branch.
- Mitigation
- Look at the default branch before anyone clones.
Stop if: The default branch is not the one you release from.
Do the work in this order.
- Days 1 to 2 · inventory
Repos, secrets, rules
3 to 6 hours active1 to 2 days elapsedOwners name the systems of record waiting- List repos and default branches.
- List secret names and rulesets. Decide where issues live.
Depends on: GitHub admin and Azure DevOps project admin
Stop / go checkpointImport Git?
Go when: Issues have a home that is not 'they come with the clone'.
Stop when: Nobody can create a repo in the project.
- Days 2 to 3 · backup
Mirrors
2 to 4 hours active1 day elapsedClone waiting- git clone --mirror the important repos.
- Export issues if Boards needs them.
Depends on: Inventory
Stop / go checkpointImport to Azure?
Go when: The mirror has the branches you listed.
Stop when: A private repo clone failed auth.
- Days 3 to 6 · pilot
One repo
4 to 8 hours active2 to 3 days elapsedImport waiting- Import one repo.
- Check SHAs and the default branch.
- Create a pipeline and a secret. Run it.
Depends on: Empty target repo
Stop / go checkpointImport the rest?
Go when: SHA sample matches and the pipeline ran without a committed secret.
Stop when: The default branch is wrong or the token was too broad to revoke.
- Days 6 to 8 · cutover
Remotes
2 to 5 hours active1 to 2 days elapsedDeveloper clones waiting- Import remaining repos.
- Tell people the new remote.
- Turn on branch policies.
Depends on: Pilot
Stop / go checkpointAzure Repos is the remote?
Go when: A pull request policy blocks a direct push, if that is what you wanted.
Stop when: Issues were supposed to move and Boards is empty.
- Days 8 to 15 · observe
Leave GitHub readable
2 to 4 hours active7 days elapsedThe team pushes waiting- Watch failed pipelines.
- Keep GitHub readable.
Depends on: Remotes updated
Stop / go checkpointArchive GitHub?
Go when: A week of commits landed in Azure Repos and secret runs succeeded.
Stop when: Someone still opens pull requests only on GitHub.
Cut over with a way back.
Cutover
Developers push to Azure Repos. GitHub stays readable until you archive it on purpose.
- Finish imports and check default branches.
- Put secrets in Pipelines.
- Enable branch policies.
- Change the documented remote.
- Stop opening new GitHub pull requests. Leave the old ones readable.
Proof to capture: New commits appear in Azure Repos, policies are on, and GitHub was not archived.
Rollback
Make GitHub the remote again. The import did not delete it.
- Tell the team to push to GitHub.
- Cherry-pick or push any Azure-only commits back to GitHub.
- Disable the Azure pipeline if it is deploying from the wrong history.
- Leave the imported repo in place until you delete it deliberately.
- Revoke the GitHub token either way.
Proof to capture: GitHub has the latest commits you care about and Azure is not deploying them by accident.
- Default branch is wrong
- A secret was committed
- Issues were assumed to have moved
- The import token is still valid after cutover
- Branch policy blocks a release you still have to ship from GitHub
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
V-01Blocking | SHA sample | Compare five commit SHAs, including the tip of the release branch. | They match GitHub. | SHA list. | |
V-02Blocking | Default branch | Read the default branch in Azure Repos. | It is the branch you release from, not an alphabetical accident. | Repo settings. | |
V-03Blocking | Release tags | List tags on both hosts. | The tags you ship are present. | Tag list. | |
V-04Blocking | Pipeline run | Run the pipeline on a non-production branch. | Secrets resolve and none are in the Git commit. | Pipeline log with secrets masked. | |
V-05Blocking | Direct push | Try a push that should be blocked. | The policy rejects it. | Push error. | |
V-06Blocking | Boards expectation | Open Boards and GitHub issues side by side. | The team knows which one is the system of record. | Announcement. | |
V-07Blocking | Token revoked | Confirm the GitHub token used for import is revoked or expired. | It cannot read the repo. | Token list. | |
V-08Blocking | A contributor | Clone as a user who is not a project admin. | They can clone and push within policy. | Clone test. |
Remove the scaffolding safely.
Safe after: A week of commits in Azure Repos and a revoked import token.
- Revoke the GitHub token.
- Archive GitHub only after issues and old pull requests have a stated home.
- Delete empty failed import repos.
- Remove the mirror from laptops that should not keep a second copy.
- Store pipeline secrets only in Azure Pipelines.
- Review branch policies when the next repo is imported.
Verify against the primary material.
Platform behavior changes. Check these sources and the review dates above before executing a production migration.
- Azure DevOps: import a Git repositoryAccessed 2026-11-03
- GitHub REST: Actions secretsAccessed 2026-11-03
- GitHub: back up a repositoryAccessed 2026-11-03