|$ curl https://forge-ai.dev/api/markdown?path=docs/git/branching
$cat docs/git-—-branching-&-merging.md
updated Recently·50 min read·published

Git — Branching & Merging

GitVersion ControlIntermediate
Introduction

Branching is Git's most powerful feature. A branch is a lightweight, movable pointer to a specific commit. Creating, switching, and merging branches is nearly instantaneous because Git does not copy files — it only creates a 41-byte reference file.

This section covers branch management, merging strategies, conflict resolution, rebasing, stashing, and popular branching models like Git Flow and trunk-based development.

Branches — How They Work

A branch in Git is simply a named reference to a commit. The default branch is main (or historically master). When you make a new commit, the current branch pointer moves forward automatically.

terminal
Bash
1# List all local branches
2git branch
3
4# List all branches (including remote)
5git branch -a
6
7# Create a new branch (stays on current branch)
8git branch feature-login
9
10# Create and switch to new branch
11git checkout -b feature-login
12
13# Modern way (Git 2.23+)
14git switch -c feature-login
15
16# Switch to an existing branch
17git checkout main
18git switch main
19
20# Rename a branch
21git branch -m old-name new-name
22
23# Delete a fully merged branch
24git branch -d feature-login
25
26# Force delete (even if not merged)
27git branch -D feature-login

info

Use git switch (Git 2.23+) for branch operations and reserve git checkout for file-level operations. This separation makes intentions clearer — switch moves branches, checkout restores files.
Merging Branches

Merging combines the history of two branches. Git creates a merge commit that has two parent commits, preserving the complete history of both branches.

terminal
Bash
1# Merge feature into main
2git switch main
3git merge feature-login
4
5# This creates a merge commit if there are no conflicts.
6# Output:
7# Merge made by the 'ort' strategy.
8# 5 files changed, 120 insertions(+), 30 deletions(-)
9
10# Fast-forward merge (no merge commit needed)
11# Happens when main has not diverged since feature was created
12git merge feature-login
13# Updating a1b2c3d..e4f5g6h
14# Fast-forward
15
16# Force a merge commit even with fast-forward
17git merge --no-ff feature-login
18
19# Squash merge (combine all feature commits into one)
20git merge --squash feature-login
21git commit -m "Add login feature"
Merge TypeBehaviorUse Case
Fast-forwardLinear history, no merge commitSimple, no divergence
3-way mergeCreates merge commit with two parentsDivergent branches
--no-ffForces merge commit even when fast-forward possiblePreserve feature branch history
--squashCombines all commits into oneClean up messy history

best practice

Use --no-ff for feature branches. This creates an explicit merge commit that marks when a feature was integrated, making it easier to understand the project history and revert entire features if needed.
Conflict Resolution

Conflicts occur when two branches modify the same part of the same file. Git cannot automatically determine which change to keep and requires manual resolution.

terminal
Bash
1# When a merge has conflicts, Git shows:
2Auto-merging src/app.ts
3CONFLICT (content): Merge conflict in src/app.ts
4Automatic merge failed; fix conflicts and then commit the result.
5
6# The conflicted file contains conflict markers:
7const TIMEOUT = 5000;
8
9# Steps to resolve:
10# 1. Open the file and edit the conflicted region
11# 2. Remove the conflict markers
12# 3. Keep the correct code
13# 4. Stage the resolved file
14git add src/app.ts
15
16# 5. Complete the merge
17git commit
18
19# Abort the merge entirely
20git merge --abort
21
22# Use a merge tool
23git mergetool
🔥

pro tip

Use git diff during conflict resolution to see both sides clearly. For complex conflicts, use a visual merge tool like vscode (git config --global merge.tool vscode) or kdiff3. VS Code's built-in merge editor shows the three-way view (ours, theirs, and base).
Rebasing

Rebasing rewrites commit history by moving a branch's commits to a new base. Unlike merging (which creates a merge commit), rebasing produces a linear history. Rebase is useful for keeping feature branches up to date with main.

