Git and GitHub: Git Revert
Tags: git, github, beginners
Published: 2020-06-29 03:09:04

###Song of the Week
{% soundcloud https://soundcloud.com/user-862296609/crystal-ship %}

##Introduction
Last week we covered how to do a hard reset of your repository. Though they're sometimes necessary, hard resets are a pretty destructive method of rewinding your repository because they alter the commit history.

Today's post will cover the nondestructive rewind, aka `git revert`. 
We'll go over:
* What `git revert` does and how it is different from `git reset`.
* How to revert a single commit.
* How to revert a range of commits.

##Git Revert vs Git Reset
The `git revert <commit-hash>` command will reverse the changes made by the commit hash given, and then create a new commit. This is the preferred method for undoing the changes of a specific commit if that commit has already been push to a shared remote repository because it preserves the commit history. 

This is different from a `git reset`, because a reset will move the HEAD ref to the commit hash given, and drop all the commits after that one. A `git revert` only reverses the commit that was given and keeps all the following commits and the changes that were made in them. This makes `git revert` great for removing a bug that was introduced by a single commit without having to redo all the work in the following commits.

##How to Revert a Single Commit
To revert a single commit, enter the following git command in the terminal:

`git revert <commit-hash>`

You can also pass `git revert` a reference to the commit that is relative the HEAD ref. So, if you entered `git revert HEAD~2`, the changes from the third most recent commit would be reversed.

Let's look at an example. I created a local repository with some files whose names match the commit in which they were created, which looks like this:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bwquj9hfk21alxb1er67.png)
All these files just have the text "added content" in them and each was added with their own commit. The `git log` looks like this:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nl3q98niv64b1es5ybk1.png)
Now let's say there was some bug in commit A that we wanted to undo but keep commits B-D. We would enter the following git command:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/bnpb60rqmtfaxn4r3bb2.png)
You can see in the `git log` that the `git revert` command automatically created a commit. Also, I used `HEAD~3` to reference commit A. Remember that most recent commit is equal to `HEAD`, which can be written as `HEAD~0`. So, the fourth most recent commit is `HEAD~3`.

Now if we look at the working directory you'll see that the "commit-A" has been removed, but the rest remain.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/q2wwiqen9doqmj3zsczr.png)

##How to Revert a Range of Commits
To revert a range of commits, simply enter the following git command in the terminal:

`git revert -n <commit-hash>^..<commit-hash>`

Notice the `^` before the two periods. That caret symbol tells git to include that first commit hash given in the range. And the `-n` option tells git to not create a commit, but to instead stage the changes in the Staging Index and the Working Directory. This way you can do one commit manually to maintain a neat commit history.

In the same example we used above, lets say we want to rewind to commit A and maintain the commit history. You could do that like this:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/komuf7483lrgn2rqdo1y.png)
By running `git status`, we can see that all files added in the commits B-D have been deleted.

Now we just need to commit those changes.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/muhsx0itw5du2rpg9xe7.png)
In the `git log` we see that commits B-D are still in the commit history even though their changes have been reversed. 

If we open the Working Directory, we'll see that it has indeed rewound back to commit A.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nu1o91g80gilowphd9nv.png)

##Takeaways
* `git revert <commit-hash>` will reverse the changes made by the commit hash given, and then create a new commit.
* Unlike `git reset`, `git revert <commit-hash>` will not remove the commits after the commit given, thereby preserving the commit history.
* `git revert` will also accept a commit reference based on HEAD, like `git reset HEAD~3`, for example.
* `git revert -n <commit-hash>^..<commit-hash>` will reverse the changes by the range of commits given(inclusively), and will stash the changes in the Staging Index. The `-n` option prevents the auto commits, so you will need to do a manual commit.

##References
[Cover Image - Mt St Helens](http://seattlerefined.com/travel/5-ways-to-get-off-the-grid-at-mount-st-helens)
[Git Revert - atlassian.com](https://www.atlassian.com/git/tutorials/undoing-changes/git-revert#:~:text=The%20git%20revert%20command%20is%20a%20forward%2Dmoving%20undo%20operation,in%20regards%20to%20losing%20work.)
[Git Revert - Git Docs](https://git-scm.com/docs/git-revert)
[How to revert multiple git commits? - stackoverflow.com](https://stackoverflow.com/questions/1463340/how-to-revert-multiple-git-commits)