There is no such thing in Git as the base branch of a branch. Instead, there is only the current commit, which Git calls the tip of the branch.
Drawing a graph
To understand this visually, you must start by drawing (at least part) of the Git fix graph. Here is an example of a tiny repository with three commits in it:
A <-B <-C <--master
The "true name" of any given commit is one of those big ugly hash identifiers, c0ffeeface1deadbead... and so on. This hash identifier uniquely identifies a particular commit and is actually created by hashing (hence the name "hash identifier") the contents of that commit. They look random and impossible to remember, so here I just use single uppercase letters.
The Git method โseesโ that the graph begins by reading the name of the branch, for example master . This branch name contains a hash identifier of a type C commit. We say that master points to commit C
Meanwhile, commit C itself contains the hash identifier of its previous (or parent) commit B So, we say that C points to B , the same as master points to C
Similarly, commit B points to A A is the very first commit, so nowhere does it need to be pointed back to it ... so it's just not like that. We call A root commit, and it allows us (and Git) to stop working backwards.
These internal arrows are a little annoying to draw and note that the hash identifier of B actually part of C itself, so it can never change (if we try to change this part of C , we get a new, different commit). Therefore, we can stop worrying about drawing them and write instead:
A
an arrow coming out of a branch name, although not constant, and that this whole idea of โโmaking a tip comes from.
Suppose we want to add a new commit to master . We do all the usual settings that Git requires (add or change some files and use git add ), and then run git commit . Git:
Writes a new commit D (which receives a new unique hash identifier). This new commit returns to C :
A
Then change the master (more precisely, its stored hash identifier) โโso that it points to the new commit we just made:
A
and, of course, there is no reason to continue this break in the drawing:
A
So this is how branches grow in Git.
To create a new branch, Git simply creates a branch name that points to some existing commit:
A \ B \ C \ D <-- master
We can select any of these commits and create a new branch point there. Take B and make newbr there:
A \ B <-- newbr \ C \ D <-- master
We would do this with git branch newbr <thing-that-finds-B> .
But how to find a commit?
How do we find B ? Well, one way is to run git log and cut and paste the hash id. But another way is to use the branch name. The name newbr now points to B If we want to make another branch point for fixing B too:
git branch thirdbr newbr
This makes Git search for newbr , which points to B , and create a new name thirdbr , which ... also points to B :
A
That's why creating a branch in Git sounds so fast that it does almost nothing! It just makes a label that points to some existing commit.
The end for which some points of a branch name are called the commit end of that branch. Note that a single commit can be the end of several branches at the same time. This is part of the more important thing about Git: some commits are in many branches, all at the same time. For example, commit A - the root commit - is located on each branch. (There may be more than one root commit in the repository, although itโs a bit complicated, and now we donโt need to worry about it. You cannot do this with the commands shown so far.)
What makes special branch names
However, a special property of branch labels is that they move. They not only move, but also automatically move.
We already saw this when we created the new commit D Git wrote a new commit id in master . But: How did Git know to use master ? . How is Git known to make D parent be C ?
Well, of course, at that time we had only one branch, but now we will make a new commit, now we have three labels master , newbr and thirdbr . First, let's do a git checkout thirdbr and draw the result:
A
Nothing has changed in the drawing, 1 except that I added the word HEAD here. HEAD is how Git knows which branches and commits are the current branch and commit.
So, now we do the usual modification files, git add and git commit . Git writes a new commit with its parent set to commit B Git sees that the current branch of thirdbr and thirdbr points to B , so the current commit is B Let draw a new latch E :
E / A--B <-- newbr \ C--D <-- master
All that remains is to move the current name of the thirdbr branch thirdbr that it points to the new commit E :
E <-- thirdbr (HEAD) / A--B <-- newbr \ C--D <-- master
and we're done: we added a new commit to the thirdbr branch (which is still HEAD and therefore still the current branch, but now E is the current commit).
When you add a commit to the current branch, HEAD indicates what the current commit is and where the new commit occurs. HEAD usually contains the name of the branch and what git checkout does: it checks for a specific commit โ usually the tip of an existing branch โ and then installs the HEAD file to remember the branch name. This is the name of the branch that remembers the fixation of the tip.
Using git checkout -b newname commit means: "check the specified commit, and then create a new branch name newname that points to this commit, and then set HEAD to this new name." If you leave part of the commit, HEAD is used by default. Since HEAD is (always) the current commit, Git gets the missing part of the "check out" and simply creates a new branch name and saves it in the HEAD file.
1 So far, nothing has changed on the chart, Git needed to update our work tree and index so that we can have the files by the way they were in commit B