Branches and Merging¶
Branching is where Git's design pays off. In older systems like SVN, creating a branch meant copying the entire directory tree - it was slow, expensive, and merging was painful enough that teams avoided branches. In Git, a branch is a 41-byte file containing a commit hash. Creating one is instant. Merging is a first-class operation. This changes how you work: branches become disposable tools for isolating work, not heavyweight decisions.
What Is a Branch?¶
A branch in Git is a movable pointer to a commit. That's it. The file .git/refs/heads/main contains the 40-character SHA-1 hash of the commit that main currently points to. When you make a new commit on main, Git updates that pointer to the new commit.
When you create a new branch, Git creates a new pointer at the current commit. Both branches now point to the same commit - no files are copied:
As you make commits on each branch, their pointers diverge:
HEAD points to whichever branch you currently have checked out. It's how Git knows which branch to advance when you commit.
Creating Branches¶
# Create a new branch at the current commit
git branch feature/search
# Create and switch to it in one step
git switch -c feature/search
# Create a branch at a specific commit
git branch hotfix/login a1b2c3d
# Create a branch from another branch
git branch feature/search-v2 feature/search
Listing Branches¶
# List local branches (* marks current)
git branch
# List all branches (including remote-tracking)
git branch -a
# List branches with last commit info
git branch -v
# List branches merged into current branch
git branch --merged
# List branches NOT merged into current branch
git branch --no-merged
Switching Branches: git switch vs git checkout¶
Git 2.23 introduced git switch as a clearer alternative to git checkout for changing branches:
# Modern (Git 2.23+)
git switch main
git switch feature/auth
git switch -c new-branch # Create and switch
# Legacy (still works)
git checkout main
git checkout feature/auth
git checkout -b new-branch # Create and switch
git checkout is overloaded - it switches branches, restores files, creates branches, and detaches HEAD. git switch does one thing: switch branches. Use git switch for branch operations and git restore for file operations.
Uncommitted changes and switching
If you have uncommitted changes that would conflict with the branch you're switching to, Git refuses the switch to prevent data loss. You have three options: commit the changes, stash them (git stash), or discard them (git restore .).
Renaming and Deleting Branches¶
# Rename current branch
git branch -m new-name
# Rename a specific branch
git branch -m old-name new-name
# Delete a branch (only if fully merged)
git branch -d feature/old
# Force delete (even if not merged - you'll lose unmerged commits)
git branch -D feature/abandoned
Recovering a deleted branch
Deleted a branch by accident? The commits still exist in the object database. Use git reflog to find the commit hash, then recreate the branch: git branch recovered-branch a1b2c3d. The Rewriting History guide covers the reflog in depth.
Merging¶
Merging combines the work from two branches. When you run git merge, Git takes the commits from one branch and integrates them into the current branch.
Fast-Forward Merge¶
A fast-forward merge happens when the target branch has no new commits since the source branch diverged. Git simply moves the branch pointer forward - no new commit is created:
Before:
After:
Git moved main forward to point at D. The history is linear - no merge commit.
Three-Way Merge¶
A three-way merge happens when both branches have new commits since they diverged. Git finds their common ancestor, compares both branches against it, and creates a new merge commit with two parents:
Before:
After:
M is the merge commit. It has two parents: E (from main) and D (from feature). The merge commit records that these two lines of development were combined.
gitGraph
commit id: "A"
commit id: "B"
branch feature
commit id: "C"
commit id: "D"
checkout main
commit id: "E"
merge feature id: "M"
Forcing a Merge Commit¶
Sometimes you want a merge commit even when a fast-forward is possible, to preserve the record that a branch existed:
This is common in workflows where you want the history to show that features were developed on separate branches, even if the history could be linear.
Merge Conflicts¶
A merge conflict happens when both branches modified the same part of the same file. Git can automatically merge changes to different files or different parts of the same file, but when two branches change the same lines, Git can't decide which version to keep.
What Causes Conflicts¶
- Both branches edited the same line(s) of the same file
- One branch deleted a file that the other modified
- Both branches added a file with the same name but different content
Reading Conflict Markers¶
When a conflict occurs, Git marks the conflicting sections in the file:
<<<<<<< HEADto=======: your current branch's version=======to>>>>>>>: the incoming branch's version
With merge.conflictstyle = diff3 configured, Git also shows the original (base) version:
<<<<<<< HEAD
const timeout = 3000;
||||||| merged common ancestors
const timeout = 1000;
=======
const timeout = 5000;
>>>>>>> feature/timeout-update
This third section (the base) is invaluable - it shows what the code looked like before either change, making it much easier to understand the intent of both modifications.
Resolution Process¶
- Run
git mergeand see the conflict - Open the conflicted file(s)
- Edit to resolve - keep one version, combine both, or write something new
- Remove all conflict markers (
<<<<<<<,=======,>>>>>>>) - Stage the resolved file(s) with
git add - Complete the merge with
git commit
git merge feature/timeout-update
# CONFLICT: Merge conflict in config.js
# Edit config.js to resolve the conflict
# ... make your edits ...
git add config.js
git commit # Opens editor with pre-populated merge commit message
To abort a merge in progress and return to the pre-merge state:
Branch Management Practices¶
Naming Conventions¶
Most teams use prefixed branch names to categorize work:
| Prefix | Purpose | Example |
|---|---|---|
feature/ |
New functionality | feature/user-search |
bugfix/ or fix/ |
Bug fixes | bugfix/login-timeout |
hotfix/ |
Urgent production fixes | hotfix/security-patch |
release/ |
Release preparation | release/2.1.0 |
chore/ |
Maintenance tasks | chore/upgrade-deps |
Keep branch names lowercase, use hyphens for spaces, and include a ticket number if your team uses issue trackers: feature/PROJ-123-user-search.
Short-Lived Branches¶
Branches work best when they're short-lived. A branch that lives for months accumulates merge conflicts and diverges from the mainline. Aim for branches that last days, not weeks. If a feature is large, break it into smaller branches that merge incrementally.
Exercises¶
Further Reading¶
- Pro Git - Chapter 3: Git Branching - comprehensive coverage of branching and merging
- Official git-merge documentation - complete reference for merge strategies and options
- Official git-switch documentation - the modern branch-switching command
- Official git-branch documentation - creating, listing, renaming, and deleting branches
Previous: Commits and History | Next: Remote Repositories | Back to Index