| |

Git 3a 🧩 Understanding Branches

⏱️ Estimated read: 13 min | |

A branch is an independent line of development. It lets you work on a feature, a fix, or an experiment without touching the stable version of your code. In Git, branches are cheap — creating one is instant, and switching between them is fast. That’s why branching is at the center of almost every Git workflow.

Key point: A branch in Git is just a movable pointer to a commit. It’s not a copy of the codebase — it’s a label that says “the latest work on this line of development lives here.” Understanding that changes how you think about branches.


What are branches?

A branch is a separate workspace for your code. Imagine building a house — instead of having everyone work on the same foundation, branches let different people work on different parts at the same time.

Think of branches as parallel universes of your code. Each one evolves independently. You can make changes, test ideas, and experiment without worrying about breaking the main project.

Branches are the way teams work on multiple things at once without stepping on each other.


Why do we use branches?

Three reasons cover almost every use case.

Feature development without risk — when you add a new feature, you don’t want to break the working system. You create a branch, build the feature in isolation, and merge only when it’s ready and tested.

Bug fixes in isolation — critical bugs need fixes, but a rushed fix can introduce more problems. A dedicated branch lets you fix, test, and verify before it touches production.

Experimentation and testing — developers try new approaches and technologies. Branches let them explore without affecting stable code that other people depend on.

The pattern is the same in all three: isolate work, then merge when ready.

Why this matters: Branches turn “risky change” into “safe experiment.” You can throw a branch away, rewrite it, or merge it — all without affecting anyone else.


The main branch

Every repository has a main branch — the official version of the project. It represents what should be in production, what users are actually running, and what gets deployed to customers.

Historically the main branch was called master. Modern Git and most hosting platforms default to main. Both names mean the same thing — the primary line of development.

The main branch is typically very stable. It only receives updates when features have been thoroughly tested and approved — usually via a pull request and code review.

Why it’s stable: Everyone depends on the main branch. If it breaks, deployments fail. That’s why changes land there carefully, not casually.


Understanding HEAD

HEAD is a marker that always points to your current location in the project history. Git uses it to know where you are.

Think of HEAD as a spotlight — it always shines on the commit you’re currently on.

  • When you’re on the main branch, HEAD points to the latest commit on main.
  • When you switch to a feature branch, HEAD moves to that branch’s latest commit.
  • HEAD always points to exactly one commit — never to multiple places.

HEAD can also be detached. That happens when you check out a specific commit instead of a branch. HEAD is then pointing directly at a commit, not at a branch. Git warns you because commits made in a detached state aren’t on any branch.

┌──────────────────────────────────────────────┐
│  main                                        │
│    │                                         │
│    ▼                                         │
│  A ──► B ──► C  ← HEAD (on main)             │
│                                              │
│  After git checkout feature                  │
│                                              │
│  main                                        │
│    │                                         │
│    ▼                                         │
│  A ──► B ──► C                               │
│                                              │
│  feature                                     │
│    │                                         │
│    ▼                                         │
│  A ──► B ──► X  ← HEAD (on feature)          │
│                                              │
└──────────────────────────────────────────────┘

Why HEAD matters: Every Git command that says “the current branch” or “the last commit” is really using HEAD. Understanding it makes sense of everything from git log to git reset.


How branches work together

When you create a branch from the main codebase, you’re essentially copying everything that existed at that moment. From there, you can make unlimited changes without affecting the original.

Eventually, when your work is ready, you merge the branch back into main. It’s like bringing experimental work back to the official project.

The two-step pattern:

  1. Branch — create a new line of development
  2. Merge — bring the changes back

Everything else — rebasing, cherry-picking, rebasing — is a variation on these two.

Common branch types:

