Home > Software design >  Two remote repositories out of sync (non-fast-forwards error)
Two remote repositories out of sync (non-fast-forwards error)

Time:07-27

I have two remote repositories in Bitbucket and Github that have been set up to sync master branch from Bitbucket to Github (one-way sync) by running this shell script regularly:

git clone --mirror https://$bitbucket_url/repo.git $REPO
cd $REPO
git remote add --mirror=fetch github https://$GIT_TOKEN@$github_url/repo.git
git fetch origin
git push --tags github master

The synchronisation has been doing its job well but it started to get rejected recently with the following error (real git token and github url have been masked below):

! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'https://$GIT_TOKEN_MASKED@$github_url/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Based on the error message, I understand that there are commits on Github that Bitbucket doesn't have? So I need to do a git pull from github before git push? I did attempt that but it was complaining that I can't do git pull because there is no working tree. Any ideas how I can fix this?

CodePudding user response:

Per the Git reference material (https://git-scm.com/docs/git-clone) using --mirror when cloning will create a bare repository, intended to be used to push between two remotes. A bare repository has no working tree, no checked out branch or commit and therefore nothing to pull into. Note, that a pull entails a fetch and a merge, but in this case there is nothing to merge into.

From the Git reference material:

--mirror

Set up a mirror of the source repository. This implies --bare. Compared to --bare, --mirror not only maps local branches of the source to local branches of the target, it maps all refs (including remote-tracking branches, notes etc.) and sets up a refspec configuration such that all these refs are overwritten by a git remote update in the target repository.

After line 4 of your code, your local repository will contain all branches and tags from the Bitbucket repository, but the repository will have no working copy and no commit or branch will be checked out.

On line 5 (git push --tags github master) your push is failing because the head commit of the master branch on the Github server is not a parent to the master branch that you are attempting to push to it (i.e. non-fast-forward) and the server is trying to prevent you from undoing something that is already there. This probably means that someone pushed a commit to the master branch on the Github server, which was never pushed to the Bitbucket server.

If you were to attempt to pull from the Github server at this point, there would be nothing to pull into, since you are working from a bare repo.

To synchronize the two, you will need to use the following steps:

  1. git clone the Bitbucket server somewhere, without mirroring.
  2. Checkout the master branch.
  3. Add the Github server as a remote using remote add without mirroring.
  4. git pull the master branch on the Github server into your local master branch, which should already be checked out.
  5. If there are merge conflicts, you will need to resolve them and complete the merge.
  6. git push the local master branch to each remote so that you finish with both remotes in synch.

To avoid having this problem in the future, you may want to add some safegaurds to your Github server to prevent users from pushing to the master branch there since it is intended to be updated using your script.

  • Related