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.
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
$ 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.
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
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~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 reset will always operate like a
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 resets to cleanup changes on my local git repository prior to pushing up commits to the remote repository.