
Git time machine
Recently, my colleagues are starting to get acquainted with git. And one of their questions is how to roll back to a certain revision. On the Internet you can find a set of commands, but I want to have an understanding of each of them. Pampering with git comas without understanding can lead to a loss of development history.
In this article I want to talk about the teams
So, let's start a short educational program on the time machine provided by git. First, let's illustrate the story:

Here, circles indicate commits. The more to the right the commit, the newer it is. A commit with a hash of 6e04e .. is the very first commit. One of the basic concepts that a novice needs to understand is pointers to commits, or rather a certain “nickname” of a particular commit. Their darkness is dark, for example: HEAD, master, FETCH_HEAD, ORIG_HEAD , etc. This I listed a grain of standard nicknames. You can create them yourself, but more on that ahead.
Let us focus on two pointers: master and HEAD . master points to the oldest commit in a branch called master (this branch is created when the repository is initialized). HEADpoints to the master pointer (read the current state of the files). After the first commit appears in the repository, HEAD and master point to the same commit. And so it will continue until we switch to another branch, roll back through history, or complete a series of rash actions. So, we illustrate our history with pointers:

The HEAD pointer in our case points to master , and master points to the d79fb commit ... It is important to understand that the current state of unchanged files under version control is the commit pointed to by HEAD . That is, if HEADwill indicate a commit with a hash of 6e04e .., then the files will be in their original state. To “move” the HEAD pointer, there is a command: git checkout. Those who are even a little familiar with git have recognized in this command switching to another branch. Everything is absolutely true - when switching to another branch, we simply transfer the HEAD pointer to the last commit of the branch.
Wrap pointer HEAD (
Rollback on the history of commits:

After the checkout operation is completed, we will be in the state in which there were two commits back. This is all wonderful - we took a small step into the past, spied something there, but how to go back? For example, I don’t have super-memory, and I don’t remember the hash of the very last commit (the one on the right is d79fb ..). If we write
Have we lost the whole story? How to find out the most “new” commit? This is not a problem - there is a way out, and there are several of them:

To clarify the mechanism,
* -b flag means that you need to create a branch with the specified name and immediately switch to it.
Let us illustrate our action:

Note that the HEAD pointer points to the top of the devel branch .
Let's spawn several commits in the new branch. The history of the repository will look like this:

Returning to the master branch is also painless:

So, remember the first paragraph:
Move pointer to the top of the branch (
In addition, git allows you to move not only HEAD , but alsocontinents pointers to the tops of branches. To do this, there is a command
In both cases, a "nickname" appears for the commit from which reset was made - ORIG_HEAD .


ORIG_HEAD useful to edit incorrect commit on the local machine (!). Suppose we want to combine the last two commits into one. To do this, keeping the current state of the files, translate the master pointer two commits back:
Let's look at the changes:
Well, now let's do the trick - combine the commits
Enter the message, save. Now our story looks like this:

An important note - ORIG_HEAD still points to the d79fb commit ... If we now run the git checkout ORIG_HEAD command , we will get the so-called detach HEAD state . It is characterized by the fact that HEAD does not point to the top of the branch, but simply to commit. HEAD should always point only to the top of a branch!

To "exit" the detach HEAD state , simply switch to a branch or create a new branch with the command
So, remember the second point:
And the most important thing! The most common operation of the above when working with git is switching between branches. All other cases considered are rare, but nevertheless it is necessary to understand everything that happens when they are used!
Have a good trip through the history of your repository!
In preparing the material, the following sources were used:
The best manual - book: ProGit
Visual help on git: A Visual Git Reference ( Russian version )
UPD:
In the comments, we advised another useful resource on git: githowto
PS Thank you for the invite and I wish everyone a pleasant weekend!
In this article I want to talk about the teams
git checkout
and git reset
with the keys --soft
and --hard
. So, let's start a short educational program on the time machine provided by git. First, let's illustrate the story:

Here, circles indicate commits. The more to the right the commit, the newer it is. A commit with a hash of 6e04e .. is the very first commit. One of the basic concepts that a novice needs to understand is pointers to commits, or rather a certain “nickname” of a particular commit. Their darkness is dark, for example: HEAD, master, FETCH_HEAD, ORIG_HEAD , etc. This I listed a grain of standard nicknames. You can create them yourself, but more on that ahead.
Let us focus on two pointers: master and HEAD . master points to the oldest commit in a branch called master (this branch is created when the repository is initialized). HEADpoints to the master pointer (read the current state of the files). After the first commit appears in the repository, HEAD and master point to the same commit. And so it will continue until we switch to another branch, roll back through history, or complete a series of rash actions. So, we illustrate our history with pointers:

The HEAD pointer in our case points to master , and master points to the d79fb commit ... It is important to understand that the current state of unchanged files under version control is the commit pointed to by HEAD . That is, if HEADwill indicate a commit with a hash of 6e04e .., then the files will be in their original state. To “move” the HEAD pointer, there is a command: git checkout. Those who are even a little familiar with git have recognized in this command switching to another branch. Everything is absolutely true - when switching to another branch, we simply transfer the HEAD pointer to the last commit of the branch.
Wrap pointer HEAD ( git checkout
)
Rollback on the history of commits:

After the checkout operation is completed, we will be in the state in which there were two commits back. This is all wonderful - we took a small step into the past, spied something there, but how to go back? For example, I don’t have super-memory, and I don’t remember the hash of the very last commit (the one on the right is d79fb ..). If we write
git log
, we will see a story consisting of three commits:[user@localhost project]$ git log --pretty=oneline
6741a69bd121c295413be95d7597cd7409e713a0 add unit test
b3e74f50c3cc48e6b335014b6dc7e301b382a903 add readme
6e04e39d0952a2d6022502d56aaa05d5a064bea Initial commit
Have we lost the whole story? How to find out the most “new” commit? This is not a problem - there is a way out, and there are several of them:
- Write a team
git log --all
. This command will print us the whole story, up to the present, i.e. in our case, a history of five commits:[user@localhost project]$ git log --pretty=oneline --all d79fb5688af71b4577f450919535e7177e9d74e8 fix bug 478927e3a088d3cec489ca8810eaaca97c6ce0ff documentation 6741a69bd121c295413be95d7597cd7409e713a0 add unit test b3e74f50c3cc48e6b335014b6dc7e301b382a903 add readme 6e04ee39d0952a2d6022502d56aaa05d5a064bea Initial commit
Next, copy the desired us to remain hash and re-run the time machine:git checkout
. But I do not recommend this method, since it requires too many actions. - Git allows you to track all changes to the HEAD pointer . This is possible by the team
git reflog
, but it is no longer for beginners and is not used for our goals. The most competent is to do the following: - Recall that the master pointer points to the most recent commit. Thus, the reset is performed with a single command:
git checkout master
. Voila la:

To clarify the mechanism,
git checkout
create a new devel branch:[user@localhost project]$ git checkout -b devel
* -b flag means that you need to create a branch with the specified name and immediately switch to it.
Let us illustrate our action:

Note that the HEAD pointer points to the top of the devel branch .
Let's spawn several commits in the new branch. The history of the repository will look like this:

Returning to the master branch is also painless:
[user@localhost project]$ git checkout master

So, remember the first paragraph:
- The git checkout room moves the HEAD pointer
Move pointer to the top of the branch ( git reset ...
)
In addition, git allows you to move not only HEAD , but also
git reset
with the keys either --soft
, or --hard
.- The key
--hard
means that we lose the current state of the files and acquire the state of the commit where reset was made. - The key
--soft
means that we DO NOT lose the current state of the project, but the pointer to the current branch has already been moved, i.e. git status will give us the difference between the current state of the project (from which we reset) and the one to which we reset.
In both cases, a "nickname" appears for the commit from which reset was made - ORIG_HEAD .
git reset --hard HEAD~2
: 
git reset --soft HEAD~2
: 
ORIG_HEAD useful to edit incorrect commit on the local machine (!). Suppose we want to combine the last two commits into one. To do this, keeping the current state of the files, translate the master pointer two commits back:
[user@localhost project]$ git reset --soft HEAD~2
Let's look at the changes:
[user@localhost project]$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# ЧТО-ТО ТАМ ДЛЯ КОММИТА
#
Well, now let's do the trick - combine the commits
[user@localhost project]$ git commit -c ORIG_HEAD
Enter the message, save. Now our story looks like this:

An important note - ORIG_HEAD still points to the d79fb commit ... If we now run the git checkout ORIG_HEAD command , we will get the so-called detach HEAD state . It is characterized by the fact that HEAD does not point to the top of the branch, but simply to commit. HEAD should always point only to the top of a branch!

To "exit" the detach HEAD state , simply switch to a branch or create a new branch with the command
git checkout -b new_branch_name
So, remember the second point:
git reset
with keys--soft
or--hard
moves the pointer to the top of the branch, and with it the pointer HEAD .
And the most important thing! The most common operation of the above when working with git is switching between branches. All other cases considered are rare, but nevertheless it is necessary to understand everything that happens when they are used!
Have a good trip through the history of your repository!
In preparing the material, the following sources were used:
The best manual - book: ProGit
Visual help on git: A Visual Git Reference ( Russian version )
UPD:
In the comments, we advised another useful resource on git: githowto
PS Thank you for the invite and I wish everyone a pleasant weekend!