Table of Contents Link to heading
- What Branches Are
- Branch Operations
- Merging
- Rebasing
- Cherry-Picking
- Remote Branches
- Branching Strategies
What Branches Are Link to heading
A Git branch is a lightweight, movable pointer to a commit. The default branch is conventionally named main (or historically master). When you create a new branch, Git creates a new pointer — it does not copy the repository. The HEAD pointer tracks which branch you are currently working on.
This design makes branching nearly instantaneous and cheap regardless of repository size, which is why branching is central to Git workflows in a way it was not with older version control systems.
main: A --- B --- C
\
feature: D --- E
C is the common ancestor of main and feature. Both branches can evolve independently and be merged when ready.
Branch Operations Link to heading
Creating and Switching Branches Link to heading
# Create a new branch (does not switch to it)
git branch feature/login-page
# Create and switch in one command
git checkout -b feature/login-page
git switch -c feature/login-page # modern syntax (Git 2.23+)
# Switch to an existing branch
git checkout main
git switch main # modern syntax
# Create a branch from a specific commit or tag
git checkout -b hotfix/cve-2024-1234 v2.3.1
feature/, bugfix/, hotfix/, release/. This makes the purpose of each branch visible in branch listings and in pull request titles without needing to open the branch.Viewing Branches Link to heading
git branch # list local branches
git branch -a # list local and remote branches
git branch -v # with last commit message per branch
git branch -vv # with upstream tracking branch
git branch --merged # branches fully merged into current
git branch --no-merged # branches with unmerged commits
Deleting Branches Link to heading
# Delete a fully merged branch
git branch -d feature/login-page
# Force delete an unmerged branch
git branch -D feature/abandoned
# Delete a remote branch
git push origin --delete feature/login-page
Merging Link to heading
Fast-Forward Merge Link to heading
When the target branch has not diverged from the source — i.e., the target branch is directly behind the source in the commit history — Git moves the branch pointer forward without creating a merge commit:
git checkout main
git merge feature/login-page # fast-forward if no divergence
Before: main: A --- B
\
feature: C --- D
After: main: A --- B --- C --- D
Use --no-ff to force a merge commit even when fast-forward is possible — this preserves the visual history of which commits were part of a feature branch:
git merge --no-ff feature/login-page
Three-Way Merge Link to heading
When both branches have diverged (each has commits the other does not), Git performs a three-way merge using the two branch tips and their common ancestor. This produces a merge commit with two parent pointers:
git checkout main
git merge feature/login-page
Before: main: A --- B --- C
\
feature: D --- E
After: main: A --- B --- C --- M
\ /
D --- E
M is the merge commit. Its two parents are C (last commit on main) and E (last commit on feature).
Resolving Merge Conflicts Link to heading
A conflict occurs when both branches modify the same lines of the same file. Git marks the conflict in the file and halts the merge:
<<<<<<< HEAD
return "login successful"
=======
return "authentication complete"
>>>>>>> feature/login-page
Resolution workflow:
# See which files have conflicts
git status
# Open each conflicted file, resolve the conflict markers, save
# Then mark as resolved:
git add path/to/resolved-file.py
# Complete the merge
git commit
git mergetool opens the configured three-pane diff editor (vimdiff, VS Code, IntelliJ). Configure your preferred tool with git config --global merge.tool vscode.To abandon a merge in progress and return to the pre-merge state:
git merge --abort
Rebasing Link to heading
Rebase replays commits from one branch on top of another, rewriting commit history to produce a linear sequence. The result is cleaner history than a three-way merge — but rebase rewrites commits (new SHAs), so it should only be used on private/local branches that have not been pushed to a shared remote.
# Rebase feature onto main (replay feature commits on top of current main)
git checkout feature/login-page
git rebase main
# Then fast-forward main
git checkout main
git merge feature/login-page # fast-forward only
Before: main: A --- B --- C
\
feature: D --- E
After: main: A --- B --- C --- D' --- E'
D' and E' are new commits with the same changes as D and E but different parent commits and different SHAs.
Interactive Rebase Link to heading
Interactive rebase allows editing, squashing, reordering, or dropping commits before integrating:
# Interactively rebase the last 4 commits
git rebase -i HEAD~4
# Or rebase all commits since branching from main
git rebase -i main
In the editor, change pick to:
squash(ors): combine this commit with the previous onereword(orr): keep the commit but edit its messageedit(ore): pause to amend the commitdrop(ord): remove the commit entirely
Cherry-Picking Link to heading
Cherry-pick applies the changes from a specific commit onto the current branch, without merging the entire source branch:
# Apply a single commit from another branch
git cherry-pick a3f5c9d
# Apply multiple commits
git cherry-pick a3f5c9d b4e6d1f
# Apply a range of commits
git cherry-pick a3f5c9d^..b4e6d1f
# Cherry-pick without committing (stage changes only)
git cherry-pick --no-commit a3f5c9d
Cherry-pick is useful for backporting a specific fix from main to a release branch without bringing along all commits that followed the fix.
Remote Branches Link to heading
Remote branches are read-only references that track the state of branches on a remote repository. They are updated when you fetch or pull.
# Fetch all remote branches (update remote refs, do not merge)
git fetch origin
# Fetch a specific branch
git fetch origin feature/login-page
# Create a local tracking branch from a remote branch
git checkout --track origin/feature/login-page
git switch --track origin/feature/login-page # modern syntax
# Push a local branch to remote and set upstream tracking
git push -u origin feature/login-page
# See remote branch status vs local
git branch -vv
Branching Strategies Link to heading
GitFlow Link to heading
GitFlow defines a strict branching model with long-lived branches and clear naming:
| Branch | Purpose |
|---|---|
main |
Production-ready releases only; tagged with version numbers |
develop |
Integration branch; next release is assembled here |
feature/* |
Individual features; branch from develop, merge back to develop |
release/* |
Release preparation; branch from develop, merge to main AND develop |
hotfix/* |
Urgent production fixes; branch from main, merge to main AND develop |
GitFlow is well-suited to projects with scheduled release cycles and multiple supported versions in production.
Trunk-Based Development Link to heading
In trunk-based development, all engineers commit directly to main (the “trunk”) or use very short-lived feature branches (< 1 day). Feature flags control which features are active in production, decoupling code deployment from feature release.
Key practices:
- Commits are small, frequent, and always integrated into main
- CI runs on every commit; the build must always pass
- No long-lived feature branches — reduces merge complexity and integration risk
- Feature flags enable incomplete work to be deployed safely
Trunk-based development is the model used at high-deployment-frequency organisations (Google, Meta, Netflix) and is well-suited to continuous delivery pipelines.