Git and Github: The Complete Guides - Chapter 2: Undo Things in Git
Tags: github, git, webdev, beginners
Published: 2021-09-04 07:09:10

![goran-greets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s7an2i0uf70z8df374ic.png)

When you are creating your **<u>timeline/branch</u>** ``i.e`` setting up the history of your project. You gain an important ability to go <u>back in time</u>. 

![reversetime](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8s75pywemzcrs0297mwt.gif)

By going back, you are able to **checkout** the previous states your project was in, you are able to **revert** the changes of those states and even **reset** the course of history!

The common theme is *<u>Undoing</u>* and we will discuss this in detail later on, For now I want to discuss a topic we briefly touched on in the first chapter. That is **HEAD**.

<h3>What is HEAD?</h3>

To have a clear understanding of **HEAD** let us see an example of **HEAD** again. When we log the history of our project using the summary syntax with the command:  
``` 
git log --oneline 
```

![git-history-summary](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajw9iwrpzwtm3czybkh5.gif)

We can now see a summary of the history of our commits. If you notice the first commit, there is a <u>(HEAD -> master)</u> associated with it. **HEAD** is a pointer, **master** is the default branch. The branch itself is a timeline of the commits. By default **HEAD** points to the last commit, but if you go back in time to other previous states of the project, then **HEAD** will point to the relevant commit. ``More on this soon...``

To prove that **HEAD** points to the last commit by default, we can run the following command: ```
git show HEAD
```
![git-show-head](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p6ni5rfvxp423pjxhuqk.gif)


The output from this command shows us the last commit with a bunch of information. The bottom half of which will be discussed in other chapters...

Notice that **HEAD** acted as a unique identifier (ID) of a commit. Meaning if we pass in the weird string of characters of the last commit along with ``git show`` we will get the same output.

![git-show-id](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aso8k3b7en4206v90igl.gif)

