If you’re working in the terminal and you want to create a branch, you might try `git create branch my-branch`. The syntax is intuitive, short, and, unfortunately, doesn’t exist.
Instead, you’re left with this command:
This is the fastest and easiest of creating a branch from the command line, but at first glance, this command is much less intuitive or easy to remember.
However, if you understand how git branches work below the surface, you’ll realize why this makes sense, and is ultimately the better way of describing what happens when you create a branch.
In this article, we’ll dive deep on Git branches to understand why they chose to express this behavior in such a non-obvious manner.
In order to understand what a branch is, let’s dive into the Git docs on branching. Here’s what they have to say:
A branch in Git is simply a lightweight movable pointer to [a commit]. The default branch name in Git is master. As you initially make commits, you’re given a master branch that points to the last commit you made. Every time you commit, it moves forward automatically.
Let’s break that down. This is a pretty interesting conceptual model, because if you’re anything like me, you initially imagined a branch as being more of a folder than anything else, with commits stored inside of it somehow. If that were the case, then it would make sense to want to create a branch as if you were creating a folder, using some kind of “create” command.
However, if a branch is just a “pointer” or reference to a specific commit, then that mental model doesn’t work at all. Branches being pointers implies that Git stores its data in a flat structure, rather than as a directory -- which is actually true! In fact, all commits live next to each other at the same level. And, think about it -- you’d never have a branch stored inside of another branch. Clearly we aren’t dealing with folders here.
That begs another question, then: how are commits ordered sequentially? That is, how can you tell what the other commits in a branch are, if a branch is just a pointer to one single commit?
Again, the Git docs have the answer.
When you commit in Git, Git stores a commit object that contains a pointer to the snapshot of the content... and zero or more pointers to the commit or commits that were the direct parents of this commit: zero parents for the first commit, one parent for a normal commit, and multiple parents for a commit that results from a merge of two or more branches.
Not only do branches point to commits, commits themselves can point to other commits. When you run `git log` or some other utility, or view commits on GitHub, those tools are examining the relationships between commits to present them in order.
The same thing happens when you run any commands relating to a branch: Git checks the commit that the branch is pointing to, and then finds all of its parents by looking at those pointers. Then, it operates on an entire line of commits together.
Now that we have a fundamental understanding of how branches and commits are stored and operated on, it’s time to explore the checkout command, which is where the magic in this shortcut happens. Here’s what it looks like:
The `checkout` command finds the commit that the branch passed to it is referring to, then updates all of the files on your hard drive (the files in your “working tree”) to match the files stored in that commit.
It also switches your current HEAD to that branch, so that if you create a new commit, that commit will store a pointer to the last commit made in the checked out branch.
Normally, this command is used to check out branches that already exist. However, when we pass in the “-b” flag, this command will create a branch with that name, and update HEAD to point to that branch.
This behavior produces the same result as running these two commands in order:
Now, if you want to push this newly created branch to your remote server, you’ll have to run another command:
This command tells the remote server that a new branch has been created locally, so that it can recreate the same branch.
The `--set-upstream` flag creates a relationship between the branch on the server, and this branch locally. Interestingly, that relationship is not established automatically by Git -- this is because a local branch can actually be linked to a branch of an entirely different name on the server.
Now that you’ve gotten your head around the basics of branch creation, it’s a good time to advocate for using some best practices -- generally speaking, you should stick to what’s called “git flow” for managing your repository. Unless you’re a solo developer working on a side project, it’s probably worth using.
Git flow is a methodology for creating, naming, and merging branches that keeps your codebase in a “clean” state at all times, so that you can merge pull requests and create releases in a smooth, bug-free fashion. Read about how it works in a blog post by its author, Vincent Driessen.
There are some other patterns too, of course -- there always are -- if you’re interested in checking those out too, read this Stack Overflow answer.
So you wish to do a "git force pull"? This tutorial will show you the right git commands to discard your local changes and replace them with your latest commit.
So you wish to "git remove untracked files"? This tutorial will show you the right git commands to remove unused files in your working directory.