TypePurpose
mainStable, deployable
feature/*New features
fix/*Bug fixes
hotfix/*Urgent production fixes
release/*Preparing a release
experiment/*Experimental work

Why this shape: Branches have meaning. Naming them by purpose — feature, fix, hotfix — makes the repository self-documenting.


Full Working Git Example

A complete walkthrough — building a restaurant website with feature branches.

Step 1: Initialize the repository

mkdir restaurant-website
cd restaurant-website
git init

You now have an empty repository.

Step 2: Set up the initial project structure

echo "# Restaurant Website" > README.md
echo "<html><body><h1>Welcome to Our Restaurant</h1></body></html>" > index.html

git add .
git commit -m "Initial commit: Basic website structure"

One commit on main — the base.

Step 3: Create a feature branch

git checkout -b feature/menu

echo "<h2>Our Menu</h2><ul><li>Burger</li><li>Pizza</li></ul>" >> index.html

git add index.html
git commit -m "Add basic menu items"

-b creates the branch and switches to it in one command. HEAD now points to feature/menu. The main branch is untouched.

Step 4: Continue working on the feature

echo "<h3>Appetizers</h3><ul><li>Caesar Salad</li><li>Fried Calamari</li></ul>" >> index.html
echo "<h3>Main Courses</h3><ul><li>Grilled Salmon</li><li>Beef Steak</li></ul>" >> index.html

git add index.html
git commit -m "Add menu categories and items"

Another commit on the feature branch.

Step 5: Work on another feature

git checkout main
git checkout -b feature/reservations

echo "<h2>Make a Reservation</h2><form><input type='text' placeholder='Name'></form>" >> index.html

git add index.html
git commit -m "Add reservation form"

You switched back to main, then created a second branch from it. Two independent branches now exist — each with different work.

Step 6: Return to the menu feature

git checkout feature/menu

echo "<h3>Desserts</h3><ul><li>Chocolate Cake</li><li>Tiramisu</li></ul>" >> index.html

git add index.html
git commit -m "Add dessert section to menu"

The menu branch keeps evolving independently.

Step 7: Test and merge the menu feature

git checkout main
git branch -a
git merge feature/menu
git log --oneline

git merge feature/menu brings the menu work into main. Main now includes everything from the menu branch. History shows the merge.

Step 8: Continue working on reservations

git checkout feature/reservations

echo "<p>Available times: 5pm, 6pm, 7pm, 8pm</p>" >> index.html

git add index.html
git commit -m "Add reservation time slots"

git checkout main
git merge feature/reservations

Reservations are now merged into main too. Main has menu and reservations.

Step 9: View the final result

git log --oneline --graph --all
cat index.html

The graph shows the branching and merging. index.html has everything.

Step 10: Create a bug fix branch

git checkout -b fix/menu-bug

git add index.html
git commit -m "Fix HTML structure in menu"

git checkout main
git merge fix/menu-bug

A short-lived branch for a fix. Merged immediately.

Why this example matters: It shows the actual rhythm of branch-based work — branch, commit, switch, branch, commit, merge. Every feature gets its own space, and main stays clean until work is ready.


Complete Example Session

# ============================================
# PART 1: INIT
# ============================================

mkdir restaurant-website
cd restaurant-website
git init
# [ Initialized empty Git repository ... ]

# ============================================
# PART 2: INITIAL COMMIT
# ============================================

echo "# Restaurant Website" > README.md
echo "<html><body><h1>Welcome to Our Restaurant</h1></body></html>" > index.html
git add .
git commit -m "Initial commit: Basic website structure"
# [ [main (root-commit) a1b2c3d] Initial commit ]

# ============================================
# PART 3: CREATE FEATURE BRANCH
# ============================================

git checkout -b feature/menu
# [ Switched to a new branch 'feature/menu' ]

echo "<h2>Our Menu</h2><ul><li>Burger</li><li>Pizza</li></ul>" >> index.html
git add index.html
git commit -m "Add basic menu items"
# [ [feature/menu d4e5f6g] Add basic menu items ]

# ============================================
# PART 4: CONTINUE FEATURE
# ============================================

echo "<h3>Appetizers</h3><ul><li>Caesar Salad</li><li>Fried Calamari</li></ul>" >> index.html
echo "<h3>Main Courses</h3><ul><li>Grilled Salmon</li><li>Beef Steak</li></ul>" >> index.html
git add index.html
git commit -m "Add menu categories and items"

# ============================================
# PART 5: SWITCH AND CREATE ANOTHER BRANCH
# ============================================

git checkout main
git checkout -b feature/reservations
echo "<h2>Make a Reservation</h2><form><input type='text' placeholder='Name'></form>" >> index.html
git add index.html
git commit -m "Add reservation form"

# ============================================
# PART 6: BACK TO MENU
# ============================================

git checkout feature/menu
echo "<h3>Desserts</h3><ul><li>Chocolate Cake</li><li>Tiramisu</li></ul>" >> index.html
git add index.html
git commit -m "Add dessert section to menu"

# ============================================
# PART 7: MERGE MENU
# ============================================

git checkout main
git branch -a
# [   feature/menu ]
# [   feature/reservations ]
# [ * main ]

git merge feature/menu
# [ Updating a1b2c3d..h7i8j9k ]
# [ Fast-forward ]
# [  index.html | 5 +++++ ]
# [  1 file changed, 5 insertions(+) ]

git log --oneline
# [ h7i8j9k (HEAD -> main, feature/menu) Add dessert section to menu ]
# [ ... ]

# ============================================
# PART 8: MERGE RESERVATIONS
# ============================================

git checkout feature/reservations
echo "<p>Available times: 5pm, 6pm, 7pm, 8pm</p>" >> index.html
git add index.html
git commit -m "Add reservation time slots"

git checkout main
git merge feature/reservations

# ============================================
# PART 9: VIEW RESULT
# ============================================

git log --oneline --graph --all
# [ *   merge commit ]
# [ |\  ]
# [ | * Add reservation time slots ]
# [ | * Add reservation form ]
# [ * | Add dessert section to menu ]
# [ |/  ]
# [ * Add menu categories and items ]
# [ * Add basic menu items ]
# [ * Initial commit ]

cat index.html
# (contains all merged content)

# ============================================
# PART 10: BUG FIX BRANCH
# ============================================

git checkout -b fix/menu-bug
git add index.html
git commit -m "Fix HTML structure in menu"
git checkout main
git merge fix/menu-bug

Quick Reference

Branch Commands

CommandPurpose
git branchList local branches
git branch -aList all branches
git branch NAMECreate a branch
git checkout NAMESwitch to a branch
git checkout -b NAMECreate and switch
git switch NAMESwitch (modern)
git switch -c NAMECreate and switch (modern)
git merge NAMEMerge into current
git branch -d NAMEDelete merged branch
git branch -D NAMEForce delete

HEAD

TermMeaning
HEADPointer to current commit
Attached HEADOn a branch
Detached HEADOn a specific commit
HEAD~1One commit back
HEAD~NN commits back

Branch Naming

PrefixPurpose
feature/New feature
fix/Bug fix
hotfix/Urgent production fix
release/Release prep
experiment/Experiment

Merge Types

TypeWhen
Fast-forwardTarget hasn’t changed
Three-wayBoth branches have commits
SquashCombine into one commit

Inspecting Branches

CommandShows
git log --oneline --graph --allVisual branch history
git branch -vBranch with last commit
git branch --mergedBranches merged into current
git branch --no-mergedUnmerged branches

Best Practices

✅ Do This:

# Create a branch per feature
git checkout -b feature/login                     # ✅

# Use descriptive names
git checkout -b fix/signup-validation             # ✅

# Merge back when done
git checkout main
git merge feature/login                           # ✅

# Delete merged branches
git branch -d feature/login                       # ✅

# Check status often
git status                                        # ✅

# Use --graph to see history
git log --oneline --graph --all                   # ✅

❌ Don’t Do This:

# Don't work directly on main
# Use a feature branch                             # ❌

# Don't use vague branch names
git checkout -b stuff                             # ❌

# Don't force-delete unmerged branches
git branch -D feature/important                   # ❌ data loss

# Don't forget to merge before deleting
git branch -d feature/menu                        # ⚠️  if unmerged

# Don't merge without testing
git merge feature/menu                            # ⚠️  run tests first

# Don't leave branches lying around
# Delete merged ones                                # ⚠️

Common Pitfalls

PitfallProblemSolution
Commit on wrong branchWork in the wrong placeCheck git status before committing
Forgot to switch backContinuing on old branchgit branch shows current
Merge conflictSame lines changedResolve, then commit
Detached HEADCommits not on a branchCreate a branch immediately
Deleting unmerged branchLost workUse -D only when sure
Too many stale branchesClutterDelete merged ones
Merging without testingBroken mainTest before merge

Real-World Examples

1. Create a branch

git checkout -b feature/login

Creates and switches in one command.

2. Switch branches

git checkout main

Moves HEAD to main.

3. Modern switch

git switch -c feature/login

Newer syntax, same effect.

4. List branches

git branch -a

Shows local and remote branches.

5. Merge a branch

git merge feature/login

Brings changes into current branch.

6. Delete a merged branch

git branch -d feature/login

Safe delete — refuses if unmerged.

7. Force delete

git branch -D feature/old

Deletes even if unmerged.

8. Visualize history

git log --oneline --graph --all

Shows branch structure.

9. Check what’s merged

git branch --merged

Lists branches already in main.

10. Find unmerged branches

git branch --no-merged

Lists branches not yet merged.


Visual: Branching and Merging

┌──────────────────────────────────────────────┐
│  main                                        │
│    │                                         │
│    A ──► B ──► C ──► D ──► F ──► G           │
│              │                    ▲          │
│              │                    │          │
│              └──► X ──► Y ──► Z ───┘          │
│                  feature/menu                │
│                                              │
│  Feature branches split off, then merge back │
│                                              │
└──────────────────────────────────────────────┘

Visual: HEAD

┌──────────────────────────────────────────────┐
│  HEAD points to a commit                     │
│                                              │
│  main                                        │
│    │                                         │
│    ▼                                         │
│  A ──► B ──► C                               │
│              ▲                               │
│              │                               │
│              HEAD                            │
│                                              │
│  When you switch branches, HEAD moves        │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  Detached HEAD                               │
│                                              │
│  main                                        │
│    │                                         │
│    ▼                                         │
│  A ──► B ──► C                               │
│         ▲                                    │
│         │                                    │
│         HEAD (detached)                      │
│                                              │
│  Commits made here aren't on any branch      │
│                                              │
└──────────────────────────────────────────────┘

Visual: Fast-Forward vs Three-Way Merge

┌──────────────────────────────────────────────┐
│  Fast-forward                                │
│                                              │
│  main:    A ──► B                            │
│  feature: A ──► B ──► C ──► D                │
│                                              │
│  After merge:                                │
│  main:    A ──► B ──► C ──► D                │
│                                              │
│  Main just moves forward — no merge commit   │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  Three-way merge                             │
│                                              │
│  main:    A ──► B ──► E ──► F                │
│  feature: A ──► B ──► C ──► D                │
│                                              │
│  After merge:                                │
│  main:    A ──► B ──► E ──► F ──► M          │
│                        │         ▲           │
│                        └──► C ──► D          │
│                                              │
│  A merge commit (M) is created               │
│                                              │
└──────────────────────────────────────────────┘

Visual: Common Branching Workflow

┌──────────────────────────────────────────────┐
│  main                                        │
│    │                                         │
│    ├──► feature/login                        │
│    │         │                               │
│    │         └──► merged back to main        │
│    │                                         │
│    ├──► fix/signup-bug                       │
│    │         │                               │
│    │         └──► merged back to main        │
│    │                                         │
│    └──► feature/dashboard                    │
│                  │                           │
│                  └──► merged back to main    │
│                                              │
│  One branch per task, merged when ready      │
│                                              │
└──────────────────────────────────────────────┘

Summary

ConceptMeaning
BranchIndependent line of development
MainStable, deployable branch
HEADPointer to current commit
Feature branchIsolated work for a feature
MergeCombine branches
Fast-forwardSimple move of a pointer
Three-way mergeCreates a merge commit
Detached HEADHEAD on a commit, not a branch

Key takeaways:

  • A branch is a movable pointer — not a copy of the codebase
  • Branches let you work on features, fixes, and experiments in isolation
  • Main (formerly master) is the stable, deployable branch
  • HEAD always points to your current commit — on a branch or detached
  • git checkout -b NAME creates a branch and switches to it
  • git switch -c NAME is the modern equivalent
  • git merge NAME brings another branch’s changes into the current one
  • Merges are fast-forward (simple pointer move) or three-way (merge commit)
  • Delete merged branches with -d; force-delete with -D
  • Use descriptive branch names — feature/, fix/, hotfix/
  • Use git log --oneline --graph --all to see the whole picture
  • Never work directly on main — always branch for changes

Remember: Branches are Git’s superpower. They’re cheap, fast, and safe. Create one per feature, isolate your work, merge when it’s tested and ready. Keep main stable. Delete branches when they’re done. And let --graph show you the shape of your history — it’s the fastest way to understand what happened.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!