Advent Day 20: Force with Lease
This is day 20 of my Git Tips and Tricks Advent Calendar. If you want to see the whole list of tips as they're published, see the index.
On one of my projects, we usually use a workflow where we create short-lived topic branches and try to keep a reasonably clean history. This means that we often rebase our topic branches onto the master branch so that you'll have a nice history when we merge the pull requests.
This all sounds nice, but there's a bit of a "gotcha" when it comes to this workflow: if you push a branch to the server and you later rebase that branch, the server will think that you're not up-to-date, since you don't have the latest commits on that branch:
To github.com:ethomson/repo.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:ethomson/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.
You'll now need to actually force push to the remote, basically
removing the existing commits and pushing up your new, rewritten branch
in their place. And this is a bit scary, since there's really no
checking. Unless, of course, you use the --force-with-lease
option.
The "force with lease" option basically does an atomic compare-and-swap on the branch that you're pushing to, based on the last information you fetched. In other words, it ensures that you're force pushing to remove what you think that you're force pushing to remove.
Assuming that you're up-to-date: you've just done a git fetch
on the
remote, when you run git push --force-with-lease
, you'll overwrite the
remote:
To github.com:ethomson/repo.git
+ d008110...3653e05 master -> master (forced update)
However, if you're not up-to-date... maybe somebody snuck in and
pushed to your branch while you weren't paying attention. If you were
only to git push --force
then you would overwrite their commits, losing
them. But if you git push --force-with-lease
then you will not
overwrite the branch and lose their changes.
To github.com:ethomson/repo.git
! [rejected] master -> master (stale info)
error: failed to push some refs to 'git@github.com:ethomson/repo.git'
This makes git push --force-with-lease
a good default, and I suggest
you get in the muscle memory of using it (or set an alias if you prefer).
You can always do a "true" force if you really need to, but this helps
avoid any accidental mistakes.