Sometimes, you f*ck up. I f*ck up, you f*ck up, everyone f*cks up at some point, we all do it, it’s natural. F*ck, some people actually even make a living out of it and sadly, it is often the same people we look up to or those who run the country. But f*ck it. This section is still all about Git and Git is the magical land where f*ckups are undoable.
However, in most situations it is probably more preferable to just log the mistakes too and make new commits that fix them to reflect an accurate change history.
But no matter the weather, even in dire situations where there have been more commits in the meanwhile, on top of the ones we’re trying to undo, we have a few options:
git checkout can help restore peace and order in your working directory, we’ll have to talk about it later too when we reach branching, as it’s primary purpose is way greater. Here we can use it to replace files in the working area with the files stored in the repository (at the current HEAD pointer or any ref pointer really). But note that, when not using branches, it is good practice to include a double bare dash
-- to avoid confusion and any unforeseen consequences:
To restore a file in the working directory with the file located at the current HEAD in the repository:
git checkout -- <file>
To restore a file with the file located in the repository at a specific commit pointer:
git checkout 24f6daf6f -- <file>
Looks like we’re going to need to talk about the checksums anyway… Git generates a checksum for each change set, using the SHA-1 hash algorithm, which helps maintaining data integrity. It is the checksum that can be used to refer to a specific commit.
Typically, a SHA1 hash value is rendered as a 40 digits long hexadecimal number, but because of how unique they all are, it is considered safe to address them by only their first few characters (8 characters for instance is way safe).
This command is used to revert an entire commit. Well, in actuality it creates a new commit which mirrors the targeted one (i.e. everything that was added gets deleted, everything that was deleted gets added, thus all changes undone).
git revert 24f6daf6f
So, using the above command creates the new commit and opens your editor so you can customize the commit message.
Most commonly, you could find yourself using the reset command to do one of the following two things:
- Unstage files: say you’re trying to build up a nice commit, stating multiple files and then by accident, a file that’s not supposed to be there just slips in and your staging area is ruined…
git reset HEAD
There we go, that command had the sole purpose of unstaging that file.
- Undo multiple commits: using the following commands will actually move the HEAD pointer to a previous commit. We have three useful options for three different types of resets, as follows:
The soft reset is the safest reset there is, as it doesn’t change the staging area or the working directory:
git reset --soft 24f6daf6f
The mixed reset is the default option, it changes the staging area but not the working one:
git reset --mixed 24f6daf6f
The hard reset can be destructive as it changes files in all three areas – USE WITH CAUTION.
git reset --hard 24f6daf6f
Thing is, once the HEAD pointer moves back in time, all the other commits after it might seem lost, but they’re not – you can run the same command again, referencing any future commit you can think of (you can first output a git log to a plain text file to still have access to those checksums).
By now I assume you’re old enough to know that not every project is made of candy and each one of them is totally worth tracking and just as important as the next one. There will always be such things as small OS-specific temp files, cache files, logs, dumps and stuff that constantly changes just enough to make git flip out on every commit you make. In the next post, we’ll have a serious grown-up talk on How to: Ignore files in Git (Part IV).