Sign in
← Git: basics
10 min · easy · Lesson 3 of 6

Branches

branch, switch, checkout. The feature-branch workflow, short and visual.

Why

You're fixing a bug while a coworker builds a new feature in the same repository. If you both commit to main, the half-finished feature ships to production along with your fix. Branches solve this: every task gets its own line of commits, and it only reaches main after review.

What a branch is

A branch is a named pointer to a commit. Inside the repository it's a record shaped like "branch name → commit hash". In the traditional storage format that's a file, .git/refs/heads/<name>, holding one 40-character hash:

cat .git/refs/heads/main
2acb8ed9d5faa57a03f063d84ebbd0f2efd00e68

The file might not exist. Git sometimes packs refs into a single file, .git/packed-refs (for example after git gc), and a repository using the newer reftable format stores them in a .git/reftable/ folder. The meaning doesn't change: a branch is still a "name → commit" record.

When you commit, the current branch's pointer moves to the new commit automatically. When you switch to another branch, HEAD starts pointing at it, and the files in your working folder update to match its latest commit.

This gives branches their main property: they're cheap. Creating one just means writing a single hash; no copy of the project gets made. That's why you open a branch per task and delete it after merging.

Commands

git branch                        # list local branches, * marks the current one
git branch feature/login          # create a branch at the current commit (HEAD), stay where you are
git switch feature/login          # switch to the branch
git switch -c feature/login       # create and switch to it right away
git branch -d feature/login       # delete, if its commits are already merged
git branch -D feature/login       # delete without checking

git branch -d refuses to delete a branch whose commits aren't merged anywhere yet, and suggests -D:

error: the branch 'feature/login' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/login'

This protects you from losing work. -D deletes the branch silently, so check first that you don't need its commits.

Before Git 2.23, you switched with git checkout. The command still works, but 2.23 split its responsibilities: git switch switches branches, git restore restores files. You'll see checkout constantly in other people's tutorials. Write switch yourself: it's less likely to accidentally overwrite files.

To create a branch from a specific commit instead of the current one:

git switch -c hotfix abc1234

In place of abc1234 you put a hash (the first 7 characters are enough) or a tag name.

HEAD: where you are

HEAD is a pointer to where you currently are. Usually it points at a branch, and through it at that branch's latest commit. In git log output this looks like HEAD -> main.

Sometimes you get detached HEAD: HEAD points straight at a commit instead of a branch. This happens when you want to look at an old version or a tag. git switch makes you say so explicitly:

git switch --detach v1.0

git checkout v1.0 detaches HEAD without asking.

You can commit in detached HEAD, but the new commit doesn't belong to any branch. Switch back, and it's gone from git log --all. Git warns you about this when you switch and shows the hash of the abandoned commit:

Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  1a75b2a detached-work

If you've done work in detached HEAD and haven't left yet, create a branch right there: git switch -c rescue. The commits will stay on it.

If you've already left, the commit isn't gone yet. Git keeps a log of HEAD movements, the reflog. Every line in it is a commit HEAD has visited:

git reflog
2acb8ed HEAD@{0}: checkout: moving from 1a75b2a... to main
1a75b2a HEAD@{1}: commit: detached-work
2acb8ed HEAD@{2}: checkout: moving from main to HEAD

Once you've found the hash you need, point a branch at it: git branch <name> <hash>. The reflog only lives in your local repository. Entries for commits no branch can reach are kept for 30 days by default (gc.reflogExpireUnreachable), the rest for 90 (gc.reflogExpire). Once an entry is gone, git gc can erase the commit itself too.

Feature-branch workflow

This is how almost any team works:

  1. From a fresh main, create a branch for the task: git switch -c feature/payments-retry.
  2. Work on it. Keep commits small, each about one change.
  3. Push the branch to the remote repository: git push -u origin feature/payments-retry.
  4. Open a Merge Request in GitLab or a Pull Request in GitHub. This is a request to merge the branch into main: your teammates review the changes, CI runs checks, you make fixes.
  5. Once it's approved, the branch gets merged into main and deleted.

The -u flag (long form --set-upstream) on the first push records the link between the local branch and the branch on the remote. Git responds like this:

branch 'feature/payments-retry' set up to track 'origin/feature/payments-retry'.

After that, plain git push and git pull are enough, no arguments needed. The link shows up in git branch -vv, in square brackets:

* feature/payments-retry 2acb8ed [origin/feature/payments-retry] one
  main                   2acb8ed one

Why main gets protected

Usually main is the source of truth for deployment: CI/CD runs on push to main. That's why team repositories follow rules like these:

In GitLab this is set up under Settings → Repository → Branch rules (older versions called this section Protected branches). GitHub has branch protection rules for this, plus the newer rulesets: the Rulesets section in repository settings.

Deleting a branch on the remote

git push origin --delete feature/payments-retry

More often the branch gets deleted through the UI after merging. GitLab has a "Delete source branch" checkbox in the MR; GitHub has a "Delete branch" button after the merge.

Common mistakes

What you'll need for the tasks

In the next lesson we'll cover how to merge a branch back: merge or rebase.

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson git-basics-03-branches