Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git: --force-with-lease and multiple pushurls

Tags:

git

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?

like image 288
ivan Avatar asked Sep 26 '16 00:09

ivan


People also ask

What does force with lease do git?

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.

How do I push all branches to a remote?

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.

Does git push force affect other branches?

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.

Does git push push to all branches?

No, git push only pushes commits from current local branch to remote branch that you specified in command.


1 Answers

Very interesting question! I've tried it out and successfully reproduced what you are describing with Git 2.11.0 in these test repos:

  • https://github.com/hkdobrev/git-test
  • https://github.com/hkdobrev/git-test2

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 with push.default set to matching or with multiple push destinations configured with remote.*.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.g git push origin +master to force a push to the master branch). See the <refspec>... section above for details.

like image 99
Haralan Dobrev Avatar answered Oct 15 '22 10:10

Haralan Dobrev