Seamlessly migrating from one git service to another for your team and capistrano

Published: July 15, 2016
I decided to move my projects from one hosted git service to a different one and needed to make the change near seamless for contributors and production

There are quite a decent number of services out there to host code repositories with Bitbucket, GitHub, and GitLab leading the pack. Sometimes one finds himself needing to switch services for whatever reason. Obviously this decision shouldn't be taken lightly and a decent amount of thought should be put into making the choice to move or not.

For me, personally, I choose to move my repos (all private) from a self-hosted GitLab instance to GitLab.com proper. I have a couple contributors to the repos so they will need to be able to seamlessly be able to switch to the new repo location. Most of these projects use capistrano to deploy to production, so that process will also need to be updated to point to the new repo. The process I took to do this can be applied to pretty much any hosted service and should be scalable to larger teams as long as proper communication happens.

Step 1: Push up and pull down all the things

To ensure a seamless transition, all WIP branches, changes, tags, etc from all contributors that are currently being tracked by origin should be pushed up before starting and at least one contributor should pull down (a simple git pull should handle this) all changes locally to serve as a canonical backup in case something goes wrong.

Step 2: Setup access and accounts

This is mostly administrative. Projects and accounts should be created on the new service for all contributors with proper push/pull access given where applicable.

Step 3: Point origin to the new location

Whichever service you switch should provide you with a clone url for the new repo. It's either a http(s) or SSH path ending in .git. This is what you'd normally use when cloning a repo for the first time (git clone git@somesite.com:user/project.git). Instead, we'll use the URL to point all local repos to the new service.

Each contributor should run this command at the root of their local copy of their project (replacing the url with the proper one to the new location of the repo):

git remote set-url origin git@gitlab.com:t27duck/my-project.git

This command will tell git that origin now points to a new location so all pushes and pulls will now draw from the other endpoint from now on.

Step 4: Migrate projects to new service

This step can be done in one or two ways. While I cannot speak for Bitbucket and other services (due to lack of experience with them), GitLab and GitHub both provide migration tools for moving repos from one service to another. If possible, use these services as they will commonly also migrate issues, wikis, Pull Requests/Merge Requests, etc along with the code. Note, for the case of private repos, most migration tools cannot access them across services (hence, the "private" part of "private repos"), so a common requirement to invoke a repo migration is to make the source repo temporarily public.

For those private projects where you do not wish to make public (even for 10 minutes)... or you raged quit your previous provider either before or after step 1 and the repo no longer exists, there's another option. This alternative method only moves the code. PRs, issues, etc won't be migrated as they are not part of the repository.

After completing step 3, one contributor should issue the following two commands in terminal:

git push --all
git push --tags

The first command pushes all branches, refs, changes, bits, bytes, whathaveyou. To the new origin. The second command pushes up any tags as, dispite what you'd assume it would do, the --all flag only pushes up branches and code. If you have a decently sized repo, it make take a few moments before the commands complete. Once that is done and you verify master, branches, and tags are all present and accounted for, contributors should be good to go for pushing and pulling as normal.

Step 5: Point deployment system to the new origin

The final step is to have your deployment system also pull from the new origin. I'm going to talk specifically about configuring capistrano as my projects are all Ruby web apps using capistrano for deployment.

First, modify config/deploy.rb to point to the new repo location. For capistrano 2.x, update the repository setting. In capistrano 3.x, it is call repo_url. This change will tell capistrano where the repo now lives when doing a full clone. Commit and push up the change.

At this point, you will still not be able to deploy as the cached version of the repo on production is still pointing to the old location. Attempting to deploy now will most likely cause "Cannot parse object 'abcdefg'" errors because the cached repo has no reference to the new refs coming from the new origin.

There are two options to fix this. You can either manually change the git configuration file to point to the new origin or flat out delete the cached repo. The latter will cause capistrano to do a full clone on the next deploy which will cause the new cached repo to automatically use the new endpoint.

Regardless of which option you take, the files live in different locations depending on the version of capistrano.

For 2.x:

The cached repo lives in {deploy_root}/shared/cached-copy

There's a .git/config file there that can be modified to point "origin" to the new location. Else, you could delete the entire cached-copy directory.

For 3.x:

The {deploy_root}/repo directory is what you want to delete. Alternatively, there's a config file that can be edited.

Once that is complete, everyone should be good to go. It's business as usual from that point forward.

Summary

It's relatively straightforward to migrate from one git provider to another. The trick is making sure all contributors and services properly point their repos to the new origin. As long as the migration process is properly communicated and executed, you shouldn't have any issues or lost data.