How-to

Git Branches: git branch to Create, Switch, Rename and Delete

Updated: · Skillaria.Top

A Git branch is a lightweight pointer to a commit, and HEAD tells you which branch you are currently on. git branch lists, creates, renames and deletes branches, while git switch moves between them; git switch -c name creates a branch and switches to it in one step.

What is a branch in Git

A Git branch is not a copy of your project. It is a tiny file that holds the hash of a single commit. Creating a branch takes a fraction of a second and uses almost no disk space, because Git duplicates nothing: every branch points into the same object database. When you commit, the current branch pointer simply moves forward to the new commit.

That changes how you should think about branches. There is no reason to be afraid of them or to ration them: a separate branch for every task, bug fix or experiment is standard practice. If an experiment goes nowhere, you delete the branch and your main history stays clean.

You can see for yourself that a branch is just a file:

$ cat .git/refs/heads/main
8b7d4e2a9f1c0d3e5b6a7c8d9e0f1a2b3c4d5e6f

(In large repositories some refs are packed into .git/packed-refs, so the file may not exist. The idea is the same.)

What is HEAD in Git

HEAD is a pointer to whatever is currently checked out into your working directory. Usually HEAD points not to a commit but to a branch:

$ cat .git/HEAD
ref: refs/heads/main

The chain looks like this: HEAD → main → commit. A new commit gets the commit HEAD resolves to as its parent, and then Git moves the branch HEAD points to. That is why commits "land" on the current branch.

If you check out a commit or tag directly (git switch --detach v1.0 or git checkout 3f2a1c9), HEAD points straight at the commit. This is called detached HEAD. Browsing code in this state is safe, but new commits are not attached to any branch, and they are easy to lose once you switch away. If you started making changes, create a branch right there: git switch -c fix-from-old-version.

HEAD is also a handy reference point in expressions: HEAD~1 is the previous commit, HEAD~3 is three commits back, and HEAD^2 is the second parent of a merge commit.

How to list branches: git branch

git branch            # local branches, the current one marked with *
git branch -v         # plus the latest commit on each
git branch -vv        # plus upstream and ahead/behind counts
git branch -r         # remote branches (origin/...)
git branch -a         # all branches
git branch --show-current   # just the current branch name

Sample output of git branch -vv:

  feature/login 4c1e2d7 [origin/feature/login: ahead 2] Login form
* main          8b7d4e2 [origin/main] Update README
  old-spike     1a9b3c0 Cache experiment

Here you can see that feature/login has two unpushed commits, main matches the server, and old-spike has no remote counterpart at all.

To filter branches, use git branch --merged (already merged into the current branch, so safe to delete), git branch --no-merged (not merged yet) and git branch --list 'feature/*' (by pattern).

How to create a branch

There are two ways. git branch creates a branch but leaves you where you are:

git branch feature/login            # from the current commit
git branch hotfix v1.2.0            # from a tag
git branch review 3f2a1c9           # from a specific commit

More often you want to create a branch and switch to it right away. That is what git switch -c is for (added in Git 2.23), or its older equivalent git checkout -b:

git switch -c feature/login
git switch -c hotfix origin/main    # new branch from the latest server state

A good habit is to refresh your base before branching off for a task: git fetch, then git switch -c feature/x origin/main. That way the branch starts from up-to-date code instead of a local main you haven't pulled in ages.

Branch naming conventions

Common team conventions: a prefix for the type of work plus a short hyphenated description, such as feature/user-avatar, fix/login-timeout, chore/update-deps, sometimes with a ticket number: feature/SHOP-142-cart. You can't use spaces, ~ ^ : ? * [, two consecutive dots, or a name ending in .lock.

How to switch branches: git switch

git switch main             # switch to an existing branch
git switch -                # go back to the previous branch
git switch feature/login    # if there's no local branch but origin/feature/login exists,
                            # Git creates a local one and sets it to track the remote

When you switch, Git replaces the files in your working directory with the versions from the target branch. Uncommitted changes come along with you as long as they don't conflict with the target branch. If they do, Git refuses:

error: Your local changes to the following files would be overwritten by checkout:
        app.js
Please commit your changes or stash them before you switch branches.

Your options: commit, stash with git stash (and restore later with git stash pop), or deliberately carry the changes over with a merge attempt: git switch -m main. The --discard-changes flag throws your edits away, so use it only when you are sure you don't need them.

Why git switch instead of checkout? Checkout both switches branches and overwrites files, which makes it easy to make a mistake. Switch does one thing only, working with branches, so it's safer. For a detailed comparison of checkout, switch and restore, see the separate article.

How to rename a branch

