One of the greatest aspects about Git is that you can undo almost anything. In the end, this means that you actually can't mess up: Git always provides a safety net for you.
Fixing the Last Commit
No matter how carefully you craft your commits, sooner or later you'll forget to add a change or mistype the commit's message. That's when the "--amend" flag of the "git commit" command comes in handy: it allows you to change the very last commit really easily.
If you just want to correct the commit message, you simply "commit again" - without any staged changes but with the correct message:
$ git commit --amend -m "This is the correct message"
In case you want to add some more changes to that last commit, you can simply stage them as normal and then commit again:
$ git add some/changed/file.ext $ git commit --amend -m "commit message"
The Golden Rules of Version Control
No. 5: Never Amend Published Commits
Using the "amend" option is a great little helper that you'll come to appreciate yourself very quickly. However, you'll need to keep the following things in mind when using it:
- (a) It can only be used to fix the very last commit. Older commits can't be modified with "amend".
- (b) You should never "amend" a commit that has already been published / pushed to a remote repository! This is because "amend" effectively produces a completely new commit object in the background that replaces the old one. If you're the only person who had this commit, doing this is safe. However, after publishing the original commit on a remote, other people might already have based new work on this commit. Replacing it with an amended version will cause problems.
Undoing Local Changes
Changes are called "local" when they haven't been committed, yet: all the modifications that are currently present in your working directory are "local", uncommitted changes.
Sometimes, you'll produce code that... well... is worse than what you had before. These are the times when you want to discard these changes and start fresh with the last committed version.
To restore a file to its last committed version, you use the "git checkout" command:
$ git checkout HEAD file/to/restore.ext
You already know that the "checkout" command is mainly used to switch branches. However, if you use it with the HEAD reference and the path to a file, it will discard any uncommitted changes in that file.
If you need to discard all current changes in your working copy and want to restore the last committed version of your complete project, the "git reset" command is your friend:
$ git reset --hard HEAD
This tells Git to replace the files in your working copy with the "HEAD" revision (which is the last committed version), discarding all local changes.
Discarding uncommitted changes cannot be undone. This is because they have never been saved in your repository. Therefore, Git has no chance to restore this kind of changes.
Always keep this in mind when discarding local changes.
Undoing Committed Changes
Sometimes you'll want to undo a certain commit. E.g. when you notice that your changes were wrong, when you introduced a bug, or simply when the customer has decided he doesn't want this anymore.
Using the "git revert" command is one possibility to undo a previous commit. However, the command doesn't delete any commits. Instead, it reverts the effects of a certain commit, effectively undoing it. It does this by producing a new commit with changes that revert each of the changes in that unwanted commit. For example, if your original commit added a word in a certain place, the reverting commit will remove exactly this word, again.
Simply provide the hash of the commit you want to revert:
$ git revert 2b504be [master 364d412] Revert "Change headlines for about and imprint" 2 files changed, 2 insertions(+), 2 deletions (-) $ git log commit 364d412a25ddce997ce76230598aaa7b9759f434 Author: Tobias Günther <email@example.com> Date: Tue Aug 6 10:23:57 2013 +0200 Revert "Change headlines for about and imprint" This reverts commit 2b504bee4083a20e0ef1e037eea0bd913a4d56b6.
Another tool to "undo" commits is the "git reset" command. It neither produces any new commits nor does it delete any old ones. It works by resetting your current HEAD branch to an older revision (also called "rolling back" to that older revision):
$ git reset --hard 2be18d9
After this command, your currently checked out branch will be at revision 2be18d9. The commits that came after this one are effectively undone and are no longer visible in the history of this branch.
Be careful, however: calling the command with the "--hard" option will discard all local changes that you might currently have. The project is completely restored as it was in that past revision.
If you call it with "--keep" instead of "--hard", all changes from rolled back revisions will be preserved as local changes in your working directory.
Just like "revert", the "reset" command also doesn't delete any commits. It just makes it look as if they hadn't existed and removes them from the history. However, they are still stored in Git's database for at least 30 days. So if you should ever notice you accidentally removed commits you still need, one of your Git expert colleagues will still be able to restore them for you.
Both commands, revert and reset, only affect your current HEAD branch. Therefore, you should make sure you have checked out the correct branch before starting to play with them.