OK, so you understand what commits are and how they relate to other commits with the parent hash IDs. Now you might be thinking that it sounds remarkably similar to svn changesets, and you’d be right up until this point. Now what if I told you that each commit can have multiple parents and descendants? If you consider the case where the second commit from our previous example has two descendants then you have the beginnings of branches! In other words we can have two commits that have the same parent which effectively forks the history:
As you can see from this example we have two different people making commits descending from our original second commit. Now we have created a fork in the history we can now start adding more commits on top of the ends of the fork like so:
To make the branching concept complete we now need to look at how git exposes branches to you. This is an area that is very different to svn. Subverison would effectively create a whole copy of the repo as a branch. When you check it out it would appear as a whole separate directory on your file systems so your directory structure might be something like this:
cool-project |--branches |--trunk |-- code files... |--cool-mods |-- code files... |--boring-bug-fixes |-- code files...
To switch branches you physically work in a different directory.
Git keeps tracks of branches simply by using a pointer. So a branch is really nothing more than a pointer to a commit. Think of it like a post-it note with the name of the branch on it. So our previous example might look like this now:
We have two pointers, one for each branch. This is all that is required to create a branch in git, you just create a new pointer and you can start your commits going off in a fork from the main branch. To do this you would use the command
git branch <branchname>. So to create the branch in our example you would run
git branch bugfix1. Once you have created a new branch you need to check it out. This is where branching in git gets fun! To checkout the new branch you would run
git checkout <branchname>. Easy. What this does is alter your working directory to reflect the state of the repository as determined by the commit that the branch is pointing at.
Let’s examine what that means in reality. If I had master checked out then I should see text.txt with 4 lines in it:
repo |--test.txt <-- 4 lines up to "I'm a mactard"
Now if I run
git checkout bugfix1 then what will happen? Well bugfix1 is pointing at commit 987tsr. If you track the history back through the commits then you see that this branch is identical to master up to the point where we had 2 lines in test.txt, at which point The Rathboner started adding some git fanboy messages to a new file. So what git does is actually change the files in your working directory to match the contents of the files as committed by The Rathboner in commit 987tsr. It does not create a subdirectory for the branch and move you into it. It does not use any sort of symlinking or naming conventions. It does not have to download anything at all from any servers. It literally takes the test.txt file on your drive, deletes the last 2 lines from it and creates the bone.txt file. This makes branching soooo easy in git. You just go into the repo directory and run
git checkout <branchname> and all your files are changed to match the branch you asked for with virtually no processing required. This is what your repo directory would look like if you ran
git checkout bugfix1
repo |--bone.txt <-- 2 lines up to "I love Linus" |--test.txt <-- 2 lines up to "I'm Ugly"
Note: ‘master’ is a convention in git that refers to the main branch, similar to ‘trunk’ in svn.
The equivalent of the branches above in svn would look something like this:
repo |--branches |--trunk | |--test.txt <-- 4 lines up to "I'm a mactard" |--bugfix1 |--bone.txt <-- 2 lines up to "I love Linus" |--test.txt <-- 2 lines up to "I'm ugly"
To ‘switch’ branch you would have to download the file from the svn server again (quite a big deal when the repo is huuuge) and then cd into the new branch’s directory. So as you can see git makes branching much easier, quicker and lightweight.