What Are Branches?#
A branch is an independent line of development. When you create a branch, you get a copy of the codebase where you can make changes without affecting the main branch. When the work is ready, you merge it back.
main: A---B---C
\
feature: D---E---FBranches let you:
- Work on a feature without breaking the stable code
- Have multiple people working on different things simultaneously
- Experiment freely — throw the branch away if it doesn’t work out
Viewing Branches#
# List local branches (* marks current)
git branch
# List all branches (including remote)
git branch -a
# Show which branch you're on
git branch --show-current
# List branches with last commit info
git branch -vCreating Branches#
# Create a new branch
git branch feature/login
# Create and switch to it immediately
git checkout -b feature/login
# Modern alternative (Git 2.23+)
git switch -c feature/loginThe branch starts from wherever you are (usually the latest commit on main).
Branch from a specific point#
# Branch from a tag
git checkout -b hotfix/v1.2.1 v1.2.0
# Branch from a specific commit
git checkout -b recovery abc1234Switching Branches#
# Classic
git checkout feature/login
# Modern (Git 2.23+)
git switch feature/login
# Switch back to previous branch
git checkout -
git switch -Important: Switching branches changes the files in your working directory. Uncommitted changes come with you if they don’t conflict — otherwise Git will refuse to switch until you commit or stash them.
Branch Naming Conventions#
Good branch names are descriptive and categorized:
| Pattern | Use for | Example |
|---|---|---|
feature/ | New functionality | feature/user-auth |
fix/ | Bug fixes | fix/login-timeout |
hotfix/ | Urgent production fixes | hotfix/security-patch |
refactor/ | Code restructuring | refactor/database-layer |
docs/ | Documentation changes | docs/api-readme |
Keep names lowercase, use hyphens not spaces, and be specific:
# Good
git checkout -b feature/add-password-reset
git checkout -b fix/null-pointer-on-empty-input
# Bad
git checkout -b stuff
git checkout -b my-branch
git checkout -b testMerging#
Merging combines the work from one branch into another.
Basic merge#
# Switch to the branch you want to merge INTO
git checkout main
# Merge the feature branch into main
git merge feature/loginFast-forward merge#
When the target branch hasn’t changed since you branched off, Git just moves the pointer forward:
Before:
main: A---B---C
\
feature: D---E
After (git merge feature):
main: A---B---C---D---ENo merge commit is created — the history stays linear.
Three-way merge#
When both branches have new commits, Git creates a merge commit:
Before:
main: A---B---C---G---H
\
feature: D---E---F
After (git merge feature):
main: A---B---C---G---H---M
\ /
feature: D---E---FM is the merge commit that ties the two histories together.
No-fast-forward merge#
Force a merge commit even when fast-forward is possible — preserves branch history:
git merge --no-ff feature/loginThis is useful when you want the branch grouping visible in history.
Merge Conflicts#
Conflicts happen when both branches modified the same part of the same file. Git can’t decide which version to keep, so it asks you.
What a conflict looks like#
git merge feature/login
# CONFLICT (content): Merge conflict in src/auth.js
# Automatic merge failed; fix conflicts and then commit the result.The conflicted file contains markers:
function getUser(id) {
<<<<<<< HEAD
return database.findById(id);
=======
return cache.get(id) || database.findById(id);
>>>>>>> feature/login
}| Marker | Meaning |
|---|---|
<<<<<<< HEAD | Start of your current branch’s version |
======= | Divider between the two versions |
>>>>>>> feature/login | End of the incoming branch’s version |
Resolving conflicts#
- Open the file and decide what the final version should be
- Remove the conflict markers
- Stage the resolved file
- Commit
// Resolved — keeping the improved version
function getUser(id) {
return cache.get(id) || database.findById(id);
}git add src/auth.js
git commit -m "Merge feature/login: resolve auth.js conflict"Abort a merge#
If you want to back out entirely:
git merge --abortThis resets everything to before you ran git merge.
Check for conflicts before merging#
# Dry run — see if it would conflict
git merge --no-commit --no-ff feature/login
git merge --abort # undo the testDeleting Branches#
After merging, clean up:
# Delete a merged branch
git branch -d feature/login
# Force delete an unmerged branch (careful — loses work)
git branch -D feature/experimentDelete a remote branch#
git push origin --delete feature/loginRemote Branches#
Push a branch to remote#
# First push — set up tracking
git push -u origin feature/login
# Subsequent pushes
git pushTrack a remote branch#
# Fetch all remote branches
git fetch
# Create a local branch tracking a remote one
git checkout -b feature/login origin/feature/login
# Or the shorthand (if branch name matches)
git checkout feature/loginSee what remote branches exist#
git branch -rKeeping Branches Up to Date#
Your feature branch may fall behind main while you work. Sync it:
Merge main into your branch#
git checkout feature/login
git merge mainThis creates a merge commit in your feature branch.
Rebase onto main (alternative)#
git checkout feature/login
git rebase mainThis replays your commits on top of the latest main — keeps history linear but rewrites commits. Don’t rebase branches that others are working on.
Practical Workflows#
Feature branch workflow#
# Start a feature
git checkout main
git pull
git checkout -b feature/shopping-cart
# Work on it
# ... edit files ...
git add .
git commit -m "Add cart item model"
# ... more work ...
git commit -m "Add cart API endpoints"
# Keep up with main
git merge main
# Push for review
git push -u origin feature/shopping-cart
# After review, merge to main
git checkout main
git merge feature/shopping-cart
git push
git branch -d feature/shopping-cartHotfix workflow#
# Branch from main (or the release tag)
git checkout -b hotfix/fix-payment-crash main
# Make the fix
git commit -am "Fix null pointer in payment processing"
# Merge back to main
git checkout main
git merge hotfix/fix-payment-crash
git push
# Clean up
git branch -d hotfix/fix-payment-crashExploring an idea#
# Try something risky
git checkout -b experiment/new-algorithm
# ... work on it ...
# Didn't work out — throw it away
git checkout main
git branch -D experiment/new-algorithmViewing Branch History#
# Graph showing all branches
git log --oneline --graph --all
# Commits on feature that aren't on main
git log main..feature/login --oneline
# Commits on main that aren't in feature (what you're missing)
git log feature/login..main --oneline
# Which branches contain a specific commit
git branch --contains abc1234
# Which branches are merged into main
git branch --merged main
# Which branches are NOT merged into main
git branch --no-merged mainQuick Reference#
| Task | Command |
|---|---|
| Create branch | git checkout -b name |
| Switch branch | git checkout name or git switch name |
| List branches | git branch |
| Merge branch | git merge name |
| Delete branch | git branch -d name |
| Push branch | git push -u origin name |
| Delete remote branch | git push origin --delete name |
| Abort merge | git merge --abort |
| Switch to previous | git checkout - |
| Show graph | git log --oneline --graph --all |
Best Practices#
- Keep branches short-lived — merge or delete within days, not weeks
- Pull main often into your feature branch to avoid painful merge conflicts later
- Use descriptive names with category prefixes (
feature/,fix/,docs/) - Delete branches after merging — stale branches clutter the repo
- Don’t commit directly to main — use branches even for small changes on shared repos
- If a merge conflict scares you, use
git merge --abortto reset and think about it - Use
git log --graphto visualize what happened — it’s the fastest way to understand complex branch history - Prefer
--no-ffmerges on shared repos so branch history is preserved in the log


