Git can also reset the revision history to a specific point so that you can start over from that point.
Suppose you realise your last few commits have gone in the wrong direction, and you want to go back to an earlier commit and continue from there — as if the “bad” commits never happened. Git’s reset feature can help you do that.
Git reset moves the tip of the current branch to a specific commit, optionally adjusting your staged and unstaged changes to match. This effectively rewrites the branch's history by discarding any commits that came after that point.
Resetting is different from the checkout feature:
- Reset: Lets you start over from a past state. It rewrites history by moving the branch ref to a new location.
- Checkout: Lets you explore a past state without rewriting history. It just moves the
HEAD
ref.
→
[reset to C2
...]
master
branch!There are three types of resets: soft, mixed, hard. All three move the branch pointer to a new commit, but they vary based on what happens to the staging area and the working directory.
- soft reset: Moves the cumulative changes from the discarded commits into the staging area, waiting to be committed again. Any staged and unstaged changes that existed before the reset will remain untouched.
- mixed reset: Cumulative changes from the discarded commits, and any existing staged changes, are moved into the working directory.
- hard reset: All staged and unstaged changes are discarded. Both the working directory and the staging area are aligned with the target commit (as if no changes were done after that commit).
Preparation First, set the stage as follows (e.g., in the things
repo):
i) Add four commits that are supposedly 'bad' commits.
ii) Do a 'bad' change to one file and stage it.
iii) Do a 'bad' change to another file, but don't stage it.
The following commands can be used to add commits B1
-B4
:
echo "bad colour" >> colours.txt
git commit -am "Incorrectly update colours.txt"
echo "bad shape" >> shapes.txt
git commit -am "Incorrectly update shapes.txt"
echo "bad fruit" >> fruits.txt
git commit -am "Incorrectly update fruits.txt"
echo "bad line" >> incorrect.txt
git add incorrect.txt
git commit -m "Add incorrect.txt"
echo "another bad colour" >> colours.txt
git add colours.txt
echo "another bad shape" >> shapes.txt
Now we have some 'bad' commits and some 'bad' changes in both the staging area and the working directory. Let's use the reset feature to get rid of all of them, but do it in three steps so that you can learn all three types of resets.
1 Do a soft reset to B2
(i.e., discard last two commits). Verify,
- the
master
branch is now pointing atB2
, and, - the changes that were in the discarded commits (i.e.,
B3
andB4
) are now in the staging area.
Use the git reset --soft <commit>
command to do a soft reset.
git reset --soft HEAD~2
You can run the following commands to verify the current status of the repo is as expected.
git status # check overall status
git log --oneline --decorate # check the branch tip
git diff # check unstaged changes
git diff --staged # check staged changes
Right-click on the commit that you want to reset to, and choose Reset <branch-name> to this commit
option.

In the next dialog, choose Soft - keep all local changes
.

2 Do a mixed reset to commit B1
. Verify,
- the
master
branch is now pointing atB1
. - the staging area is empty.
- the accumulated changes from all three discarded commits (including those from the previous soft reset) are now appearing as unstaged changes in the working directory.
Note howincorrect.txt
appears as an 'untracked' file -- this is because unstaging a change of type 'add file' results in an untracked file.
Use the git reset --mixed <commit>
command to do a mixed reset. The --mixed
flag is the default, and can be omitted.
git reset HEAD~1
Verify the repo status, as before.
Similar to the previous reset, but choose the Mixed - keep working copy but reset index
option in the reset dialog.

3 Do a hard reset to commit C4
. Verify,
- the
master
branch is now pointing atC4
i.e., all 'bad' commits are gone. - the staging area is empty.
- there are no unstaged changes (except for the untracked files
incorrect.txt
-- Git leaves untracked files alone, as untracked files are not meant to be under Git's control).
Use the git reset --hard <commit>
command.
git reset --hard HEAD~1
Verify the repo status, as before.
Similar to the previous reset, but choose the Hard - discard all working copy changes
option.
done!
Rewriting history can cause your local repo to diverge from its remote counterpart. For example, if you discard earlier commits and create new ones in their place, and you’ve already pushed the original commits to a remote repository, your local branch history will no longer match the corresponding remote branch. Git refers to this as a diverged history.
To protect the integrity of the remote, Git will reject attempts to push a diverged branch using a normal push. If you want to overwrite the remote history with your local version, you must perform a force push.
Preparation Choose a local-remote repo pair under your control e.g., the things
repo from Tour 2: Backing up a Repo on the Cloud.
1 Rewrite the last commit: Reset the current branch back by one commit, and add a new commit.
For example, you can use the following commands.
git reset --hard HEAD~1
echo "water" >> drinks.txt
git add .
git commit -m "Add drinks.txt"
2 Observe how the local branch is diverged.
git log --oneline --graph --all
* fc1d04e (HEAD -> master) Add drinks.txt
| * e60deae (upstream/master, origin/master) Update fruits list
|/
* f761ea6 (tag: v1.0) Add colours.txt, shapes.txt
* 2bedace (tag: v0.9) Add figs to fruits.txt
* d5f91de Add fruits.txt
3 Attempt to push to the remote. Observe Git rejects the push.
git push origin master
To https://github.com/.../things.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/.../things.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: ...
4 Do a force-push.
You can use the --force
(or -f
) flag to force push.
git push -f origin master
A safer alternative to --force
is --force-with-lease
which overwrites the remote branch only if it hasn’t changed since you last fetched it (i.e., only if remote doesn't have recent changes that you are unaware of):
git push --force-with-lease origin master
done!