We can use **HEAD** instead of an ID. It's interesting to see how we can show other commits using **HEAD**. To demonstrate let us clear the screen. Run the command to log a summary of the history of our commits to us. To use **HEAD** to log details of other commits use the following command: ```
git show HEAD~
```
The tilde sign is followed by a number representing the index of the commit. For example: ```
git show HEAD~1
```
![git-show-head-$](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ax29n4ait7y4eej1vle.gif)

We wrote 1 but **HEAD** is pointing to the second commit. 

>The commits in history are zero-indexed, meaning you start counting from 0. <u>Remember that!</u>

That's enough about **HEAD**, we will be using it throughout this journey.

<hr/>

<h3>Time to Undo Things</h3>

I actually mentioned the three basic commands we will be discussing that allow us to **undo** things. These commands are:

<ul>
<li>checkout</li>
<li>revert</li>
<li>reset</li>
</ul>

<h3>Checkout</h3>

We will start with **checkout**, with the help of this command we are able to unmodify files, also we can go <u>back in time</u> and check different states of our project. It also allows us to move between different branches.

> <u>unmodify</u> is not a word, but for some reason I feel comfortable with it here 🤔.

<h4>Unmodify</h4>

Lets first see how we can unmodify files. To do this we have to first modify some of our files in our project, This is completely up to you just remember to save the changes you make in your file.

When we check the status of our files, we see that our files have been modified.

![git-status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zih8qmsbse1gyfahsi9h.gif)

Lets say for some reason we accidentally introduced an error, and we need to unmodify the file. For example we created a variable we shouldn't have. We can use the following command to unmodify the file: ```
git checkout 
```
This command is followed by the file you want to unmodify. For example: ```
git checkout script.js
```

![git-checkout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ytts9ah0u2elv4u1711l.gif)

As a way to unmodify a bunch of files at the same time we can use the following command: ```
git checkout .
```
or
```
git checkout *
```

Another job that ``git checkout`` can do is go <u>back in time</u> and check the previous states of the project. Unlike the other two commands we will also be discussing, **checkout** is the safest one. Because with **checkout** we cannot change or delete previous commits.

To go <u>back in time</u> we need to specify the **commit/snapshot** we want to go back to, we have learned we can specify a commit using the <u>Unique Identifier</u> (ID) or with the *HEAD* pointer. We perform the jump by running the command: ```
git checkout
```
This command is followed by the ID of the commit you want to go back to. For example: ``` 
git checkout 90077b6 
```
You can also use the *HEAD* pointer like this: ``` 
git checkout HEAD~2 
```

![git-time-jump](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ke0d3h3mrndar5uxhkht.gif)

You have now gone <u>back in time</u> to the previous state of your project. Something you will notice is how any files that you may have created after this commit have been deleted, also any changes to any files will go back to the state it was in when you made this commit.

Awesome right! 👍 

We went <u>back in time</u>. But you might be thinking, did that really just delete all the work from before?
Especially if you run the command to see the history of your commits: ``` 
git log --oneline 
```

![git-oneline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j7f6nqtrzxfrnlupd5bz.gif)

![noooo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ryslkg4pjllucfzhgbu5.gif)

If you look at the output of our ``git checkout ___`` command, you will notice it says we are in a **detached HEAD** state. This means that our *HEAD* is no longer pointing to the last commit. Instead *HEAD* is now pointing to the **ID** of the commit we are currently in. That's how we are able to go back to the previous state of our projects.

To go **back to the future** 😏  we just need to run the following command: ```
git checkout master
```
Now we are back! if we check to make sure all our commits are still intact, we will be delighted to see they all still there, more importantly *HEAD* is pointing at the last commit.

![git-checkout-master](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ltlbtivw75copw37afic.gif)

>**Checkout** is really safe because it is <u>read-only</u>, you cannot change or edit previous commits

Lets prove it by going <u>back in time</u> again.

We now know how we can go <u>back in time</u> to a previous commit. `` git checkout `` followed by the ID of the commit. Once we are in the previous state, we can go through the normal procedure of modifying our files, setting them to the staging area and finally running the commit with a message of-course. 

![git-checkout-readonly](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/87oishllgfo2knzqz34d.gif)

When we run the ``git log --oneline`` we can see the commit we just made, but if we go 😏 **back to the future** / <u>master branch</u> and run the same command again. We don't see the commit at all!

If we go <u>back in time</u> to the same commit as before and run the command, we don't see the commit there either. This is what we mean when we say **checkout is safe**.

To make a new commit in the case of **checkout** you can do this by creating other branches. But lets keep that for later...

We are going to go over the other commands that allow you to make permanent changes to the timeline of your project. **revert** and **reset**.

<h3>Revert</h3>

The ``git revert`` command is used to undo the changes to your projects commit history. How it works is like this: <u>It takes a specific commit</u>, <u>it inverses that commit</u>, <u>then creates a new revert commit</u>. We do this by running the command: ```
git revert
```
This command is followed by the ID of the commit you want to revert, For example: ```
git revert 736ec6b 
```

![git-revert](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fckovafcaxw5ngwmgmdw.gif)

A new temporary file is opened called **COMMIT_EDITMSG**, it contains the original commit message and a info message to let you know that <u>this reverts commit with the specified ID</u>. When you close the file, whatever changes you made in the specified commit will be undone and a new commit will be added to the timeline.

>If you remove the original message and leave it empty and close the temporary file, it will abort the revert process.

One other thing to take note of is, when you use the ``git revert`` command, you don't touch any of the changes of other commits, you only make the change to the specified commit.

This is useful because maybe you have some mistakes in your previous specified commits and ``git revert`` allows you to fix those mistakes without changing any other commit.

<h3>Reset</h3>

This is known as the **dangerous** or **unsafe** command because it can permanently delete your work. So needless to say you must be careful with ``git reset``

The ``git reset`` command is a little unique in that it has three different <u>flags</u> you can use with it and each flag behaves in different ways. 

These flags are:

<ul>
<li>mixed</li>
<li>soft</li>
<li>hard</li>
</ul>

For example: ```
git reset --soft 5d5751c
```
This command is followed by the unique identifier of the previous commit, **meaning the ID of the commit <u>BEFORE</u> the commits you want to delete.**

>When you perform a ``git reset`` you choose the commit you want to start your deletion at, and only start deleting all the commits that come after.

<h4>Mixed</h4>

The **mixed flag** is a <u>default flag</u>, meaning if we run just ``git reset`` then the **mixed flag** will work anyway.

Go ahead and make a couple of commits, by now you should be familiar with the whole process...

When we run the command: ```
git reset --mixed
```

![git-reset](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fivoat987mdn7e9lg078.gif)

The commits seem to be removed from the history of commits but they don't seem to have been physically removed. This is because we only removed them from the staging area. We can check this by running the ``git status`` command.

![git-status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p8d9vyk8l0kxjz23uz0f.gif)

To completely remove files after running the ``git reset --mixed `` command, we need to run the command: ```
git checkout .
```

![git-checkout.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tdjq7yggjdpct8lr5b2w.gif)

As you see the ``git reset `` command with the ``--mixed`` flag only deletes the commits from the history and also unstages the files from the staging area, they end up in the working directory.

<h4>Soft</h4>

There is a small difference between the ``--soft`` flag and the ``--mixed`` flag. Just like the latter, the ``--soft`` flag is followed by the unique ID of the commit before the actual commits you want to change. Also like ``--mixed`` when you run the command it will remove the commits from the history. But a key difference is the files are maintained at the staging area. We can check this by running ``git status``

![git-status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8uqb83g71s8ecogfgijb.gif)

As you see both files are green, meaning they are still in the staging area. To remove them from the staging area we have to run the command: ```
git reset .
```
This will remove them from the staging area and finally to delete the files permanently. We run the command: ```
git checkout .
```

In both cases ``--mixed`` and ``--soft``, we go through a couple of steps in order to make the changes to the files.

<h4>Hard</h4>

If you want to remove the commits in history and delete the files simultaneously, you can simply use ``git reset`` with the ``--hard`` flag. We can run the command as follows:
```
git reset --hard
```
>This is followed by the commit (ID) **BEFORE** the commits you want to delete. Remember this will permanently delete your files!

![git-reset-hard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6m1r0zftdolppb89y18.gif)

<h3>Git Ignore</h3>
We covered a bunch so far, how we can track files, how to add them to the staging area, how to make commits and also how to undo things. This seems like the right time to discuss how we can ignore files or folders from tracking, meaning the files and folders that should not be recognised by Git.

When you are working on projects, you might have some files that you actually don't need to keep track of because it might slow down your workflow. Here we will introduce a solution to ignore those particular files.

In Git we call this the **.gitignore file**, really it doesn't have a name, you just need to specify its extension which is **.gitignore**

First we need to create a new file and call it ``.gitignore`` then we can check the status and see that it is not being tracked. To see how it works, lets create two text files inside the same directory as the .gitignore file. Once we create them we can check the status and see that we now have three files that are untracked. You can also notice this by there green color and the **U** next to the files.

![git-ignore](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4a0lvp1smig8i2pioibx.gif)

To let Git know we don't want files/folders to be tracked, we need to insert them into the ``.gitignore`` file. We insert them by specifying the path of the files relative to the path of the .gitignore file.

When we added the files correctly inside the ``.gitignore`` file and save. We will no longer have a green color to the files or have the **U** next to them. Immediately they will be ignored by Git.

![git-status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3wp1qq0npoacdwz6x7vm.gif)

I want to discuss something that I think you need to be aware of when working with **.gitignore**, Suppose we wanted Git to ignore our index.html and style.css file. We would add them to the ``.gitignore`` file and save. You would think that they would be ignored by Git, but if we modify those files again and check the status, we see Git has still been tracking our files.

![git-ignore-1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kxx4w305glqxs7d75oyl.gif)

The reason for this behaviour is because those two files have already been modified before we even created the ``.gitignore`` file. In order to fix this, we have to delete the cache from the history. We do that by running the following command: ```
git rm -r cached .
```
When we run this command we cleaned the cache.

![git-ignore](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aoqwf9liy38r9233zhqh.gif)

Here are some extra tips for working with the ``.gitignore`` file. As you may have noticed every time we want to have a file ignored by Git, we have to insert the path of that file inside the ``.gitignore`` file. Imagine we had hundreds of files that we needed Git to ignore, having to insert them one-by-one would be ridiculously slow. Therefore we get to use a special character for specifying all files. This character is the **asterisks** *

For instance we have a folder named newFolder and it contains hundreds of files we want to ignore, to select all of them we use the asterisk:
```
newFolder/*
```

But say for instance, the same folder had some images or style.css files inside that we needed for our project, the only files we want to ignore in this folder is the text files. If we use the command from before, Git would ignore all files in the folder. To fix this we can specify the extension that Git should ignore by using the asterisk along with the extension:```
newFolder/*.txt
```

That concludes this chapter, I think we've covered **Git** so it's time to explore **GitHub** next. I hope you found it enjoyable, if you did leave a heart or you can just ``.gitignore`` it.

![serena](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9m9pfjcidqqbnlr6ce3.gif)