Git reset is part of the git commands that used to confuse me, as I did not understand its purpose or use-cases at first.

On top of that, it seemed to be doing very different things depending on its input, which is either a commit or a path.

But once I dug a bit deeper into each option of this command and understood the logic behind it, which eventually is about undoing staged or committed changes, it really helped me to manage my commit history or revert my mistakes, which is particularly useful for my rebase workflow.

With this article, I hope to help you clarify how this command works and how you can use it in your daily development tasks.

Git reset with a path: unstaging files

When used with a path, the reset command does the opposite of git add: it removes changes from the staging area.

git reset with a file

NB: a new command was recently added to git, git restore, to fulfill this purpose and will probably become the advised way to perform this action. For now, git restore is still experimental. Still, I think it should be preferred over git reset for unstaging changes, as its implementation seems more intuitive, whereas git reset should be used to update the branch’s head, as we will see after.

But if you want to use the reset command for unstaging files, here are some examples:

git reset # unstage all changes
git reset file-a file-b # unstage file-a and file-b files
git reset folder-a/ # unstage all changes in the folder folder-a

I personally do not use git reset for this purpose as I don’t deal much with staging my changes. I usually commit everything (I have set an alias for this purpose that I am always using), and if I need to commit partial changes, I prefer to use my IDE to do so.

Git reset with a commit: modifying the head of your branch

When used with a commit, git reset allows us to change the head of our branch. While doing so, it allows us to extract the changes between our current head and the new one, and put them in our staging area, working directory, or forget them depending on the mode used.

Let’s see how the different modes work.

The soft mode 🪶

The soft mode keeps your current staging area and working tree as it is while moving your head to the specified commit. Changes between your previous head and the new one are added to the staging area.

git reset in soft mode

This option provides a great way to rewrite your history in only one commit, instead of using an interactive rebase to squash your commits:

git reset --soft main
git commit -m' feat: an atomic feature'

The mixed mode 🥗

Using git reset with the mixed mode (the default mode) puts all your changes between your previous and current head, including your current staged changes, in the working directory.

git reset in mixed mode

This option is handy when you want to rewrite your history in several commits, for instance to create two commits from all the changes you made from your main branch:

git reset --mixed main
git commit --interactive -m' fix: a correction aside the main feature'
git commit --all -m' feat: the main feature'

The hard mode 🪨

The heavy-handed one! It sets the index to the given commit and removes all changes between your previous and current index or in your working directory.

git reset in hard mode

This option is perfect if you want to get rid of your current changes:

git reset --hard HEAD # or just git reset --hard

NB: use this command only if you know you are not interested in keeping your current changes, as you will not be able to recover them. If you are unsure yet but want to start back from your last commit, use git stash instead to put your current changes aside.

Another interesting use-case is to reset your branch to the state of the remote one (a kind of force pull):

git reset --hard origin/my-branch

You need to be cautious when using this command as you might lose changes. If you made a mistake, it is still possible to get your lost commits back, but not your current staged and unstaged changes.

The keep mode ✊

This mode is a bit peculiar. It is similar to the hard mode as it will drop the commits between your previous and new head. But it will carry on your unstaged changes and abort if there is any conflict between these changes and the removed commits.

git reset in keep mode

This command is dedicated to doing one thing: remove commits while being safe that they won’t conflict with our current unstaged changes. I don’t really find myself in this kind of situation, but if that happens, I guess it is valuable to know this option.

What about the merge mode?

As explained in the documentation, reset --merge is meant to be used when resetting out of a conflicted merge. This is a particular situation, and as I don’t use merge in my workflow, I have never had the use for this command.

But do not hesitate to read the documentation if you think you might be interested.