Migrating your Git repository from Azure DevOps Repos to GitHub should be easy. But it is not always like that.
We need to take care of Authentication, Clone, Branches, Tags, History... and much more...
In this video we will tackle all this problems and we will successfully migrate our repo to GitHub (including full history, branches and tags)!
Don't worry if you are not overly familiar with Git and it's command line, I will try to be as clear as possible.
And stay with me until the end for a bonus content.
Video
If you are a visual learner or simply prefer to watch and listen instead of reading, here you have the video with the whole explanation, which to be fair is much more complete than this post.
If you rather prefer reading, well... let's just continue :)
Problem 1: How to get the code from Azure DevOps
This is fairly easy to do, you just need to have the URL of the repository you want to move over to GitHub. And you need to have access to that repo of course.
To retrieve that, just head over your project repo and you can use the Clone button there.
Now that we have the URL, we can clone the code.
Problem 2: How to authenticate to your Azure DevOps Git Repo
There are different ways to do so.
You could use the same credentials you use to authenticate to Azure DevOps portal itself, but this would open a browser windows for you to insert username and password.
If you have to migrate only one repo than I guess this is fine, but if you want to perform multiple migrations, or you are on a machine that it is nor yours you may not want this.
Another option is to genereate new Git Credentials (in the Clone dialog we used before the get the URL), and use those instead. Again, not very suitable if you want to automate migrations, also because the credentials may vary from repo to repo.
The way I prefer doing this is with a PAT, Personal Access Token. which basically replace both username and password.
To create a PAT simply access your Azure DevOps portal, go to the small icon with the user image next to your picture, and click on Personal Access Tokens.
Here you can create a new Token, and assign specific permissions to it. For the scope of Git Repo migration we'd just need the "Read" permission under "Code".
So now we are ready to get the code.
Problem 3: How to clone a repository using a PAT?
This is rather easy, just prepend the PAT to the "dev.azure.com" in your url, like this.
In fact the "--mirror" switch cannot only be applied to the "git clone" command as we have seen before, but that can be applied also to the "git push" as you can see here.
If you have the GitHub credentials stored in your machine, or you want to insert them interactively then you can use the first command and push to the new origin, which in my case is this GHorigin.
If instead you want to do this more programmatically, or you don't want to save your credentials, you can use the second command which uses the Personal Access Token instead.
Problem 7: How to get a Personal Access Token in GitHub?
To create a PAT in GitHub, go to the Settings, then Developer Settings, and finally Personal Access Tokens.
Here you can Generate a new PAT. For migration purposes, we need to assign it proper permissions.
If your repository is public, then select just "public_repo". If, instead, you want to push to a private repository, you'd need to select the whole "repo" section.
When you have your PAT, you can use it in that command.
We are almost done, just one more thing.
Problem 8: This leaves the local repo in an "unusable state"
That's right, the "git clone --mirror", as we have seen, clones the repository in its "RAW" format, which then cannot be used as a normal working copy.
If you want to migrate the repo AND then use it as a working copy, I got you covered.
Starting from the Scripts Folder, we have the "migrate-mirror.ps1" script which performs the migration as we have seen before.
But we also have the "migrate.ps1" script which instead migrate the repository and leave it in an "usable" state.
Instead of using the --mirror, we clone first the code, then we clone all the remote branches (excluding the HEAD and the master, which we already have)
Then we add the new origin as we have seen before.
After doing that, we push first the code and all the branches with the "--all" switch and then we push the tags using the "--tags" switch
Finally, we remove the source origin and optionally we rename the GitHub origin, which in my case is "GHorigin", to just origin.
And that's it.
In my GitHub repository I've shown there are also some other implementations which allow you to execute your migration in a Docker container, and even run it inside Azure Container Instances.
Take a look at the video at the top of this post (here for simpler reference) to see those examples more in depth.