In today’s post I will try to explain what restoring changes in the Git version control system is all about. Contrary to appearances, it is not as difficult as it may seem. This is a very useful skill that is really worth mastering, believe me, you will use it every day. Let’s get to the topic of the article!
Restoring changes in git
At any time there may be a need to modify the change, or to return to a still working version of the code. We can definitely compare such an operation to going back in time. However, you need to be very careful, because some operations can no longer be undone! This can cost us irrevocably lost work. Therefore, I encourage you to rethink ten times if you are sure you know what you are going to do.
We can use three different commands to undo the changes:
- git checkout – an operation that moves the HEAD pointer to the indicated commit, in peasant terms restores the project to the version from the given commit. However, you don’t need to use it literally on the entire project, you can restore changes to a single file.
- git revert – command that calls a new commit with the project version from the given commit. With this command, we can safely restore the changes we have managed to upload to the server. The safety comes from the fact that the history of changes in the repository is not modified.
- git reset – restores the changes in the repository to the indicated point in the change history. Note that using this command means modifying the change history of the local repository. When the commits are on the server this can cause a lot of complications.
Repository structure
The repository on which I will show the restoration of changes has three files written in Python named first.py, second.py and three.py, I made three commits, one for initializing the repo, the second with added lines to the first.py and third.py files, while the third is responsible for adding comments.

########### first.py
a = 1
print('one = {}'.format(a))
a = 5
print('changed value to {}'.format(a))
########### second.py
b = 2
print('two = {}'.format(b))
########### third.py
c = 3
print('three = {}'.format(c))
a = 10
print('create new variable equal to {}'.format(a))
git checkout
To restore changes we need to use a unique identifier, which we can read from the logs. For this we use the git log --oneline
command, it will return a shortened identifier that git will recognize anyway.
git log --oneline
fe25981 (HEAD -> master) added comments
6a53f06 added new lines
b014e90 first commit
To go back to a previous commit, let’s assume the first one, you need to use the git checkout
command and specify a shortened version of the ID as a parameter.

Congratulations, you managed to correctly revert to the first commit! See what state the files are in now (sequentially from the top first.py, second.py and third.py):
a = 1
print('one = {}'.format(a))
b = 2
print('two = {}'.format(b))
c = 3
print('three = {}'.format(c))
Note that we are in a detached HEAD
state. The HEAD pointer points to the commit we passed as an argument.

We can perform various experimental changes, but they will not be saved. In order not to lose the changes made, you need to create a new branch, git immediately tells you how to do this.
To understand why the hint says git checkout -b
command, you need to know the definition of git checkout
. Well, this command is not only responsible for restoring changes, if we specify the name of some branch as an argument instead of the commit ID, the command will trigger a change in the position of the HEAD pointer. From now on we will be on a different branch. On the other hand, git checkout -b
will simultaneously create a new branch and cause the current branch to change to the newly created one. We can check out existing branches using git branch.
I guess it would be better to see this in an example, so I will not create a new branch first, let’s see how git behaves in the case of detached HEAD
state.

The HEAD pointer does not point to any branch, but to a commit. In case I didn’t create any branch and switched to, say, master, I would lose the changes made. Therefore, I prevent this and create a new branch using git’s suggested command. The new branch will be called “from detached”.

Now the indicator correctly identifies the branch we are in, and we are sure that we will not lose the changes we have made. We can switch to the master, now I will demonstrate undoing changes in a single file.

I decided for the purpose of this section to use a transparent shell, so that you can see/see how the code will change after the command. We will use the previous command, however, we will now add the file name.
git checkout 6a53f06 third.py

The comment about which the commit “added comments” says was removed. The change of the added comment was correctly deleted, so the goal was achieved.
When we paste / add the lost code and add the file to the staging area we will see that there is no need to do the commit again, the system saw the file return to the state it was in.
git revert
Again, we are in the latest version of the project and will return to the first version. We will use the git revert
command, whose purpose will be to create a new commit and undo the changes made during the passed commit. We will again pass the shortened ID as an argument and will have to name the new version.
To show this I made a new commit, where I added some new lines of code to the second.py file. The first.py and third.py remain unchanged.
########### second.py
b = 2
print('two = {}'.format(b))
c = 25
print('new line in second file with var = {}'.format(c))
c = 25 / 5
c = c * 2
Now let’s go back to the second commit:

In the attached image, you can see that a modification of the second.py file has appeared in the logs. We restore the changes using the shortened identifier, which points to a commit named “added new lines”. After approving this command, it displays:

At the moment, a new commit is created to which we need to add a comment. I changed the name so that it is clear that the previous version of the project was restored. We save the change and approve it.

We learned a new method for restoring older versions of a project, the correct operation of which is shown in the following illustration:

As we can see the modification history has not changed, we added a new commit, which restored the state of the files from the set moment. We can now use other methods to undo changes.
git reset
We will start by deriving the states of git reset
operation. Well, this command has three possible states:
--mixed
– this is the default state, which resets all changes made up to the given commit and saves them in the working directory. That is, everything in the modification history above the ID given as an argument will appear in the working directory.--soft
– works literally the same as--mixed
, but differs in that the changes will not appear in the working directory, but in the staging area. This makes it easy for us to decide which changes to keep and which to lose.--hard
– changes will be completely deleted, irretrievably! Therefore, be careful when using this command.
It can be helpful to visualize the aforementioned arguments:

I will now present the mentioned three methods of restoring changes one by one. Let’s start with --hard
. We will roll back one commit.

Note that we have forever lost the revert performed earlier! Be careful during this operation!
Now we will use the --soft
command. Note that the changes actually appeared in the staging area.

I did not make a new commit, I deleted the changes I made. I executed two commands:
git reset HEAD second.py # I added file to staging area
git checkout -- second.py # I deleted changes
And at the very end we will undo the changes with the --mixed
command:

The changes made are in the files in the working directory, but they are not saved. I will then delete the modification and leave the files as they were during the second commit.

Contents of first.py file:
a = 1
print('one = {}'.format(a))
a = 5
print('changed value to {}'.format(a))
Zawartość pliku second.py:
b = 2
print('two = {}'.format(b))
Zawartość pliku third.py:
c = 3
print('three = {}'.format(c))
a = 10
print('create new variable equal to {}'.format(a))
Summary
Today we learned three methods of undoing changes in this amazing version control system that is git. I highly recommend you to practice the presented material yourself and try to master it, because you will use it practically every day at work.
If you would like to learn more about git, the I recommend the previous post!
References:
What’s the difference between git reset –mixed, –soft, and –hard?