BAHRI ALTAY/ISTOCK/THINKSTOCK

Afraid no more: use git reset and its options: — soft, — mixed, and — hard like a pro.

Iryna Stein

--

Often we get by performing our daily tasks using the routines that became second nature and are no longer questioned — “why do they work the way they work”. Or if we are faced with an unknown at some point, we might just try to avoid it all together. This was my story with Git for a while until one day I realized that I no longer want to be running away from Git but want to make friends with it and understand how it works. So I decided to write a series of blogs that break down most commonly used git commands in a very beginner friendly manner, so you can start confidently using them as well!

This blog is not an introduction to Git, this is just a simple and quick reference on how to use a very popular git reset command and its options.

git reset — resets current HEAD to the specified state

- git-scm.com

In more simple words, git reset is a command that allows you to “undo” your commit after it has already been stored in git history, copy all of your changes and either move them to a new commit or discard them all together. git reset modifies commit history and therefore has to be used with caution. It is recommended to use git reset mostly when working on the local feature branch before it was pushed to GitHub and shared with other developers. Mainly because git reset modifies commit history and might create severe conflicts and difficulties when reconciling branch changes.

There are three types of git-reset: soft, mixed (default if flag is not specified) and hard. Which one to use depends on your situation. And hopefully this blog will help you make an educated choice on which command to use and when.

Lets say that we have a small project that contains the following commits:

git log --oneline

edc24fe (HEAD -> main) scaffold index.html file
d2f198d add styles to css file
62e1ebf add js script
d60feac rename index.txt file to README.md
d106e5b create basic project structure
5f98dc6 initial commit

Suddenly we realized that we would like continue our html work on a different branch: html-branch. But “oops”, seems like we have already committed the scaffolding changes to the main branch. No problem! We can remove this commit from history and move changes to a new branch using git reset --soft <commit hash> :

git reset --soft d2f198d
git switch -c html-branch
git commit -m "scaffold index.html file"

Why git reset --soft was the best choice here?

Because we wanted to move the commit while keeping it staged so we can recommit it on another branch. This command simply removes the commit-hash from your Git history while preserving all changes in your working directory and keeping them in staging area for the next step.

Compare commit history by running git log --online on both branches to confirm that the reset was successful.

//after running git reset --soft d2f198d on main branch
//main branch commit log should look like this:

d2f198d (HEAD -> main) add styles to css file
62e1ebf add js script
d60feac rename index.txt file to README.md
d106e5b create basic project structure
5f98dc6 initial commit


//and html-branch log should look like this:

edc24fe (HEAD -> html-branch) scaffold index.html file
d2f198d (main) add styles to css file
62e1ebf add js script
d60feac rename index.txt file to README.md
d106e5b create basic project structure
5f98dc6 initial commit

We continue working on the html file by adding more content to the page. And then we realize that we committed something to Git history that we didn’t want to really commit just yet. We wanted to make more changes inside our bio paragraph and then make a commit with a message “add bio”.

git log --oneline 

5b70fec (HEAD -> html-branch) add bio //we don't want this commit just yet
609e1c9 add parapraph
edc24fe scaffold index.html file
d2f198d (main) add styles to css file
62e1ebf add js script
d60feac rename index.txt file to README.md
...

For that we could use git reset --mixed <commit hash>or drop the --mixed flag and use git reset <commit hash>command since it always defaults to mixed reset when the flag is not specified:

git reset 609e1c9

//...do some more work on the bio paragraph

git commit -am 'add extended bio description'

Why did we choose git reset --mixed here?

Because this command removes commit from Git history, preserves the changes in your working directory and un-stages all files from staging area. Now we can continue our work: add more files, folders, modify code and when we are satisfied with the new changes, we will create a new fresh commit(s) from scratch.

//after running git reset 609e1c9 and adding a new commit your new modified
//commit history should look like this:

2925525 (HEAD -> html-branch) add extended bio description
609e1c9 add parapraph
edc24fe scaffold index.html file
d2f198d (main) add styles to css file
62e1ebf add js script
d60feac rename index.txt file to README.md

We continue the work, add some code to our index.js file, commit it, add more code, commit it again and so on. And then we realized that we were not supposed to write any of JavaScript code just yet, because we needed to wait for PM approval on the project direction. But, oh no! the commits are already on the branch. We don’t want to look silly and expose our lame history to reviewers, so we would like to remove commits and all the changes that have been done with it. However, our current Git history already looks like this. What to do?

git log --oneline

be4dd7d (HEAD -> html-branch) add sum function
6985af7 add console logs to js file
2925525 add extended bio description
609e1c9 add parapraph
edc24fe scaffold index.html file
d2f198d (main) add styles to css file
....

We can use git reset --hard <commit hash>command to completely erase commits from history and remove all the changes that were made in our working directory and staging area. Please be aware that all changes that associated with the removed commits will be gone and you will not be able to retrieve them (at least lets just leave it at that for now ;))

git reset --hard 2925525

HEAD is now at 2925525 add extended bio description

git log --online

2925525 (HEAD -> html-branch) add extended bio description
609e1c9 add parapraph
edc24fe scaffold index.html file
d2f198d (main) add styles to css file
62e1ebf add js script
d60feac rename index.txt file to README.md
....

git status

On branch html-branch
nothing to commit, working tree clean

We see that now our two JavaScript related commits are erased from the Git history. Working directory changes are gone as well and our staging are is clean. Phew, we saved ourselves some embarrassment, no trace of wrong work is left!

That’s all — now you can use git reset and its options like a pro in your day to day work.

Subscribe, for more useful snippets.

Note:

working directory — files, folders etc where you write your code

staging area — a place where changes are added after you run git add . To check your staging area run git status

git history — a repository of all commits that have been made. To see history log you can run git log or git log --oneline for a shortened version.

--

--