Skip to content

Branching

Branching is the mechanism that enables parallel development — multiple developers (or a single developer working on multiple features) can modify the codebase independently, then integrate their changes. Git”s branching model is one of its defining strengths: branches are cheap (O(1)O(1) creation), fast to switch, and designed to be created and deleted frequently.

This contrasts sharply with older VCS where branching was an expensive operation that involved copying the entire directory tree (CVS) or was mediated by a central server (early SVN).

A branch in Git is nothing more than a 40-byte text file containing a SHA-1 hash. Creating a branch does not copy any files or objects — it writes a single reference:

Terminal window
$ git branch feature-auth
## Equivalent to: echo $(git rev-parse HEAD) > .git/refs/heads/feature-auth

This means:

  • Creating 1,000 branches costs the same as creating 1 (a few microseconds).
  • Deleting a branch does not delete any commits — it only removes the reference.
  • A commit can be reachable from multiple branches simultaneously.
gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    branch feature
    checkout feature
    commit id: "D"
    commit id: "E"
    checkout main
    commit id: "F"

In the graph above:

  • main points to commit F.
  • feature points to commit E.
  • Commits A``B``C are shared by both branches — no duplication.
  • Commit D and E are only reachable from feature.
  • Commit F is only reachable from main.
Terminal window
## Create a branch from HEAD (does not switch to it)
$ git branch feature-auth
# Create and switch in one step
$ git switch -c feature-auth
# Create a branch from a specific commit
$ git branch release-1.0 a3f2b1c
# Create a branch from a remote-tracking branch
$ git switch -c feature-auth origin/feature-auth
Terminal window
# List local branches
$ git branch
# List all branches (local + remote-tracking)
$ git branch -a
# List branches with their last commit
$ git branch -v
# Show which branches contain a specific commit
$ git branch --contains a3f2b1c
# Show merged branches (branches whose commits are all in HEAD)
$ git branch --merged
# Show unmerged branches (branches with commits not in HEAD)
$ git branch --no-merged

Git provides two commands for switching branches. git switch is the modern command (Git 2.23+); git checkout is the legacy command that serves double duty (switch branches and restore files).

Terminal window
# Modern (recommended)
$ git switch main
# Legacy (still widely used)
$ git checkout main

When you run git switch mainGit performs the following:

  1. Validate: Check that the working directory is clean (or that changes can be carried over).
  2. Update HEAD: Write ref: refs/heads/main to .git/HEAD.
  3. Update the index: Load the tree object pointed to by main into the index.
  4. Update the working directory: Compare the new index with the current working directory, and add, modify, or delete files as needed.
flowchart LR
    A["git switch main"] --> B{"Working directory<br/>is clean?"}
    B -->|Yes| C["Update HEAD"]
    C --> D["Update index"]
    D --> E["Update working directory"]
    B -->|No| F{"Changes conflict<br/>with target branch?"}
    F -->|No| G["Carry changes over<br/>(update index only)"]
    G --> C
    F -->|Yes| H["Error: would overwrite<br/>local changes"]
    H --> I["Commit, stash,<br/>or discard changes first"]

If you have uncommitted changes that do not conflict with the target branch, Git carries them over. If they do conflict, Git refuses to switch:

error: Your local changes to the following files would be overwritten by checkout:
src/main.c
Please commit your changes or stash them before you switch branches.
Aborting

If you want to discard local changes and switch anyway:

Terminal window
$ git switch -f main
# Discards all uncommitted changes in the working directory and index

Branching in Git is like creating a parallel universe: you can experiment freely without affecting the main line. A branch is just a pointer to a commit, so creating one is instant. When you switch branches, Git updates your working directory to match the snapshot at that commit. Think of it as time travel: you jump to a different point in the codebase’s history and work from there. Merging brings the parallel universes back together.