Posted on 08th June 2018

Git is now one of the most popular version control systems used by software development teams across the world. Most of these development teams create their Git repos in GitHub, Bitbucket, GitLab, etc., which provides cloud based or on-premises repository management service for collaborative software development. In this article we discuss how to keep two git repositories on different hosts in sync. Let's say you have a repository in GitHub and you want keep that in sync with another repository in Bitbucket. We can call the repo in GitHub as our primary repo and the Bitbucket repo as the secondary or backup repo. All the changes that you make in the GitHub repo should be reflected on the Bitbucket repo also. How do we do this?

The first step is to use the git clone command to copy the content of the primary repo to a folder in your local computer.

# git clone --mirror https://primary_repo_url/primary_repo.git

A folder that is initialised as a git repository contains all your project files, called the working tree, and also contains a subdirectory named .git that contains the revision history of your files. A repository that contains only the revision history and no working tree is called a bare repo. The --mirror option is used to create a bare repo that maps all the branches and refs in the source(primary repo) with the branches and refs in the target(local repo).

The git clone command above will create a directory with the name of your repo. Change to that directory and add a remote for the secondary repo (repo in Bitbucket)

# cd primary_repo.git # git remote add --mirror=fetch secondary https://secondary_repo_url/secondary_repo.git

Run git fetch command to get the commits and refs from the primary repo (origin) to your local repo.

# git fetch origin

Finally run git push to update the secondary repo using local refs.

# git push secondary --all

The secondary repo will now have all the files and their revision history same as in the primary repo.

After this, whenever a commit is made on the primary repo, run the git fetch followed by git push commands to keep the two repos in sync.

# git fetch origin # git push secondary --all

Reverse Sync

The procedure explained above assumes that changes(commits) are made on the primary repo and not on the secondary. If you make any commit on the secondary repo then the git push command will fail with the following message.

error: failed to push some refs to 'https://secondary_repo_url/secondary_repo.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first merge the remote changes (e.g., hint: 'git pull') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.

In this scenario you should push the changes from secondary to primary using the command below.

# git fetch secondary # git push origin

Syncing both directions

Consider a third scenario where both the primary and secondary repos have changed and you want to keep the changes on both. In this case you need to checkout the changed files from both repos to a working tree directory, then make a new commit. Below is an example.

# mkdir ../workdir # git fetch origin # git --work-tree=../workdir/ checkout branch_name file_name1 # git fetch secondary # git --work-tree=../workdir/ checkout branch_name file_name2 # git push secondary # git push origin

Note: If you want to discard the changes in the target repo and overwrite it with local, run git push command with --force option.