Notes of Maks Nemisj

Experiments with JavaScript

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.

Branches are labels
Branches are labels

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.

Branches with labels
Branches with labels

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:

Cat branch is removed
Cat branch is removed

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:

Log of dangling "cat" branch
Dangling “cat” branch

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.

Dangling cat branch after checkout
Dangling cat branch after checkout

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 label is a label

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:

Content of the refs folder and octopus file
Content of the refs folder

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.

Log with new-cat-branch
new-cat-branch created

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?

2 branch or not 2 branch

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.

, ,

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.