git branch -m new-name              # rename the current branch
git branch -m old-name new-name     # rename any branch
git branch -M main                  # same, overwriting an existing branch

If the branch was already pushed, the rename is local only. Push the new name and delete the old one:

git push -u origin new-name
git push origin --delete old-name

How to delete a branch

git branch -d feature/login     # deletes only if the branch is merged
git branch -D old-spike         # force delete
git push origin --delete feature/login   # delete on the server

-d protects you from losing work: if the branch has commits that aren't in the current branch (or its upstream), Git refuses with "The branch 'x' is not fully merged". -D deletes without asking. You can't delete the branch you are on, so switch to another one first.

Deleted the wrong one? Commits don't disappear immediately. Find the hash of the branch's last commit in git reflog and restore it: git branch old-spike 1a9b3c0.

To clean up stale remote-tracking branches that no longer exist on the server, run git fetch --prune.

Tracking a remote branch (upstream)

A local branch can have an upstream, the remote branch it is compared and synced with. You need it so that git push and git pull work without arguments and git status shows ahead/behind.

git push -u origin feature/login                 # on the first push
git branch --set-upstream-to=origin/main main    # for an existing branch
git branch --unset-upstream                      # remove the link

Merging branches in a nutshell

Branches exist to be merged back. When the work is ready, switch to the target branch and merge yours into it:

git switch main
git merge feature/login

If main hasn't moved since you branched off, Git does a fast-forward: it simply moves the main pointer to the branch's latest commit, with no merge commit. If both branches have new commits, Git performs a three-way merge: it finds the common ancestor, compares both sides and creates a merge commit with two parents. In teams, merging usually happens through a pull request on the server, and locally you just pull the fresh main.

Git branch cheat sheet

TaskCommand
List branchesgit branch -vv
Create a branchgit branch name
Create and switchgit switch -c name
Switchgit switch name
Go backgit switch -
Renamegit branch -m old new
Delete a merged branchgit branch -d name
Force deletegit branch -D name
Delete on the servergit push origin --delete name
Branches that are safe to deletegit branch --merged
Restore a deleted branchgit reflog, then git branch name hash

Common beginner mistakes

  • Working directly on main. If you've already committed but not pushed, create a branch where you are (git branch feature/x), then reset main back to the server: git reset --hard origin/main (make sure everything is committed first).
  • Branching off a stale main. Run git fetch first and create the branch from origin/main.
  • Committing in detached HEAD. Create a branch before switching away; if you already switched, find the commits in git reflog.
  • Long-lived branches. The longer a branch lives on its own, the more painful the merge. Pull in main regularly and merge your work in small chunks.

Branching workflows in a team

Team processes vary, but almost everyone uses one of two models. In GitHub Flow there is one long-lived branch, main, which is always deployable, plus short feature branches merged through a pull request after review and CI checks. Git Flow adds develop, release branches and hotfix branches; it's heavier and suits products with infrequent versioned releases.

Whatever the model, the same rules apply: one branch per task, branches live for days, not months, a branch is updated with fresh main before the pull request, and after merging it's deleted both locally and on the server. The main branch is protected in the repository settings: direct pushes and force pushes are blocked, and merging happens only through review.

How to view branch history

The easiest way to see how branches relate is a graph: git log --oneline --graph --all draws every branch and where they diverge right in your terminal. To see commits that are on a branch but not yet in main, use git log main..feature/login, and for the file diff, git diff main...feature/login (three dots compare against the common ancestor, so you see only the branch's own changes).

Summary

A Git branch is a cheap pointer to a commit, and HEAD is a pointer to the current branch. git branch manages branches (listing, creating, renaming, deleting), git switch moves between them, and git switch -c does both at once. Branches, HEAD, fast-forward and three-way merges are covered with hands-on examples in the Git course.

FAQ

How many branches can I create in Git?

Practically as many as you like: each branch is a ref of a few dozen bytes, so size is not a concern.

Are commits deleted along with a branch?

Not right away. Commits that nothing references anymore are kept for a while and remain reachable via git reflog until garbage collection removes them (by default no sooner than 30 days for unreachable reflog entries).

How do I find which branch the current branch was created from?

Git doesn't store that: a branch only knows its own commit. You can find the common ancestor with main using git merge-base HEAD main.

What's the difference between a branch and a tag?

A branch moves forward with every new commit, while a tag stays pinned to one commit forever and is used to mark releases.

Why main instead of master?

It's just the default branch name. Many hosting services switched to main; you can set the name for new repositories with git config --global init.defaultBranch main.

Learn this topic on video

This topic is covered in depth with practice in the course “Git from Scratch to Confident Developer”.

Go to course