terminal
Bash
1# Rebase current branch onto main
2git switch feature-login
3git rebase main
4
5# Interactive rebase (edit, squash, reorder commits)
6git rebase -i HEAD~3
7
8# This opens an editor with:
9pick a1b2c3d Add login form
10pick e4f5g6h Add validation
11pick h7i8j9k Add API integration
12
13# Commands available in interactive rebase:
14# pick - use commit
15# reword - change commit message
16# edit - stop to amend
17# squash - combine with previous commit
18# fixup - combine, discard message
19# drop - remove commit
20
21# After resolving rebase conflicts:
22git add resolved-file.ts
23git rebase --continue
24
25# Skip a problematic commit
26git rebase --skip
27
28# Abort the rebase entirely
29git rebase --abort

warning

Never rebase commits that have been pushed to a shared branch (e.g., main, develop). Rebasing rewrites history — if someone else has based work on those commits, Git will create duplicate commits and cause chaos. The golden rule: only rebase local, unpublished commits.
Cherry-Picking

Cherry-picking applies a specific commit from one branch onto another. It is useful for selectively porting bug fixes without merging an entire branch.

terminal
Bash
1# Apply a single commit to the current branch
2git cherry-pick abc123
3
4# Cherry-pick a range of commits
5git cherry-pick abc123..def456
6
7# Cherry-pick with no automatic commit (edit first)
8git cherry-pick -n abc123
9
10# Cherry-pick multiple commits
11git cherry-pick abc123 def456 ghi789
12
13# Cherry-pick with a custom message
14git cherry-pick -x abc123
15# -x adds "cherry picked from commit ..." to the message

info

Cherry-picking creates a new commit with a different hash. The -x flag appends a reference to the original commit, which is helpful for tracing provenance. Use cherry-picks sparingly — frequent cherry-picking between branches may indicate a structural issue with your branching strategy.
Git Worktrees

Git worktrees allow you to check out multiple branches simultaneously in separate directories. This avoids the overhead of stashing or committing WIP code when you need to context-switch.

terminal
Bash
1# Create a new worktree for a branch
2git worktree add ../project-hotfix hotfix
3# This creates ../project-hotfix/ with the hotfix branch checked out
4
5# Create a worktree with a new branch
6git worktree add -b new-feature ../project-feature main
7
8# List all worktrees
9git worktree list
10
11# Remove a worktree
12git worktree remove ../project-hotfix
13
14# Prune stale worktree references
15git worktree prune
🔥

pro tip

Worktrees are ideal for reviewing pull requests, running tests on different branches, or working on a hotfix while keeping your main feature branch intact. Each worktree has its own working directory, staging area, and index — they share only the .git objects.
Branching Strategies

Branching strategies define how teams organize branches around releases, features, and bug fixes. The right strategy depends on your team size, release cadence, and CI/CD pipeline.

Git Flow

A comprehensive branching model with dedicated branches for features, releases, and hotfixes. Well-suited for projects with scheduled releases.

