I am trying to understand the difference between
git push --force
and
git push --force-with-lease
My guess is that the latter only pushes to the remote if the remote has commits that the local branch doesn't have?
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.
force overwrites a remote branch with your local branch. --force-with-lease is a safer option that will not overwrite any work on the remote branch if more commits were added to the remote branch (by another team-member or coworker or what have you). It ensures you do not overwrite someone elses work by force pushing.
If you want to prioritize your local branch over the remote one, overwriting any commits made along the way, then by all means go for it. In a less extreme situation, Force Pushing can still be appropriate. Sometimes we're simply working on a private branch that should not be used by anyone else.
The --force option for git push allows you to override this rule: the commit history on the remote will be forcefully overwritten with your own local history. This is a rather dangerous process, because it's very easy to overwrite (and thereby lose) commits from your colleagues.
force
overwrites a remote branch with your local branch.
--force-with-lease
is a safer option that will not overwrite any work on the remote branch if more commits were added to the remote branch (by another team-member or coworker or what have you). It ensures you do not overwrite someone elses work by force pushing.
I think your general idea surrounding the command is correct. If the remote branch has the same value as the remote branch on your local machine- you will overwrite remote. If it doesn't have the same value- it indicates a change that someone else made to the remote branch while you were working on your code and thus will not overwrite any code. Obviously if there are additional commits in remote then the values won't be the same.
I just think of --force-with-lease
as the option to use when I want to make sure I don't overwrite any teammates code. A lot of teams at my company use --force-with-lease
as the default option for a fail-safe. Its unnecessary in most circumstances but will save you lots of headache if you happen to overwrite something that another person contributed to remote.
I'm sure you looked at the docs but there might be some more wordy explanation contained in here:
https://git-scm.com/docs/git-push
Looking for an answer drawing from credible and/or official sources.
The "compare and swap" mentioned by torek in the comments and in his other answer is further illustrated by the sources of Git itself.
the latter only pushes to the remote if the remote does not have commits that the local branch doesn't have?
That feature was introduced in this commit (Dec. 2013, Git v1.8.5-rc0)
--force-with-lease
will protect all remote refs that are going to be updated by requiring their current value to be the same as some reasonable default, unless otherwise specified;For now, "some reasonable default" is tentatively defined as "the value of the remote-tracking branch we have for the ref of the remote being updated", and it is an error if we do not have such a remote-tracking branch.
So "lease" means:
"
force-with-lease
": You assume you took the lease on the ref when you fetched to decide what the rebased history should be, and you can push back only if the lease has not been broken.
The sources still mentions "cas":
- This option was originally called "
cas
" (for "compare and swap"), the name which nobody liked because it was too technical.- The second attempt called it "lockref" (because it is conceptually like pushing after taking a lock) but the word "lock" was hated because it implied that it may reject push by others, which is not the way this option works.
- This round calls it "force-with-lease".
You assume you took the lease on the ref when you fetched to decide what the rebased history should be, and you can push back only if the lease has not been broken.
So: "git push --force-with-lease
vs. --force
"
As I mentioned in "push --force-with-lease
by default", as Git 2.13 (Q2 2017) mentions, that the option --force-with-lease
can be ignored if a background process (like the ones you find in an IDE with a Git plugin) runs git fetch origin
.
In that case, --force
prevails.
As Pavlus adds in the comments:
it is not ignored per se, it is just now you have identical refs for local remote head and remote head, so
--force-with-lease
will behave correctly -- compare these two, and if in that interval of time between fetch and push, someone updated remote, it won't behave as--force
, it will still fail.
Another difference: before Git 2.29 (Q4 2020), pushing a ref whose name contains non-ASCII character with the "--force-with-lease
" option did not work over smart HTTP protocol.
It would work with git push --force
.
See commit cd85b44 (21 Jul 2020) by brian m. carlson (bk2204
).
(Merged by Junio C Hamano -- gitster
-- in commit c2796ac, 30 Jul 2020)
remote-curl
: make--force-with-lease
work with non-ASCII ref namesReported-by: Frej Bjon
Signed-off-by: brian m. carlson
When we invoke a remote transport helper and pass an option with an argument, we quote the argument as a C-style string if necessary.
This is the case for the cas option, which implements the--force-with-lease
command-line flag, when we're passing a non-ASCII refname.However, the remote
curl
helper isn't designed to parse such an argument, meaning that if we try to use--force-with-lease
with an HTTP push and a non-ASCII refname, we get an error like this:error: cannot parse expected object name '0000000000000000000000000000000000000000"'
Note the double quote, which
get_oid
has reminded us is not valid in an hex object ID.Even if we had been able to parse it, we would send the wrong data to the server: we'd send an escaped ref, which would not behave as the user wanted and might accidentally result in updating or deleting a ref we hadn't intended.
Since we need to expect a quoted C-style string here, just check if the first argument is a double quote, and if so, unquote it.
Note that if the refname contains a double quote, then we will have double-quoted it already, so there is no ambiguity.We test for this case only in the smart protocol, since the DAV-based protocol is not capable of handling this capability.
We use UTF-8 because this is nicer in our tests and friendlier to Windows, but the code should work for all non-ASCII refs.While we're at it, since the name of the option is now well established and isn't going to change, let's inline it instead of using the #define constant.
Git 2.30 (Q1 2021) adds git push --force-if-includes
When a local branch that is based on a remote ref, has been rewound and is to be force pushed on the remote, "
--force-if-includes
" runs a check that ensures any updates to the remote-tracking ref that may have happened (by push from another repository) in-between the time of the last update to the local branch (via "git pull
", for instance) and right before the time of push, have been integrated locally before allowing a forced update.
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