Branching is Git's most powerful feature and the one that sets it apart from almost every other version control system that came before it. Understanding how branches work — not just how to use the commands, but what is actually happening under the hood — makes everything else in Git fall into place.
What a Branch Actually Is
To understand branching, you need to recall how Git stores data. Every commit is a snapshot that stores a pointer to the previous commit (its parent). This chain of parent pointers forms your project history.
A branch is simply a lightweight, movable pointer to one of these commits. That is all it is — a file containing a 40-character SHA-1 hash pointing to a specific commit. Creating a branch in Git takes milliseconds and costs almost nothing, because Git is only writing 41 bytes to a file.
This is in sharp contrast to older version control systems, where branching meant copying the entire project directory — a process that could take minutes on large projects. In Git, branching is instantaneous regardless of project size.
When you make commits on a branch, the branch pointer moves forward automatically to point to the newest commit. The branch always reflects the latest state of work on that line of development.
HEAD — Where You Are Right Now
Git uses a special pointer called HEAD to track which branch you are currently on. HEAD always points to the branch you have checked out — and the branch points to the latest commit on that branch.
When you make a new commit:
- Git creates the commit with the current commit as its parent.
- The branch pointer moves forward to the new commit.
- HEAD continues to point to the branch.
HEAD → main → commit C
When you switch branches, HEAD moves to point to the new branch, and your working directory is updated to match the state of that branch's latest commit.
Creating a Branch — git branch
To create a new branch:
git branch feature-login
This creates a new pointer called feature-login pointing to the same commit you are currently on. Importantly, it does not switch you to the new branch — HEAD still points to your original branch.
To see all branches and which one you are on:
git branch
feature-login
* main
The * marks the branch HEAD is currently pointing to — the branch you are on.
Switching Branches — git switch and git checkout
To move to a different branch:
git switch feature-login # Modern syntax (Git 2.23+)
git checkout feature-login # Older syntax — still works
When you switch branches, Git does two things:
- Moves HEAD to point to the new branch.
- Updates the working directory to match the snapshot that branch points to — adding, removing, and modifying files automatically.
Important: Switching branches changes the files in your working directory. If you switch to an older branch, your files will look exactly as they did the last time you committed on that branch. If you have uncommitted changes that conflict with the branch you are switching to, Git will refuse to switch until you deal with them — either by committing or stashing the changes.
Creating and Switching in One Step
The most common pattern is creating a branch and immediately switching to it:
git switch -c feature-login # Modern syntax
git checkout -b feature-login # Older syntax
Both are shorthand for:
git branch feature-login
git switch feature-login
Use git switch -c in new work — it is cleaner and more explicit in its intent.
How Branches Diverge
The real power of branching becomes clear when you understand divergence. Once you switch to a new branch and make commits, the histories of the two branches diverge — each branch moves independently forward from a shared common ancestor.
# Start on main, create a feature branch
git switch -c feature-login
# Make commits on feature-login
git commit -m "Add login form"
git commit -m "Add authentication logic"
# Switch back to main
git switch main
# Make a different commit on main
git commit -m "Fix homepage typo"
At this point the history looks like this:
A -- B -- C (main)
\
D -- E (feature-login)
The two branches share commits up to B, then diverge. Each branch has its own independent line of development. Changes on feature-login have no effect on main and vice versa — until you decide to merge them.
You can visualise this directly:
git log --oneline --graph --all
* c3f4d1a (HEAD -> main) Fix homepage typo
| * e8a21bc (feature-login) Add authentication logic
| * d7b903f Add login form
|/
* f30ab12 Previous commit
Why Branching Matters
Before Git made branching cheap and instant, most teams avoided it because the cost was too high. Git changed the economics completely.
The standard modern workflow uses branches for every piece of isolated work:
- Feature branches — each new feature lives on its own branch, developed independently from the main codebase.
- Bug fix branches — urgent fixes are created as short-lived branches off the stable main branch.
- Experimental branches — speculative ideas can be explored freely and thrown away if they don't work out, with no impact on anything else.
Because creating and deleting branches is instant and free, the practice of branching often — even multiple times per day — is not just possible but actively encouraged. It keeps work isolated, history clean, and collaboration safe.
Basic Branch Commands
List branches
git branch # Local branches
git branch -v # Local branches with last commit
git branch -a # All branches including remote-tracking
Create a branch
git branch <name> # Create without switching
git switch -c <name> # Create and switch
Switch branches
git switch <name> # Switch to existing branch
git switch - # Switch back to the previous branch
Delete a branch
git branch -d <name> # Delete a merged branch (safe)
git branch -D <name> # Force delete an unmerged branch
Deleting a branch only removes the pointer — it does not delete any commits. If the branch was merged, those commits remain accessible through the branch they were merged into.
Rename a branch
git branch -m <old> <new> # Rename a local branch
The --decorate Flag
When viewing history, the --decorate flag (included by default in --oneline) shows where each branch pointer and HEAD are currently sitting:
git log --oneline --decorate
f30ab (HEAD -> main, feature-login) Add feature
34ac2 Fix bug
98ca9 Initial commit
This shows that both main and feature-login are pointing to the same commit, and HEAD is on main.
Summary
| Command | What It Does |
|---|---|
git branch | Lists all local branches |
git branch <name> | Creates a new branch |
git branch -v | Lists branches with last commit |
git branch -d <name> | Deletes a merged branch |
git branch -D <name> | Force-deletes a branch regardless of merge status |
git switch <name> | Switches to an existing branch |
git switch -c <name> | Creates a branch and switches to it |
git switch - | Switches back to the previous branch |
git checkout <name> | Switches to a branch (older syntax) |
git checkout -b <name> | Creates and switches to a branch (older syntax) |
git log --oneline --graph --all | Visualises the full branch history |