When you are creating your timeline/branch
i.e setting up the history of your project. You gain an important ability to go back in time.
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 Undoing 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.
What is HEAD?
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
We can now see a summary of the history of our commits. If you notice the first commit, there is a (HEAD -> master) 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.
To prove that HEAD points to the last commit by default, we can run the following command:
git show HEAD
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.
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
We wrote 1 but HEAD is pointing to the second commit.
The commits in history are zero-indexed, meaning you start counting from 0. Remember that!
That's enough about HEAD, we will be using it throughout this journey.
Time to Undo Things
I actually mentioned the three basic commands we will be discussing that allow us to undo things. These commands are:
We will start with checkout, with the help of this command we are able to unmodify files, also we can go back in time and check different states of our project. It also allows us to move between different branches.
unmodify is not a word, but for some reason I feel comfortable with it here 🤔.
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.
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:
This command is followed by the file you want to unmodify. For example:
git checkout script.js
As a way to unmodify a bunch of files at the same time we can use the following command:
git checkout .
git checkout *
Another job that
git checkout can do is go back in time 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 back in time we need to specify the commit/snapshot we want to go back to, we have learned we can specify a commit using the Unique Identifier (ID) or with the HEAD pointer. We perform the jump by running the command:
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
You have now gone back in time 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 back in time. 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
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.
Checkout is really safe because it is read-only, you cannot change or edit previous commits
Lets prove it by going back in time again.
We now know how we can go back in time 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.
When we run the
git log --oneline we can see the commit we just made, but if we go 😏 back to the future / master branch and run the same command again. We don't see the commit at all!
If we go back in time 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.
git revert command is used to undo the changes to your projects commit history. How it works is like this: It takes a specific commit, it inverses that commit, then creates a new revert commit. We do this by running the command:
This command is followed by the ID of the commit you want to revert, For example:
git revert 736ec6b
A new temporary file is opened called COMMIT_EDITMSG, it contains the original commit message and a info message to let you know that this reverts commit with the specified ID. 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.
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 command is a little unique in that it has three different flags you can use with it and each flag behaves in different ways.
These flags are:
git reset --soft 5d5751c
This command is followed by the unique identifier of the previous commit, meaning the ID of the commit BEFORE the commits you want to delete.
When you perform a
git resetyou choose the commit you want to start your deletion at, and only start deleting all the commits that come after.
The mixed flag is a default flag, 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
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.
To completely remove files after running the
git reset --mixed command, we need to run the command:
git checkout .
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.
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
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
--soft, we go through a couple of steps in order to make the changes to the files.
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!
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.
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.
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.
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.
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:
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:
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