Safely force pushing with Git
I want to modify the most recent commit but its already been pushed to origin. Using the --force-with-lease flag I can "more safely" push to origin and overwrite the existing data.
The --force-with-lease flag checks to make sure the remote repo matches the local cache. In other words, it ensures that another commit hasn't been pushed to the remote that is missing locally.
Setup
Two commits, I want to change the most recent commit:
$ git logg -n 2 * f3c2993 (HEAD -> master, origin/master) commit 1 * ef1318d initial commit
Change most recent:
$ git commit --amend -m "commit 2"
Current status after amending commit, notice origin and local have diverged:
$ git logga -n 3 * 5e00821 (HEAD -> master) commit 2 | * f3c2993 (origin/master) commit 1 |/ * ef1318d initial commit
Regular push fails:
$ git push origin master To git@example.com:user/repo.git ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to 'git@example.com:user/repo.git' hint: Updates were rejected because the tip of your current master 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.
Since, the local copy doesn't linearly follow the remote copy a plain push fails.
Need to force push
The normal --force flag only allows for one outcome because is doesn't check the state of the remote before overwriting it.
The --force-with-lease allows two outcomes: a "happy path" where the force occurs and a "happier path" where the force push fails (saving you from potentially losing commits) because the repository doesn't match our local expectation of the remote (likely a push from another clone of the repository)
Happy Path
Push with lease succeeds:
$ git push origin master --force-with-lease Counting objects: 8, done. Delta compression using up to 4 threads. Compressing objects: 100% (7/7), done. Writing objects: 100% (8/8), 779 bytes | 0 bytes/s, done. Total 8 (delta 5), reused 0 (delta 0) To git@example.com:user/repo.git f3c2993..5e00821 master -> master
State after push:
$ git logga -n 2 * 5e00821 (HEAD -> master, origin/master) commit 2 * ef1318d initial commit
Happier Path
Push with lease fails:
$ git push --force-with-lease To git@example.com:user/repo.git ! [rejected] master -> master (stale info) error: failed to push some refs to 'git@example.com:user/repo.git'
State after push (same as before):
$ git logga -n 3 * 5e00821 (HEAD -> master) commit 2 | * f3c2993 (origin/master) commit 1 |/ * ef1318d initial commit
Fetching the remote will pull down the changes and allow you to inspect the commits:
$ git fetch --all Fetching origin remote: Counting objects: 1, done. remote: Total 1 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (1/1), done. From git@example.com:user/repo.git 5e00821..850e9c0 master -> origin/master
However be careful with fetching, as it will update our knowledge of the remote so the next --force-with-lease would likely succeed.
Note
I use two git aliases in this post for git log:
logg = log --color --date-order --graph --oneline --decorate logga = log --color --date-order --graph --oneline --decorate --all