Most of us avoid dreaded git reset command — but actually it can be really useful, as long as you know how it works!
What does git reset do?
To put it simply, git reset will take your branch back to a certain point in the commit history, but there are 3 different levels to this:
- soft: Moves the head back to the commit specified but leaves all the updated files in the index and working directory —all of the files after the commit that you’ve moved to have been tracked (like doing git add) and are ready to be committed as a new commit.
- mixed: Moves the head and index back to the commit specified but leaves the files in the working directory — all of the files after the commit are in your working directory as untracked files. If you add them all now, you’ll be at the same stage as a soft reset above.
- hard: Moves the head, index and working directory back to the commit specified — all of the updated files after the commit specified are now *GONE*!!! (uncommitted files are unrecoverable at this point)
Why would you need this?
Here are a few examples so that you will have a better idea of when to use each. You should only ever do this on your own branch, or when you're sure no one has already pulled any of the commits that you will be removing.
Soft
Let’s say you have done a few tiny commits and you want them to all be put into 1 more descriptive commit.
A -> B -> C -> D
git reset — soft A
Your branch head is now pointing at A
git commit -m “my new merged commit”
git push origin branch --force-with-lease
We now have a new commit, E but it contains all of the files that were committed in B, C, D. Note: the force-with-lease flag is because otherwise git will complain that your local branch is behind the remote. (This is a safer version of force)
A->E
Mixed
You’ve just pushed a few commits, but you want to go back and remove a couple of files in a previous commit.
A->B->C
git reset --mixed A
Your branch head and index is pointing at A
git status
This will show that all of your changes in B and C are there, but are untracked
Now you are free to add the files that you wish to add into a new commit
git add <files> git commit -m "updated commit"
git push origin branch --force-with-lease
A->D
Your head is now at the new commit D, and any files that you’ve not staged will still be in your working tree, ready to add into another commit or to do what you want with.
Hard
When would you possibly want this? Usually when things have gone wrong because it’s VERY RISKY. I’ll talk you through a scenario where I’ve needed it.
I’ve been working on my own branch, testing out a feature and I’ve changed many files, but it’s been a fail and I want to go back a few commits and get rid of every change I’ve made since.
If I’ve not made any commits or pushed any commits. I can just reset back to commit A and my working directory is now clear, all of my updated files are gone from history.
git reset --hard A
If I’ve actually pushed these commits to my remote branch, then, before doing this, you need to make sure that no one is working from those commits because they will be orphaned. But if you know that it’s safe to do, then run the command above. The only difference here is that you will need to do a FORCED push afterwards to push the remote branch to that state, otherwise it will tell you that your local branch is behind.
git reset --hard A
git push origin branchname --force-with-lease
This will delete your commits from the remote branch history, so you can see it could be very dangerous.
I have used this to get me out of reverted merge commit hell recently. I did a pull request to merge a branch into another branch, it had merge conflicts and the conflicts were not resolved correctly, so I reverted the merge. This however meant I couldn’t do the pull request again because it saw no updates, so I tried to revert that and it ended up in a bit of a mess. I then used git reset --hard to take the branches back to before this situation and get rid of these ugly reverts in the history!
Top comments (14)
My best use case for git reset is when I want to update my pull request, merge master and clean up the git history
git fetch
andgit merge origin/master
git reset --soft origin/master
same exact code in the repository but clean git historygit push --force
Pull Requests: a simple workflow
Jean-Michel Fayard 🇫🇷🇩🇪🇬🇧🇪🇸🇨🇴 ・ Dec 4 '17 ・ 2 min read
Interesting use case. So you're merging all of your pull requests commits into one commit. I can see that this makes the history cleaner. I personally like to keep that commit history in projects, especially for a big merge. It makes it easier to step back to a more specific point in the future after the merge. I can definitely see the benefits though :)
On GitHub you can do the same thing with merge and squash the commits.
The history in master is kept clean but you have a link to the pull request to see the details
Yeah it definitely makes sense, especially if you do lots of tiny commits then you would clutter the main branch :) I guess there is a balancing act though as to leaving enough commits to be able to cherry pick certain functionality in future (rather than looking at a pull request and manually doing things)
Thanks for this article @Charlotte, I want to underline that for your soft example,
git rebase
is also a very good choice.Thank you :) rebasing definitely is an option but reset works differently. Reset is much simpler if you just want to go to an earlier commit and forget about the commits after it.
Any other use for soft? Because for that use case I use rebase.
I guess resetting should be thought of more as when you want to totally undo your commits after a certain point and re-write them into a new commit message. With rebase you'd still have all the commits after that point there and you choose what you want to do with them (pick, squash, etc). So if you do not care about those commits, reset it the easiest option.
Ah yeah, makes sense so it is for situations where you want no witnesses of your crimes. :)
I've been using git for years, and I didn't know how git reset worked, maybe even forgot that it existed. I've cleaned up many a "git mess" though. It's always been, um "messy"!
I have had many instances where a knowledge of git reset would have been very useful.
Thanks for sharing!
And there's also good old plain
git reset
which only unstages the staged changes.The default is mixed :)
Good article, learned a bit more about GIT. Especially from the conversations.
Thanks for this! I have very rarely delved into using reset!