Skip to content

Branches in Git

docs.scrimba.com

You are partway through building weather-app, and you want to try adding a five-day forecast feature. It might take a few commits to get it right, and until it does, you do not want half-finished code sitting next to the app that already works for everyone using it. A branch is how Git solves this: a separate line of work where the experiment lives, while main keeps working exactly as it did before you started.

What a branch actually is

Here it is in action:

bash
$ git branch forecast
$ git switch forecast
Switched to branch 'forecast'

Two commands: the first creates a new branch called forecast, the second moves you onto it. From here, every commit you make lands on forecast while main stays exactly where it is.

A branch is a movable label that points at a commit. Right now, main and forecast both point at the same commit, the one you were standing on when you ran git branch forecast. The moment you commit again while on forecast, that label moves forward to the new commit. main stays exactly where it was until you switch onto it and commit there yourself.

You can see both labels with git branch:

bash
$ git branch
  forecast
* main

The asterisk marks the branch you are currently on. A branch is only a name pointing at a commit. That is the whole mechanism. Everything else in this chapter is a variation on moving those labels around.

In a real project, branch names carry information. A common convention is a short prefix describing the kind of change, followed by a description: feature/five-day-forecast, fix/broken-date-picker, chore/upgrade-eslint. Some teams add a ticket number (feature/wa-142-forecast). Whichever convention you pick, keep it consistent across the project so anyone can tell what a branch is for without opening it.

The workflow those names support is short-lived feature branches: create a branch for one piece of work, commit to it, get the change reviewed and merged, then delete the branch. A branch that lives for weeks drifts far from main, and the eventual merge gets harder the longer that drift continues. The healthiest branches finish within days.

A branch is barely more than a tiny file holding one commit hash, tucked inside Git's own bookkeeping. Creating one means writing that single line, which is why git branch finishes instantly even in a repository with years of history behind it. That same cheapness is why naming conventions and short-lived branches matter in practice: deleting a stale branch costs Git nothing either, so nothing stops abandoned ones from piling up except a team's own discipline.

JunoWhat a branch actually isgit branch forecast creates a new label pointing at your current commit, and git switch forecast moves you onto it. Nothing about your files changes until you actually commit there. I like to picture branches as sticky notes on a commit: cheap to add, cheap to move, cheap to peel off when you are done.
JunoWhat a branch actually is A branch name is a pointer, so creating one costs nothing and switching between them is instant. Give branches names that describe the work, such as feature/five-day-forecast, and keep them short-lived: create, commit, merge, delete. Long-lived branches are the ones that turn a routine merge into a real headache.
JunoWhat a branch actually is A branch really is a small file holding a commit hash, which is why creating one is one of the cheapest operations in Git. Naming conventions and short-lived branches are how a team keeps its parallel work legible; Git itself enforces none of it. I have cleaned up enough abandoned branches from six months ago to have strong opinions about this.

Creating and switching branches

git branch forecast on its own creates the label without moving you there. Most of the time you want both at once, so git switch has a shortcut:

bash
$ git switch -c forecast
Switched to a new branch 'forecast'

-c stands for "create". git switch -c both creates and moves you onto a branch. That single command is the one you will reach for almost every time you start new work.

Switching branches changes which snapshot your files show. Try it: the echo line below is a one-step way to create a new file, src/forecast.js, with a single line in it, and everything else is commands you already know:

bash
$ git switch forecast
$ echo "// five-day forecast logic" > src/forecast.js
$ git add src/forecast.js
$ git commit -m "Start forecast module"
$ ls src
forecast.js  index.js
$ git switch main
Switched to branch 'main'
$ ls src
index.js

The file has not vanished. It lives on the forecast branch, and the main branch's snapshot never included it. Switch back to forecast and it reappears exactly as you left it.

You will see an older verb for this in existing code and tutorials: git checkout forecast does the same job as git switch forecast. checkout is older and does more than one thing: depending on what you give it, it can switch branches or restore a file to an earlier version, and the command name alone does not tell you which one is about to happen. switch and restore split those two jobs into separate commands so the name tells you upfront: switch moves you between branches, restore puts a file back to how it looked at a given commit. Reach for the newer verbs; recognize checkout when you meet it in the wild.

