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.