Pushing to multiple git repos

Scope

How to push a git repo to multiple remote repositories, such as GitLab & BitBucket, with a single command.

Background

I’ve been using BitBucket, which offers unlimited private repositories for free, to backup my git repos in the cloud.

Recently I switched to GitLab. They offer a similar service, also for free, but with a nicer UI. In particular, GitLab can search inside files, not just the filenames. This feature is curiously missing from BitBucket, although it was logged back in 2011.

Unfortunately, GitLab’s shiny UI baulks when confronted with my notes repository: a single folder containing several hundred markdown files. BitBucket, meanwhile, handles it without a hitch.

I wanted to keep both repos updated so that I can use whichever service is more useful for a given task. Having redundant copies of my files also offers some peace of mind.

Method

Set up the remote repos

I use the web interface of GitLab and BitBucket to set up the repositories. There are some CLI tools available, but I haven’t tried them.

The URLs for the common hosting services are identical, except for the domain:

BitBucket git@bitbucket.org:namespace/repo.git
GitHub git@github.com:namespace/repo.git
GitLab git@gitlab.com:namespace/repo.git

To prevent your being asked for passwords when pushing, make sure to set up SSH access for each service.

Configure your local repo

The basic idea is to add multiple “push URLs” to a single remote alias. The confusing bit (but it’s a feature, not a bug) is that these URLs override a repo’s default URL when pushing.

Let’s say that you have created an alias called origin to your main remote repository (in my case this would be on GitLab):

git remote add origin git@gitlab.com:namespace/repo.git

To push to multiple repositories you need to add push URLs for all repositories. For example:

git remote set-url --add --push origin git@gitlab.com:namespace/repo.git
git remote set-url --add --push origin git@bitbucket.org:namespace/repo.git

These commands will add push URLs for both GitLab and BitBucket, even though I already have set up GitLab as a remote repo.

If you prefer, you can make the changes directly in your repo’s .git/config.

After the above commands it would look something like this:

[remote "origin"]
	url = git@gitlab.com:namespace/repo.git
	fetch = +refs/heads/*:refs/remotes/origin/*
	pushurl = git@gitlab.com:namespace/repo.git
	pushurl = git@bitbucket.org:namespace/repo.git

The configuration file makes clear what is going on with the URLs.

Pushing & pulling

Once you’ve set this up, git push commands will push to all repos you’ve defined with pushurls.

For example, git push --all -u will push all your local branches and set up tracking.

Note that you’ll only be pulling from the main repository (once again, it’s a feature, not a bug).

Caveats

I’ve been using this technique for private repos where I am the only user. But I don’t anticipate any issues if multiple people are pushing changes, as long as everyone is pushing and fetching from the main repository. Even Linus does this (or at least he did in 2006, though the setup was a bit different at the time). And the only horror story I’ve found had to do with post-hooks.

It would be more efficient to push the local changes to only one remote repo and then set up a post-commit hook to push the changes to other repositories. But as far as I can tell it’s not possible with BitBucket or the freely hosted version of GitLab.

Update (21 January 2015)

Last week GitLab released a new UI. Not only does the new release look great, it now handles my numerous markdown files without breaking a sweat. Hurrah!


Further reading on Stack Overflow

Comments? Tweet or email me.