I have a git repo with remote origin
mirrored on 3 hosts.
$ git remote -v
origin [email protected]:username/repo.git (fetch)
origin [email protected]:username/repo.git (push)
origin [email protected]:username/repo.git (push)
origin [email protected]:username/repo.git (push)
Everything, everywhere is at commit A.
$ git rev-parse HEAD
A
$ cat .git/refs/remotes/origin/master
A
I author commit B and push it. Now everyone's at commit B.
$ git push origin master
To github.com:username/repo.git
A...B master -> master
To gitlab.com:username/repo.git
A...B master -> master
To bitbucket.org:username/repo.git
A...B master -> master
$ git rev-parse HEAD
B
$ cat .git/refs/remotes/origin/master
B
Now I notice a mistake in that last commit, so I fix it and ammend the commit. This puts me out of sync with the remote(s).
$ git rev-parse HEAD
C
$ cat .git/refs/remotes/origin/master
B
I like to avoid blindly --force
pushing, so I use --force-with-lease
, but this fails in an interesting way.
$ git push --force-with-lease origin master
To github.com:username/repo.git
+ B...C master -> master (forced update)
To gitlab.com:username/repo.git
! [rejected] master -> master (stale info)
To bitbucket.org:username/repo.git
! [rejected] master -> master (stale info)
The problem is, --force-with-lease
will only consider the push safe if the remote ref is at the same commit as last time I communicated with it, and my local records the sha1 of that commit in .git/refs/remotes/origin/master
. As soon as the first mirror (GitHub) is updated, git updates my local's remote ref to commit C, causing the push attempts to GitLab and Bitbucket to fail, since we're now expecting them to be at commit C.
I want to figure this out, so first I force the GitHub mirror back to commit B.
$ git push origin +B:refs/heads/master
To github.com:username/repo.git
+ C...B B -> master (forced update)
Everything up-to-date
Everything up-to-date
Now I need to be more specific about which commit I expect the remotes to be at for the push. The documentation says you can specify exactly which ref to update, and what commit you expect it to currently be at with --force-with-lease=<refname>:<expect>
, so I try that.
$ git push --force-with-lease=origin/master:B origin master
To github.com:username/repo.git
! [rejected] master -> master (non-fast-forward)
To gitlab.com:username/repo.git
! [rejected] master -> master (non-fast-forward)
To bitbucket.org:username/repo.git
! [rejected] master -> master (non-fast-forward)
Clearly I'm doing something wrong. Maybe I've got the <refname>
wrong? I feel I'm so close. What am I missing?
Introducing Force with Lease Using this flag, git checks if the remote version of the branch is the same as the one you rebase, i.e. did someone push new commits when we were rebasing. The push is then rejected if the remotes branch is changed. It's like taking a lease on the version of the branch you started changing.
The steps to follow in order to push new Git branches to remote repos such as GitHub, GitLab or Bitbucket are as follows: Clone the remote Git repo locally. Create a new branch with the branch, switch or checkout commands. Perform a git push with the –set-upstream option to set the remote repo for the new branch.
It's well known that git's push --force is strongly discouraged as it can destroy other commits already pushed to a shared repository. This isn't always completely fatal (if the changes are in someone's working tree then they can be merged), but at the very least it's inconsiderate, at worst disastrous.
No, git push only pushes commits from current local branch to remote branch that you specified in command.
Very interesting question! I've tried it out and successfully reproduced what you are describing with Git 2.11.0 in these test repos:
I was able to successfully push with --force-with-lease
to both remote URLs by using the following form:
git push --force-with-lease origin +master
Notice the +
sign before the branch name. Here is the output:
$ git push --force-with-lease origin +master
Counting objects: 2, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 232 bytes | 0 bytes/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To github.com:hkdobrev/git-test.git
+ 099b95f...08c7548 master -> master (forced update)
Counting objects: 2, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 232 bytes | 0 bytes/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To github.com:hkdobrev/git-test2.git
+ 099b95f...08c7548 master -> master (forced update)
From the git-push (1)
man page:
Note that
--force
applies to all the refs that are pushed, hence using it withpush.default
set to matching or with multiple push destinations configured withremote.*.push
may overwrite refs other than the current branch (including local refs that are strictly behind their remote counterpart). To force a push to only one branch, use a+
in front of the refspec to push (e.ggit push origin +master
to force a push to themaster
branch). See the<refspec>...
section above for details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With