Branches and Merging

Branches and Merging

Why branches?

Branches are one of the most powerful concepts in Git. They allow you to work on different features in parallel, without interfering with stable code.

main        ●───●───●───────────●───●
                     \         /
feature/login         ●───●───●

Each branch is simply a pointer to a commit. Creating a branch is nearly instantaneous.

Working with branches

Creating and navigating

# View branches
git branch                       # Local branches
git branch -a                    # All (local + remote)

# Create a branch
git branch feature/login

# Switch branches
git checkout feature/login
# Or (Git 2.23+)
git switch feature/login

# Create AND switch in a single command
git checkout -b feature/login
# Or
git switch -c feature/login

# Delete a branch
git branch -d feature/login      # Only if merged
git branch -D feature/login      # Force deletion

Naming conventions

feature/feature-name               # New feature
fix/bug-description                # Bug fix
hotfix/urgent-fix                  # Urgent production fix
refactor/refactoring-name          # Refactoring
docs/update-readme                 # Documentation

Merging

Classic merge

# Switch to the target branch
git checkout main

# Merge the feature branch
git merge feature/login

Case 1: Fast-forward

If main has not moved since the branch was created, Git simply moves the pointer forward:

Before:
main     ●───●
              \
feature        ●───●───●

After (fast-forward):
main     ●───●───●───●───●
# Force a merge commit (even in fast-forward)
git merge --no-ff feature/login

Case 2: Merge with commit

If main has evolved in parallel, Git creates a merge commit:

Before:
main     ●───●───●───●
              \
feature        ●───●

After:
main     ●───●───●───●───M
              \          /
feature        ●───●────

Resolving conflicts

A conflict occurs when two branches modify the same part of a file.

Anatomy of a conflict

<<<<<<< HEAD
const title = "Welcome";
=======
const title = "Hello!";
>>>>>>> feature/login
  • Between <<<<<<< HEAD and =======: the code from the current branch
  • Between ======= and >>>>>>> feature/login: the code from the incoming branch

Resolving a conflict

# 1. Git reports the conflict during the merge
git merge feature/login
# CONFLICT (content): Merge conflict in src/app.js

# 2. View the conflicting files
git status

# 3. Open the file and choose the correct version
# Remove the markers <<<<<<<, =======, >>>>>>> and keep the right code
const title = "Welcome!";

# 4. Mark as resolved and commit
git add src/app.js
git commit -m "Merge feature/login: resolve title conflict"

Aborting a merge in progress

# If the merge goes wrong, you can abort
git merge --abort

Rebase — rewriting history

Rebase is an alternative to merge that produces a linear history.

Merge vs Rebase

Merge:
main     ●───●───●───●───M
              \          /
feature        ●───●────

Rebase:
main     ●───●───●───●
                       \
feature (rebased)       ●'──●'
# Switch to the feature branch
git checkout feature/login

# Rebase onto main
git rebase main

# Then merge (fast-forward)
git checkout main
git merge feature/login

When to use rebase vs merge?

Situation Recommendation
Update a feature branch with the latest changes from main git rebase main
Integrate a completed feature into main git merge --no-ff feature
Branch shared with other developers Never rebase
Local personal branch Rebase OK

Golden rule: never rebase a branch that has been pushed and shared with others.

Cherry-pick — pick a specific commit

# Apply a specific commit from another branch
git cherry-pick abc1234

# Cherry-pick without committing immediately
git cherry-pick --no-commit abc1234

# Useful for:
# - Applying a hotfix across multiple branches
# - Recovering a commit from an abandoned branch

Common workflows

Git Flow

main        ●─────────────────●─────────────●
             \               / \           /
develop       ●───●───●───●────●───●───●──●
                   \     /          \   /
feature/a           ●───●            ● ●
                                    feature/b
  • main: production code
  • develop: feature integration
  • feature/*: feature development
  • release/*: release preparation
  • hotfix/*: urgent fixes

GitHub Flow (simpler)

main        ●───●───●───────●───●
                     \     /
feature/login         ●───●
                      ↑
                Pull Request + Review
  1. Create a branch from main
  2. Make commits
  3. Open a Pull Request
  4. Code review
  5. Merge into main

Best practices

  1. One branch per feature — keep branches short and focused
  2. Update regularly your feature branch from main
  3. Resolve conflicts early — the longer you wait, the harder it gets
  4. Delete merged branches to keep the repository clean
  5. Use --no-ff for important merges to preserve the history
  6. Never force push to main — use Pull Requests