How to Use git reset
git reset
is a powerful command Rietta staff use on a daily basis. However powerful, git reset
has 2 distinct features:
- Hard resets, in which will modify the working tree
- Soft resets, in which will modify the index
Often you’ll hear that the reset
command is basically the opposite of the add
command. While this is true for the default reset, there are other options to reset the index without unstaging the files as well. This blog article summarizes the subtle difference between the two different soft resets as well as provides a nice trick to view.
reset
(aka reset --mixed
)
The most common way to use git reset
is without any arguements. This results in the same operation as passing the flag --mixed
and will change our index as well as remove the files from the staging tree.
Let’s begin with checking what changes we’ve made and then adding our changes into the staging area.
$ git status
Changes not staged for commit:
modified: .circleci/config.yml
$ git add .circleci/config.yml
$ git status
Changes to be committed:
modified: .circleci/config.yml
We can utilize the reset
command to move changes out of the staging area back into our working directory, so if we supply the file path to said command, which in our case is .circleci/config.yml
,
$ git reset .circleci/config.yml
Unstaged changes after reset:
M .circleci/config.yml
$ git status
Changes not staged for commit:
modified: .circleci/config.yml
The reset command will undo and move our changes out of our staging area back into our working directory. We will be back at the exact point before we ran git add .circleci/config.yml
. When checking our git status
, we can see that our changes are now only present in our working directory and not in our staging area.
reset --soft
We can take this a step further and use the reset
command to not only undo and move changes from our staging area back to our working directory, but we can also do the same with commits in our local git repository.
For example, lets say we had already commited some changes to our local git repository but want to make some final modifications before pushing our commit to our remote repository.
$ git status
Changes not staged for commit:
modified: .circleci/config.yml
$ git add .circleci/config.yml
$ git commit -m 'We will reset these changes'
[ea7edf0] We will reset these changes
1 file changed, 1 insertion(+), 1 deletion(-)
So now that our changes are in our local git repository prior to being pushed up to our remote repository, we can use reset
with the --soft
flag along with where we’d like the HEAD
pointer to be in order to undo the commit and have the changes back in our local directory to make our modifications.
The command we want to execute will look like this: git reset --soft HEAD~1
, we are moving our HEAD pointer to the commit we had just made.
$ git log -1
ea7edf0 2 minutes ago Christopher Choi We will reset these changes
$ git reset --soft HEAD~1
$ git status
Changes to be committed:
modified: .circleci/config.yml
reset --patch
git add --patch
is very useful when wanting to be more specific with what changes we want to add to the staging area before committing. We can apply the same rule to reset
by applying the same --patch
flag when wanting to undo and put specific changes from a commit in a our local git repository back into our working directory.
So for example, if we had added a large changeset and committed it to our local git repository but want to pull out some changes from the commit, we can run git reset --patch HEAD~1
.
We are also specifying which commit we want to modify with our HEAD
pointer, HEAD~1
meaning one commit prior to what our HEAD currently is.
$ git reset --patch HEAD~1
- # run test suite!
+ # run tests!
- run:
name: Run Tests
command: |
Apply this hunk to index [y,n,q,a,d,e,?]?
The options in the array are exactly the same as git add --patch
so we can piece by piece undo our changes and put them back into our working directory. As a caveat, you cannot use --patch
with --soft
, --hard
, or --mixed
. The --patch
reset will always operate like a --mixed
reset.
Conclusion
Before I understood reset
better, I would push up changes to the remote repository and any subsequent modifications to said changes were added as separate commits. I would then do an interactive rebase to squash/fixup commits. While this process worked, it often lead to messy looking pull requests littered with force-pushed
logs. I now use git reset
s to cleanup changes on my local git repository prior to pushing up commits to the remote repository.