git-flow
Bash
1# Main branches (infinite lifetime):
2main # Production-ready code
3develop # Integration branch for features
4
5# Supporting branches (finite lifetime):
6feature/* # New features (branch off develop)
7release/* # Release preparation (branch off develop)
8hotfix/* # Urgent fixes (branch off main)
9
10# Workflow:
11git checkout -b feature/login develop
12# ... work on feature ...
13git checkout develop
14git merge --no-ff feature/login
15
16# For releases:
17git checkout -b release/1.2 develop
18# ... final tweaks ...
19git checkout main
20git merge --no-ff release/1.2
21git tag -a v1.2

info

Git Flow is powerful but heavyweight. It works best for teams doing versioned releases (libraries, mobile apps). For continuous deployment, consider trunk-based development instead.

Trunk-Based Development

A simpler model where developers make short-lived feature branches (1-2 days) off main and merge frequently. Integrates well with CI/CD and continuous deployment.

trunk-based
Bash
1# Principles:
2# - Short-lived branches (hours, not days)
3# - Frequent merges to main (multiple times daily)
4# - Feature flags for incomplete work
5# - Automated testing on every push
6
7# Workflow:
8git checkout -b add-search
9# ... work for a few hours ...
10git add . && git commit -m "Add search component"
11git push -u origin add-search
12# Create PR, get review, merge to main
13git checkout main
14git pull
15git branch -d add-search
16
17# Key practices:
18# - Keep branches under 1 day old
19# - Use feature flags (not branches) for incomplete features
20# - Run CI on every branch push
21# - Rebase daily to reduce merge conflicts

best practice

Trunk-based development reduces merge complexity and catches integration issues early. Combined with feature flags, it enables continuous delivery — every commit to main can be potentially releasable.

GitHub Flow

A simpler variant of trunk-based development popularized by GitHub. Everything branches from main, and all merges happen via pull requests.

github-flow
Bash
1# Rules:
2# 1. Anything in main is deployable
3# 2. Create descriptive branches off main
4# 3. Push to named branches frequently
5# 4. Open a PR early (even as draft)
6# 5. Get review, run CI checks
7# 6. Merge to main and deploy immediately
8
9# Branch naming convention:
10feat/add-user-auth
11fix/login-validation
12chore/update-deps
13docs/api-readme
14refactor/db-layer
Tagging

Tags mark specific points in history, typically used for releases (v1.0, v2.3.1). Unlike branches, tags do not move when new commits are added.

terminal
Bash
1# Create a lightweight tag
2git tag v1.0.0
3
4# Create an annotated tag (recommended)
5git tag -a v1.0.0 -m "Release version 1.0.0"
6
7# Tag a specific commit
8git tag -a v1.0.0 abc123 -m "Release 1.0.0"
9
10# List all tags
11git tag
12
13# Search for tags matching a pattern
14git tag -l "v2.*"
15
16# Show tag details
17git show v1.0.0
18
19# Push tags to remote
20git push origin v1.0.0
21
22# Push all tags
23git push --tags
24
25# Delete a local tag
26git tag -d v1.0.0
27
28# Delete a remote tag
29git push origin --delete v1.0.0
🔥

pro tip

Always use annotated tags (-a) for releases. They store the tagger name, email, date, and a message. Lightweight tags are fine for private pointers but lack the metadata needed for release tracking.
Advanced Branch Operations

These commands handle edge cases in branch management, from detached HEAD states to complex ref log operations.

terminal
Bash
1# Detached HEAD state — investigate old commits
2git checkout abc123
3# Warning: you are in 'detached HEAD' state.
4# Create a branch if you want to keep changes:
5git switch -c investigation-branch
6
7# Find branches containing a specific commit
8git branch --contains abc123
9
10# List merged branches (safe to delete)
11git branch --merged main
12
13# List unmerged branches
14git branch --no-merged
15
16# Show upstream tracking info
17git branch -vv
18
19# Compare branches (commits in A but not B)
20git log main..feature-login --oneline
21git log feature-login..main --oneline
22
23# Recover a deleted branch (if you have the hash)
24git reflog
25# Output: abc123 HEAD@{0}: checkout: moving from main to feature-x
26git checkout -b recovered-branch abc123

warning

The reflog is your safety net. It records every movement of HEAD and branch pointers. If you accidentally delete a branch or reset too far, use git reflog to find the commit hash and recover it. Reflog entries expire after 90 days by default.
Common Pitfalls

✗ Rebasing public branches

git rebase main (on shared branch)

Rewriting pushed commits causes duplicate commits for other developers. Only rebase local, unpublished work.

✗ Merge commit pollution

git pull (without --rebase)

git pull creates a merge commit by default. Use git pull --rebase to keep a linear history, or set git config --global pull.rebase true.

✗ Long-lived feature branches

git checkout -b feature-huge (2 weeks ago)

Branches that live for weeks accumulate massive merge conflicts. Merge or rebase daily to stay in sync. Use feature flags instead of long-lived branches.

$Blueprint — Engineering Documentation·Section ID: GIT-02·Revision: 1.0