Featured image

Table of Contents Link to heading

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
Tip
Use a naming convention that encodes the branch type and a brief description: 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
Tip
Use a merge tool for complex conflicts: 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.

Warning
Never rebase commits that have been pushed to a shared remote branch. When others have based work on those commits, rewriting them creates divergence that forces them to resolve conflicts unnecessarily. Rebase is for cleaning local history before sharing; merge is for integrating shared history.

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 (or s): combine this commit with the previous one
  • reword (or r): keep the commit but edit its message
  • edit (or e): pause to amend the commit
  • drop (or d): 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.

Note
The right branching strategy depends on team size, deployment frequency, and release model. GitFlow works well for versioned software with infrequent releases; trunk-based development works well for web services with continuous deployment. Mixing both approaches — e.g., short-lived feature branches merged via pull request with immediate CI and deployment to staging — is the most common enterprise hybrid.