Cancelling bad stuff in Git

In this post I am going to provide some examples of Git usage that may come in handy in case of unsuccessful previous actions during staging, committing, and merging.

Let’s say you have staged some files to be further committed with git add and then change your mind. To unstage all or some files, invoke the git reset command (which is in a way an inverse of git add). A scenario for all changed files:

$ git add .
$ git reset

To unstage a single file, run the following command:

$ git reset myfile.py

If you look up the Git documentation, the previous two example correspond to the form of git reset that resests the state of the index (what is going to be committed next) to what is currently in the last commit, i.e. HEAD. As such they can be written in more explicit form as follows:

$ git reset HEAD
$ git reset HEAD myfile.py

If you have already made a commit, but then want to undo it, git reset can be used in one of these three ways:

$ git reset --soft HEAD~1
$ git reset --mixed HEAD~1
$ git reset HEAD~1

Such form of git reset operates on commits rather than on files (as the previous examples do). What is done here is that what HEAD points to has moved: initally HEAD points to the last commit that we are going to undo, and after any of these commands HEAD points to HEAD~1, i.e. the previous commit before the last one.

The option --soft only moves the commit history, but doesn’t touch the index. It means that whatever was staged with git add previously will remain staged. The option --mixed, however, updates the index to match the target commit (in our case HEAD~1), so you will have a possibility for performig staging afterwards. It is also the default option, so it may be ommitted as in the third line.

To discard changes made to a specific file and update it to what is currently in the last commit, the git checkout command can be used:

$ git checkout -- myfile.py

The dash-dash literal (--) here is optional, and is used to separate options (starting with dash) and parameters (in this case, the filename). See this Stack Overflow question for more details on that matter.

To get a better understanding of the various forms of git reset and git checkout, please refer to this chapter of the official Git book.

The last use case is unsuccessful merge (resulting in conflics) that has to be aborted, thus leading to reconstruction of the pre-merge state. In this situation, the following command shall be invoked:

$ git merge --abort