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 agit 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:
git clone
the Bitbucket server somewhere, without mirroring.- Checkout the
master
branch. - Add the Github server as a remote using
remote add
without mirroring. git pull
themaster
branch on the Github server into your localmaster
branch, which should already be checked out.- If there are merge conflicts, you will need to resolve them and complete the
merge
. git push
the localmaster
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.