To complement torek’s excellent answer:
A force-push can cause problems with later merges.
If you force-push a branch A, you are removing some existing commits from that branch (otherwise you would not need to force). If (as described in torek’s answer) these commits are also referenced from another branch B, then they will remain in that branch. If you later later merge the two branches A and B, A will contain the “new” (forced) commits, and B “old” commits.
In this situation
git merge will probably not do what you want. In the best case, you will get merge conflicts. Worst case, you will not but the result will still be wrong. For example, if you removed a commit c1 from A using
git rebase -i, if c1 is also in B it will be re-introduced if you merge A and B. That might silently re-introduce a bug :-(.
So if you force-push, make sure the commits you remove are not referenced from other branches / tags (or force-push these branches/tags as well).
Illustration of a deleted commit “returning”
Run the bash script below. The script creates a git repository, and branches master, branch1 with the following commits:
- good commit (branch1)
- bad commit (master)
git reset --hardto discard “bad commit” from master
- merges branch1 into master
Output from the script:
Initialized empty Git repository in .... [... creating commits ...] ### Bad commit in history: * 7549dcc (HEAD, master) bad commit * 31afec8 Initial ### Removing bad commit: HEAD is now at 31afec8 Initial ### Bad commit no longer in history: 31afec8 (HEAD, master) Initial ### Merging branch1: Updating 31afec8..be801e5 Fast-forward 0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 b create mode 100644 c ### Bad commit is back! be801e5 (HEAD, master, branch1) good commit 7549dcc bad commit 31afec8 Initial
As you can see, “bad commit” is removed from master using
git reset --hard. However, “bad commit” is still contained in branch1, so when branch1 is merged into master, “bad commit” returns.
Note: This example does not actually force-push, but if you worked with an upstream repo, you would have to force-push after removing the bad commit – the result would be the same.
#!/bin/bash set -e if [ -e gitrepo ]; then echo "Please remove gitrepo"; exit 1; fi git init gitrepo; cd gitrepo; git config core.pager cat touch a; git add a; git commit -m Initial touch b; git add b; git commit -m "bad commit" git checkout -b branch1; touch c; git add c; git commit -m "good commit" git checkout master echo "### Bad commit in history: " git log --oneline --decorate echo "### Removing bad commit: " git reset --hard HEAD~1 echo "### Bad commit no longer in history: " git log --oneline --decorate echo "### Merging branch1: " git merge branch1 echo "### Bad commit is back!" git log --oneline --decorate