5 04 2020
GIT: Learn by doing. Chapter 2
What is a “branch” ?
If you have read the previous two chapters, you should already know that commit is the most fundamental part in Git. Diverged history points (alternate future) makes it possible to create branches. In this chapter I’m going to tell you about the “branches” you knew before – what are those, and why do you need them?
It appears that branches which you see in Git client, are only labels by themselves. It’s a human-readable reference to a commit. It’s like a Domain Name System (DNS). Domain name resolves to an IP address, and the branch resolves to a Git commit.
This brings us the first valuable property of a branch. Your commit can have as many labels as you want, and because of that, labels don’t have a time they’ve been created. Remember the task from the previous chapter? Have a look at the picture below. I’ve just added 5 “branches” to the dangling commit I had, while the branch was created at 28 of march, branches/labels are placed now.
The second and most valuable point is that removing a branch doesn’t remove the commit itself. It solely removes the label on that commit. I repeat – it SOLELY removes the label on that commit. Very important! Commit stays in history, and even if you do not see it, you can find it as I’ve already described in the previous chapter.
Enough theory it’s time for another exercise.
Undeletable
Task 1: Prepare the environment
The repository will have almost the same structure as in the previous post, except that history will have labels now – branches.
For curl’ers:
curl -o- https://raw.githubusercontent.com/nemisj/git-learn/master/branch-with-label.sh | bash
For wget’ers:
wget -qO- https://raw.githubusercontent.com/nemisj/git-learn/master/branch-with-label.sh | bash
For Powershell’ers:
Invoke-WebRequest https://raw.githubusercontent.com/nemisj/git-learn/master/branch-with-label.ps1 -UseBasicParsing | Invoke-Expression
When you run the script, it will create a branch-with-label
folder, and in that folder, you will find two branches: octopus
and cat
.
I’ve used such branch names on purpose and removed the ‘master’ branch repository to decouple your brain from the idea of “branches you know.”
Task 2: Removing a “branch”
Now, let’s remove the cat
branch, where the last commit message was “Added 1.1.2”. However, before doing that, remember the hash of the commit on which “cat” label is placed. In my case, it is 6f3810e5
. To remove the branch, I use the CLI, but you can do it through your Git client.
git branch -D cat
After removing cat
branch, my log looks like this:
Task 3: Find the dangling commit
Now let’s find out that commit 6f3810e5
is still there by using fsck
command. Execute in terminal:
git fsck --lost-found
This is what I’ve got in my log afterward:
To make sure, we are still able to access the commit and the whole tree of the “cat” branch, let’s check it out. Execute in your terminal:
git checkout {dangling_commit}
# for me it will be
# git checkout 6f3810e583258eb144cadd1e4628dfbac4341057
Commit, on which the cat
branch was set, is still there with all the history. Let’s have a look at the log.
What does this information give us? First of all, confidence that we can reclaim our branch if we have removed it by coincidence. Second, the confirmation that name of the branch is only a label, and we should start thinking of it like that.
A branch is just a …
Task 4: Creating a label/branch
Before we are going to make a new label for our dangling commit, I will tell you one exciting secret. Let’s list files inside hidden folder .git/refs/heads
, which is at the root of the repository. In that folder, you will see one file which has the name octopus
. Execute in terminal:
ls -l .git/refs/heads
Furthermore, let’s have a look at the content of that file. You will be surprised, yet the only line in this file has the hash to which the octopus
label is pointing to. Execute in your terminal:
cat .git/refs/heads/octopus
This is my output:
What does this mean? In its simplest form to create a label/branch on a commit, you have to create a file inside .git/refs/heads
with the content, which is the commit hash. Time to try it out and type in terminal:
echo {dangling_commit} > .git/refs/heads/new-cat-branch
As soon as you create a file, your git log will show you your new branch. It goes the same for removing a branch. Just kill the file in .git/refs/heads
, and your label is gone. Of course, not the commit itself, but this you already know.
After you understand that branch as you knew it is just a label on a commit, “Create a branch” in Bitbucket or GitHub has a different meaning to you, right? This button does not change the history of your repository. It does not make any new “shunt” or sideways in your history. It only places another label on the commit from where you want to start.
Task 5: Extend your imagination
To practice a bit more, let’s have a look at the picture below. Would you consider this history containing two branches or only one?
Technically, these are two branches. A branch is a label, and this history has two labels. As soon as someone starts committing from octopus commit, then you will see a split, but until then, it’s just a flat-liner, still with two labels/branches.
Nothing to loose
Task 6: Last but not least
I’m not sure if you know about the amend flag for in git commit, but this one allows you to “update” a commit if you forgot something to add/remove.
Let’s take as an example commit with the message Added 1.2.2
. You can change the content of this commit by adding something to the zork
file and then amending it to the last commit. Please change file zork
and then run in terminal:
git commit --amend -a --no-edit
By doing this, you will not see any new commit in the log, but instead, if you click on the last commit of the “octopus” branch, you will notice that your changes are also in that commit. This commit has a new hash, but it looks identical to the previous one, except for the changes. You would think actually that this commit is indeed the previous one, but it’s not true. What Git is doing underneath is creating a new commit and places the branch name on that new commit. The previous commit, which was there, is still available in the history, and, of course, you can find it with fsck and make checkout, but this I will leave for you to experiment. Below is the history after finding the old commit and checking it out:
The green line is an early commit, and the new one is with the ‘octopus’ label.
What
Summery
Let’s recap what we have learned:
- A branch is a label on a commit, which has no date, no owner, no attribute expect the commit hash
- A branch is a simple text file on a File System (FS)
- Removing a branch doesn’t remove your commit at all
- Everything that happens in Git, stays in Git
- Flatline history might still contain thousands of branches.
What’s next…
Now you’ve seen that its not so easy to delete something in Git and in the next chapter, I will start explaining what a “merge” is and how is it related to stuff so far explained.
GIT: Learn by doing GIT. Learn by doing: Chapter 3