There's no strong reason to have your own name master
, and no strong reason not to have your own name master
. It comes down to what you want, and what you want to deal with.
What's important to know here is that at one level, Git doesn't really care about branch names at all. (At other points, it does, but we should look first at this level where it doesn't.)
In Git, a branch name is just a moveable label, like one of those yellow sticky notes (or the "sign here" label forms of them), that you paste onto a commit. It's the commits themselves that make up the actual branches.
Branches without names
Suppose we start with a repository with just one commit in it. Call that commit A
(instead of its actual incomprehensible and unpronounceable hash ID):
A
Now we add a new commit, B
, whose parent is A
:
A <-B
We say that commit B
"points to" A
. In other words, it's commit B
itself that remembers my previous commit is A
.
When we add new commit C
, that commit points back to B
:
A <-B <-C
(What about A
, where does it point? Nowhere: it has no parent! It can't, because it was the first commit. Technically A
is a root commit. You will see Git print this phrase, root commit
, when you make the first commit in a new repository.)
All these internal arrows are a pain to draw. We know they always point backwards: they have to, because commits can only remember their parents, not any children that don't exist yet ... and once Git makes a commit, it can never change anything in that commit. So it can't add a list of children later. As a result, Git always wants to work backwards, from the newest commits to the oldest. So let's draw them without arrows:
A--B--C
If we want to make a branch, we just pick some commit somewhere and use it as the parent of a new commit. Let's make a new commit D
whose parent is B
:
A--B--C
\
D
and now we have a branch!
Branch names
The problem Git has is that it can't find these commits quickly. This is where branch names come in. We make a branch name, like master
, and make it point to the tip commit of the underlying branch:
A--B--C <-- master
\
D
We need another name for commit D
; let's call it develop
for now:
A--B--C <-- master
\
D <-- develop
Now, the thing that makes branch names like master
special is that we can get "on" them, using git checkout
:
$ git checkout master
Switched to branch 'master'
$ git checkout develop
Switched to branch 'develop'
We need a way to remember which branch we're "on". Git uses HEAD
for this:
A--B--C <-- master
\
D <-- develop (HEAD)
When you git checkout
a branch, Git checks out the tip commit of the branch, and makes HEAD
remember that particular branch-name.
Here's the other special feature: when you make a new commit, its parent is the current (HEAD) commit, and then Git reads HEAD
to see which branch it names, and moves the branch name. Let's make a new commit E
on develop
by changing some files, using git add
, and git commit
:
A--B--C <-- master
\
D--E <-- develop (HEAD)
New commit E
points back to D
. The name develop
now points to E
. The name HEAD
still refers to develop
, but the current commit is now E
.
Protecting (or keeping) commits
I mentioned above that Git has a hard time finding commits without the names. But these names do not just make it easy to find the commit they point to. They also serve to protect these commits. Git has a maintenance command, git gc
or the Garbage Collector, that does a slow and painful crawl to find every commit (and other object) in the repository, and check whether they have names. If not, git gc
can collect them up as trash and remove them.
Thus, the existence of a name tells Git that this commit matters: Git should keep it. If this commit matters, then its parent commit also matters. That parent commit's parent matters too, and so on all the way back to the root commit. So if master
points to C
, Git has to keep C
(and then also B
and A
). If develop
points to E
, Git has to keep E
(and then also D
and B
and A
).
But branch names are not the only names we have. We also have what Git calls remote-tracking branches, like origin/master
. (And we have tags, and some special references like stash
, and Git's "notes", and so on. The general term here is references, though you don't need to remember that.)
When you git clone
a repository from somewhere, Git talks to another Git. That other Git has its own branches (and all its other references, including tags and remote-tracking branches). Your Git gets their list of branches (and tags; your Git normally ignores their remote-tracking branches here). Your Git then renames all their branches. So if we clone this repository, with its five commits, we get a new copy that looks like this:
A--B--C <-- origin/master
\
D--E <-- origin/develop
These origin/
names are remote-tracking branches (not regular, ordinary, local branches; you cannot get "on" them). These serve just as well as local branches to protect the commits, and to let Git find them. You just can't get on them. (If you try, you get what Git calls a "detached HEAD" instead.)
Your Git then creates at least one local branch name, usually master
, using one of these remote-tracking branch names, usually origin/master
, and gets you onto that branch:
A--B--C <-- master (HEAD), origin/master
\
D--E <-- origin/develop
Note that nothing happened to any of the commits. We've just added a new label, master
, pointing to commit C
, just like origin/master
does. (And then we had Git set HEAD
to master
to remember that this is the branch we're "on".)
(Like branch names, tag names also get copied over. However, unlike branch names, tag names don't get origin/
shoved in front. So that's one of several things that makes tags different from branches. Like remote-tracking branch names, you can't get "on" a tag either, and as a rule, tag names should not move the way branch names do.)
Using git fetch
to add to your repository
This is all fine and well for the original git clone
, but eventually you probably want to pick up new commits that someone else added to the repository you cloned. You do this by running git fetch
. (If you run git pull
, be aware that it just runs git fetch
, and then runs a second Git command. So you're still using git fetch
.)
What git fetch
does is go back to the other Git at origin
and get, from it, its current branch names and their commit hash IDs. Since hash IDs are guaranteed to be unique across all these sharing repositories, your Git can tell if those are new commits, or not. Your Git then asks their Git for the new commits (and their parents and grandparents and so on, as needed, to get back to the point where you are talking about commits you already have). Let's see what happens as we bring in two new commits from their master
:
F--G <-- origin/master
/
A--B--C <-- master (HEAD)
\
D--E <-- origin/develop
Here F
's parent commit is C
. We already had C
so our Git didn't have to bring that one in. But our Git did bring in their G
, which required their F
. And, our Git saw that their master
now names commit G
. So our Git updates our memory—our origin/master
—to point to G
too.
Now our master
is behind, and we need to do something to make it catch up. Or, we could just delete it, as long as we stop using it as HEAD
. For instance, we could git checkout -b develop origin/develop
to make a new local develop
based on origin/develop
, and move our HEAD
there:
F--G <-- origin/master
/
A--B--C <-- master
\
D--E <-- develop (HEAD), origin/develop
Again, nothing happens with any commits: this is all name-shuffling. (Well, our index and work-tree get filled-in from commit E
, too.)
We can update our master
to match theirs:
F--G <-- master, origin/master
/
A--B--C
\
D--E <-- develop (HEAD), origin/develop
and now we can straighten out the kink in the graph (draw A--B--C--F--G
in one big straight line). Or we can delete our name master
and not have to drag it around any more, and likewise straighten out the drawing.
Nothing really changes as a result: we just have, or don't have, the name master
pointing to some commit. If we do have it, we must decide whether to update it. If we don't have it, we don't have to decide anything. Those are your reasons to have, or not to have, master
: so that you can remember where it was, and drag it around if you like to do that, or so that you don't have it and don't have to drag it around if you don't like to do that.
(If you do keep the name, you can tell just what's come in since the last time you dragged it forward. If you don't keep the name ... well, Git has reflogs that save previous values of references, including remote-tracking branches, so you can pretty much do the same thing, except that reflog entries eventually expire.)