Git Essentials(Second Edition)
上QQ阅读APP看书,第一时间看更新

HEAD, or you are here

During previous exercises we continued to see that HEAD thing while using git log, and now it's time to investigate a little bit.

First of all, what is HEAD? As branches are, HEAD is a reference. It represents a pointer to the place on where we are right now, nothing more, nothing less. In practice instead, it is just another plain text file:

[13] ~/grocery (berries)
$ cat .git/HEAD
ref: refs/heads/berries

The difference between the HEAD file and branches text file is that the HEAD file usually refers to a branch, and not directly to a commit as branches do. The ref: part is the convention Git uses internally to declare a pointer to another branch, while refs/heads/berries is of course the relative path to the berries branch text file.

So, having checked out the berries branch, in fact we moved that pointer from the master branch to the berries one; from now on, every commit we do will be part of the berries branch. Let's give it a try.

Add a blackberry to the shopping list:

[14] ~/grocery (berries)
$ echo "blackberry" >> shoppingList.txt

Then perform a commit:

[15] ~/grocery (berries)
$ git commit -am "Add a blackberry"
[berries ef6c382] Add a blackberry
 1 file changed, 1 insertion(+)

Take a look on what happened with the usual git log command:

[16] ~/grocery (berries)
$ git log --oneline --graph --decorate
* ef6c382 (HEAD -> berries) Add a blackberry
* 0e8b5cf (master) Add an orange
* e4a5e7b Add an apple
* a57d783 Add a banana to the shopping list

Nice! Something happened here:

  • The berries branch moved to the last commit we performed, confirming what we said before: a branch is just a label that follows you while doing new commits, getting stuck to the last one
  • The HEAD pointer moved too, following the branch it is actually pointing to, the berries one
  • The master branch remains where it was, stuck to the penultimate commit, the last one we did before switching to the berries branch

Okay, so now our shoppingList.txt file appears to contain these text lines:

[17] ~/grocery (berries)
$ cat shoppingList.txt
banana
apple
orange
blackberry

What happens if we move back to the master branch? Let's see.

Check out the master branch:

[18] ~/grocery (berries)
$ git checkout master
Switched to branch 'master'

Look at the shoppingFile.txt content:

[19] ~/grocery (master)
$ cat shoppingList.txt
banana
apple
orange

We actually moved back to where we were before adding the blackberry; as it is being added in the berries branch, here in the master branch it does not exist: sounds good, doesn't it?

Even the HEAD file has been updated accordingly:

[20] ~/grocery (master)
$ cat .git/HEAD
ref: refs/heads/master

But at this point someone could raise their hand and say: "That's weird! In Subversion, we usually have different folders for each different branch; here Git seems to always overwrite the content of the same folder, isn't it?".

Of course, it is. This is how Git works. When you switch a branch, Git goes to the commit the branch is pointing to, and following the parent relationship and analyzing trees and blobs, rebuilds the content on the working directory accordingly, getting hold of that files and folders (that is the same Subversion can do with the switch branch feature, actually).

This is a big difference between Git and Subversion (and other similar versioning systems); people used to Subversion often argue that in this manner you cannot easily compare branches file by file, or open in your favorite IDE two different versions of your in-development software. Yes, this is true, in Git you cannot do the same, but there are some tricks to work around this issue (if it is an issue for you).

Another important thing to say is that in Git you cannot check out only a folder of the repository, as you can do in Subversion; when you check out a branch, you get all its content.

Go back to the repository now; let's do the usual git log:

[21] ~/grocery (master)
$ git log --oneline --graph --decorate
* 0e8b5cf (HEAD -> master) Add an orange
* e4a5e7b Add an apple
* a57d783 Add a banana to the shopping list

Uh-oh: where is the berries branch? Don't worry: git log usually displays only the branch you are on, and the commit that belongs to it. To see all branches, you only need to add the --all option:

[22] ~/grocery (master)
$ git log --oneline --graph --decorate --all
* ef6c382 (berries) Add a blackberry
* 0e8b5cf (HEAD -> master) Add an orange
* e4a5e7b Add an apple
* a57d783 Add a banana to the shopping list

Okay, let's see: we are on the master branch, as the shell prompts and as HEAD remembers us, with that arrow that points to master; then there is a berries branch, with a commit more than master.