I just learned about git push --force-with-lease
. It\'s pretty awesome. But, of course, I don\'t use force that often, and so I\'m worried that I might forget a
For people using OMYZSH you can simply use ggfl
.
I'm worried that I might forget about this nifty feature the next time I need it.
Git 2.13 (Q2 2017) explains why there is no "protection" against this push option being forgotten, because even if you do not forget it at the git push
level, it might still be ignored.
See commit f17d642 (19 Apr 2017) by Ævar Arnfjörð Bjarmason (avar).
(Merged by Junio C Hamano -- gitster -- in commit 46bdfa3, 26 Apr 2017)
push
: document & test--force-with-lease
with multiple remotesDocument & test for cases where there are two remotes pointing to the same URL, and a background fetch & subsequent
git push --force-with-lease
shouldn't clobber un-updated references we haven't fetched.Some editors like Microsoft's VSC have a feature to auto-fetch in the background, this bypasses the protections offered by
--force-with-lease
&--force-with-lease=<refname>
, as noted in the documentation being added here.
So the documentation for git push now includes:
general note on safety: supplying this option without an expected value, i.e. as
--force-with-lease
or--force-with-lease=<refname>
interacts very badly with anything that implicitly runsgit fetch
on the remote to be pushed to in the background, e.g.git fetch origin
on your repository in a cronjob.The protection it offers over
--force
is ensuring that subsequent changes your work wasn't based on aren't clobbered, but this is trivially defeated if some background process is updating refs in the background. We don't have anything except the remote tracking info to go by as a heuristic for refs you're expected to have seen & are willing to clobber.If your editor or some other system is running
git fetch
in the background for you a way to mitigate this is to simply set up another remote:
git remote add origin-push $(git config remote.origin.url)
git fetch origin-push
Now when the background process runs
git fetch origin
the references onorigin-push
won't be updated, and thus commands like:
git push --force-with-lease origin-push
Will fail unless you manually run
git fetch origin-push
.
This method is of course entirely defeated by something that runsgit fetch --all
, in that case you'd need to either disable it or do something more tedious like:git fetch # update 'master' from remote git tag base master # mark our base point git rebase -i master # rewrite some commits git push --force-with-lease=master:base master:master
I.e. create a
base
tag for versions of the upstream code that you've seen and are willing to overwrite, then rewrite history, and finally force push changes tomaster
if the remote version is still atbase
, regardless of what your localremotes/origin/master
has been updated to in the background.
I want to be reminded that I shouldn't use -f
, but I don't want to be fooled into believing that -f
means --force-with-lease
. So this is my take:
git() {
if [[ $@ == 'push -f'* ]]; then
echo Hey stupid, use --force-with-lease instead
else
command git "$@"
fi
}
Add to your .bash_profile
, .bashrc
or .zshrc
.
You can create a bash function that replaces git
and use --force-with-lease
instead of --force
# replaces `git push --force` with `git push --force-with-lease`
git() {
if [[ $@ == 'push -f'* || $@ == 'push --force'* ]]; then
command git push --force-with-lease
else
command git "$@"
fi
}
or, in one line:
git() { if [[ $@ == 'push -f'* || $@ == 'push --force'* ]]; then command git push --force-with-lease; else command git "$@"; fi; }
Just add it to your ~/.bashrc
or ~/.zshrc
.
AFAIK there is no configuration available to tell git to always use force-with-lease
instead of force
. This seems to be a good example for a feature request; if you have no problem to dive into the git code base you could implement it yourself and submit it for review.
EDIT As it stands, this is still true in April 2019.
Until then the only option I see is, as so often, to create an alias
which serves this purpose.
To create an alias one would use git config --global alias.<alias-name> <command>
, in our case I would suggest something similar to this.
git config --global alias.pushf "push --force-with-lease"
This will create an entry in your global .gitconfig
file (which you can usually find in your home directory). After this you can simply use git pushf
to force-with-lease.
If you want to implement the feature yourself but aren't sure where to start, you should at first take a look at the documentation directory in the git repository. Here you can find the coding guidelines and information on how to submit patches.
You can find all these links and more on the official community page.
My solution was to create a wrapper script, and use an alias so that I always use it in place of the real git
.
Whenever I try to git push -f
, I see the following:
⚡ git push -f
use this instead so you don't cause race conditions in the
repo: git push --force-with-lease
Some advantages of this script are:
--force-with-lease
, so i don't get nagged when i get it wronggit push --force
will work.How to implement it:
-f
git
These instructions assume Linux or Mac, running bash. I haven't tried this with zsh or Windows, but I assume it'll work there too.
~/.bash_profile
:
alias git=~/.git_wrapper.sh
~./git_wrapper.sh
:
#!/bin/bash
for arg in "$@"; do
if [ "$arg" = "push" ]; then
ispush=1
elif [ "$ispush" = 1 -a "$arg" = '-f' ]; then
echo "use this instead so you don't cause race conflicts in the repo: git push --force-with-lease"
exit 1
fi
done
git "$@"
With those changes, restart your terminal and git
should now get uppity when you try to force push.