Git Reset Git

Dec 18th, 2020 - written by Kimserey with .

Git reset is a command used to reset the current HEAD to a specified state. Common scenarios of using git reset are for clearing unstaged changes, reverting a staged commit or simply reverting a commit made by mistake back to the working tree. In this post we’ll look at Git reset with examples.

Reset Uncommitted Changes

The first usage of git reset would be to reset staged changes.

1
2
3
4
5
6
❯ git add --all
❯ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   a.md

Here we have staged changes in a.md.

1
2
3
❯ git reset
Unstaged changes after reset:
M       a.md

Reset will push the changes back to an unstaged state. Then if we want to completely discard a.md changes we can use checkout:

1
❯ git checkout -- a.md

We can also choose specific files to reset,

1
❯ git reset -- a.md

It’s also possible to reset a specific file to a specific point in time. For example consider the following history:

1
2
3
4
5
6
❯ git log -5 --oneline
2f56875 (HEAD -> master) Commit changed
758e9eb Commit changed
c190998 Added something in master
5cd20ba Changed in master Added test
b99c37f some changes

If we bring back to working tree a specific file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
❯ git reset HEAD~ -- a.md
Unstaged changes after reset:
M       a.md

❯ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   a.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   a.md

We can see that the revert has put a.md into unstaged changes but if we look at the status, a staged change on a.md has also been added. This is because the reset will stage the undo of the commit 2f56875 on a.md and at the same time add the change coming from 2f56875 as unstaged so that we have them in working tree. Also note that the current branch HEAD hasn’t been changed:

1
2
3
4
5
6
❯ git log -5 --oneline
2f56875 (HEAD -> master) Commit changed
758e9eb Commit changed
c190998 Added something in master
5cd20ba Changed in master Added test
b99c37f some changes

Reset Modes

Reset also allows us to modify the current branch by reverting to a specific point in time. For example if we have the following history:

1
2
3
4
5
6
❯ git --no-pager log -5 --oneline
2f56875 (HEAD -> master) Commit changed
758e9eb Commit changed
c190998 Added something in master
5cd20ba Changed in master Added test
b99c37f some changes

We can specify a commit to reset back:

1
git reset c190998

Resets back to commit c190998 and put all changes into working tree.

1
2
3
4
❯ git --no-pager log -3 --oneline
c190998 (HEAD -> master) Added something in master
5cd20ba Changed in master Added test
b99c37f some changes

And we have the content of 758e9eb and 2f56875 as unstaged changes:

1
2
3
4
5
6
7
8
9
10
11
12
❯ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   a.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        b.md

no changes added to commit (use "git add" and/or "git commit -a")

For safety Git will also save the original tip on ORIG_HEAD so if we want to revert back the changes, we could do:

1
git reset --hard ORIG_HEAD

This will revert back to the original HEAD 2f56875. --hard is known as a reset mode. There are multiple reset modes which are detailed in the official documentation but the ones I have been using the most are --hard, --soft and --mixed (the default mode):

  • --hard will reset the working tree and index to the specified commit - use it to remove everything,
  • --mixed (default behaviour) will reset the index and bring all changes from the commits into unstaged area,
  • --soft is similar to mixed except that it doesn’t change the index and working tree, the changes from the commits are placed in the index.

For example if we made changes in c.md and added that to the index, a reset with --mixed would move back all changes into unstaged changes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
echo "Adding some changes in C" >> c.md
❯ git add --a

❯ git --no-pager log -3 --oneline
498420c (HEAD -> master) added a and b
202d11a added c
4cf5085 added b

❯ git reset 202d11a
Unstaged changes after reset:
M       a.md
M       b.md
M       c.md

❯ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   a.md
        modified:   b.md
        modified:   c.md

As opposed to --soft which would keep them as staged changes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
echo "Adding some changes in C" >> c.md
❯ git add --a

❯ git --no-pager log -3 --oneline
498420c (HEAD -> master) added a and b
202d11a added c
4cf5085 added b

❯ git reset --soft 202d11a

❯ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   a.md
        modified:   b.md
        modified:   c.md

While --hard would just get rid of all changes in index and working tree but also the changes from the commits reverted:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
echo "Adding some changes in C" >> c.md
❯ git add --a

❯ git --no-pager log -3 --oneline
498420c (HEAD -> master) added a and b
202d11a added c
4cf5085 added b

❯ git reset --hard 202d11a
HEAD is now at 202d11a added c

❯ git status
On branch master
nothing to commit, working tree clean

We can see that 498420c has been discarded together with our change on c.md.

External Sources

Designed, built and maintained by Kimserey Lam.