checkout decides what to do based on what you hand it: a branch name switches branches, a file path restores that file, and if a name matches both, Git has to guess which one you meant. That guesswork is exactly why the command got split in two. switch only ever changes branches and restore only ever changes files, so a mistyped path can no longer switch you onto a branch by accident. Expect checkout in older scripts and tutorials for years yet; write switch and restore in anything new.

JunoCreating and switching branchesgit switch -c forecast creates a branch and moves you onto it in one step, which is what you will use almost every time. Switching branches swaps which version of your files you see, so a file added on one branch is absent on another until you switch back. Nothing is lost, it is parked on the other branch.
JunoCreating and switching branchesgit switch -c name creates and moves in one step. You will meet git checkout name doing the same job in older tutorials and codebases; it is the same idea under an older, more overloaded command. Reach for switch day to day and read checkout without hesitation when you see it.
JunoCreating and switching branchescheckout handles both branch switching and file restoration depending on its arguments, which is exactly the ambiguity switch and restore were split out to remove. Expect to read checkout in older scripts and tutorials for years yet; it still works, it is no longer the command to write by default.

HEAD and detached HEAD

Git keeps track of where you currently are with a marker called HEAD. Normally HEAD points at your current branch, and that branch points at a commit, so HEAD moves along automatically every time you switch or commit. If you check out a specific commit by its hash, or a tag (a fixed label pinned to one commit, often marking a release), instead of a branch name, HEAD attaches directly to that one commit rather than to a branch label. This state is called a detached HEAD.

You can look around freely there, and you can even commit, but nothing points at those new commits by name. If you switch to a branch without creating one first, they become hard to find again.

bash
$ git switch a1b2c3d
HEAD is now at a1b2c3d Add password strength meter

That message, "HEAD is now at", is Git telling you that you have left branch territory and attached HEAD to a single commit.

In everyday work you rarely land here by accident. It happens most often when you check out an old tag or a specific commit to look at how the code behaved at that point, or when a build tool checks out one exact commit to work from. If you only want to look, that is fine: browse the files, run the app, then switch back to a branch when you are done. If you make a change worth keeping while detached, create a branch before you switch away: git switch -c fix/old-bug from that detached state gives the commit a real label so it survives the switch.

A commit left behind by a detached HEAD is not deleted the moment you switch away. It becomes unreachable: still stored, with no branch, tag, or HEAD pointing at it anymore. Git does not clean those up right away either. It keeps a reflog, a record of everywhere HEAD has pointed, and an unreachable commit typically survives for weeks before Git actually deletes it for good.

That gives you real time to recover, though it does not last forever. Making a branch before switching away keeps detached work safe. Digging a commit back out of the reflog works today, but the entry expires eventually and a commit hash slips your mind long before that happens, so make branching the habit and save the reflog for the rare time you forget, covered in Undoing things.

JunoHEAD and detached HEAD HEAD marks the commit you are currently standing on, and it usually rides along with whichever branch you are on. If you check out a specific commit instead of a branch, you get a detached HEAD, and any new commit you make there has no branch name pointing at it. If that ever happens and you want to keep the work, make a branch on the spot before you switch anywhere else.
JunoHEAD and detached HEAD You will most likely meet a detached HEAD when checking out a tag or an old commit to look around, and it is harmless as long as you are only reading. The moment you commit something worth keeping, run git switch -c right there to give it a branch before you go anywhere else.
JunoHEAD and detached HEAD Normally HEAD points at a branch, which points at a commit. Detached, HEAD points straight at the commit, so anything you add there has no branch name holding onto it once you switch away. It is rarely gone for good since the reflog keeps a trail, but do not count on remembering the commit hash three weeks later. Make the branch before you switch, always.

Where this leads

You have now created branches, moved between them, and seen how main stays untouched while you experiment elsewhere. The next question is how work on a branch gets back into main, which is exactly what Merging and conflicts covers. If you want a refresher on reading commit history before moving on, History walks through git log, git show, and git diff.