Branching is Git's most powerful feature. You can experiment with new features or fix bugs without touching the main code. Once complete, merge them together. Flexible handling of branches allows you to work on multiple ideas simultaneously, even when developing alone.
Learning Objectives
- You can create and switch branches with
git branch·git switch. - Can explain the difference between fast-forward merge and 3-way merge.
- When a conflict occurs, you can directly correct and resolve it.
- Branches merged with
git branch -dcan be safely deleted.
Key concepts
1) Creating/Converting Branch
git branch feature/login #Create branch
git switch feature/login #Branch Switch
#Create + Convert at once
git switch -c feature/login
#Check list
git branch #local
git branch -a #local + remote
2) Fast-forward Merge
main: A---B
\
feature: C---D
git merge feature → main: A---B---C---D (straight line)
mainjust moves forward (no branching points).- No merge commits are created, so the history is clean.
git switch main
git merge feature/login #fast-forward (default)
git merge --no-ff feature/login #Force creation of merge commit
3) 3-way Merge
main: A---B---E
\ \
feature: C---D
git merge feature → main: A---B---E---M (M = merge commit)
- When both
mainandfeaturehave made their own commits. - Compare and merge the common ancestor (B) + the ends of the two branches (E, D).
4) Conflict resolution
#When a conflict occurs, git status displays the conflict file.
git status
# both modified: src/app.py
#Conflict markers are inserted when opening a file
<<<<<<< HEAD
Contents of the current branch
=======
Contents of the branch to be merged
>>>>>>> feature/login
#1. Remove the marker and leave the desired content
#2. git add <file>
# 3. git commit
See as example
Example 1 — ex01_branch.sh: Branch creation/conversion/list confirmation
#!/usr/bin/env bash
REPO=$(mktemp -d); cd "$REPO"
git init -q && git config user.name "For practice" && git config user.email "[email protected]"
echo "main" > main.txt && git add . && git commit -q -m "init"
git switch -c feature/hello
echo "hello" > hello.txt && git add . && git commit -q -m "feat: hello"
echo "=== Branch List ==="
git branch
git switch main
echo "=== HEAD POSITION ==="
git log --oneline --all --graph
Execution result
=== Branch List ===
* feature/hello
main
=== HEAD POSITION ===
* a1b2c3d (feature/hello) feat: hello
* b2c3d4e (HEAD -> main) init
Bottom line: * points to the currently checked out branch.
Example 2 — ex02_merge_ff.sh: Fast-forward Merge
#!/usr/bin/env bash
REPO=$(mktemp -d); cd "$REPO"
git init -q && git config user.name "For practice" && git config user.email "[email protected]"
echo "init" > README.md && git add . && git commit -q -m "init"
git switch -c feature/ff
echo "FF function" > feature.txt && git add . && git commit -q -m "feat: FF feature"
git switch main
git merge feature/ff
echo "=== Log after merge (straight line) ==="
git log --oneline --graph
Execution result
=== Log after merge (straight line) ===
* a1b2c3d (HEAD -> main, feature/ff) feat: FF feature
* b2c3d4e init
Bottom line: fast-forward only moves the pointer without a merge commit.
Example 3 — ex03_merge_3way.sh: 3-way Merge
#!/usr/bin/env bash
REPO=$(mktemp -d); cd "$REPO"
git init -q && git config user.name "For practice" && git config user.email "[email protected]"
echo "init" > README.md && git add . && git commit -q -m "init"
git switch -c feature/3way
echo "Function A" > feature.txt && git add . && git commit -q -m "feat: feature A"
git switch main
echo "main Additional actions" > main_extra.txt && git add . && git commit -q -m "chore: main extra"
git merge feature/3way -m "merge: feature/3way into main"
echo "=== Log after merge (with merge commit) ==="
git log --oneline --graph
Execution result
=== Log after merge (with merge commit) ===
* c3d4e5f (HEAD -> main) merge: feature/3way into main
|\
| * a1b2c3d (feature/3way) feat: feature A
* | b2c3d4e chore: main extra
|/
* 9a8b7c6 init
Bottom line: Git automatically creates a merge commit when both parties commit separately.
Example 4 — ex04_conflict.sh: Creating and resolving conflicts
#!/usr/bin/env bash
REPO=$(mktemp -d); cd "$REPO"
git init -q && git config user.name "For practice" && git config user.email "[email protected]"
echo "Common content" > shared.txt && git add . && git commit -q -m "init"
git switch -c feature/conflict
echo "Modification of feature" > shared.txt && git add . && git commit -q -m "feat: modify shared"
git switch main
echo "Modification of main" > shared.txt && git add . && git commit -q -m "chore: modify shared on main"
echo "=== Attempt to merge conflicts ==="
if ! git merge feature/conflict -m "merge" 2>/dev/null; then
echo "A crash occurred! shared.txt contents:"
cat shared.txt
#Conflict resolution: adopt main version
echo "Conflict resolution: adopt main version" > shared.txt
git add shared.txt
git commit -m "merge: resolve conflict in shared.txt"
echo ""
echo "=== Log after conflict resolution ==="
git log --oneline --graph
fi
rm -rf "$REPO"
Execution result
A crash occurred! shared.txt contents:
<<<<<<< HEAD
Modification of main
=======
Modification of feature
>>>>>>> feature/conflict
=== Log after conflict resolution ===
* d4e5f6a (HEAD -> main) merge: resolve conflict in shared.txt
|\
| * c3d4e5f (feature/conflict) feat: modify shared
* | b2c3d4e chore: modify shared on main
|/
* a1b2c3d init
Key point: Edit the conflict marker directly and complete the merge from git add → git commit.
Seeing from a different perspective
| Git concepts | Everyday metaphor |
|---|---|
| branch | parallel universe |
| merge | Merging two universes into one |
| fast-forward | Fast forward the timeline |
| conflict | Same object modified differently in two universes |
| merge commit | The intersection where two universes meet |
Common Mistakes
- Work directly from main — Always create and work on feature branches.
- Commit with conflict markers left in the file — If
<<<,===, and>>>remain, the build will be broken. - Delete unmerged branch as
-Dinstead of-d— Commit will be lost. - Omit
git pullbefore merging — Remote changes are not imported, resulting in larger conflicts. - Omitted when fast-forward must be forced (
--no-ff) — PR history disappears, making tracking difficult.
Cleanup
- Create and switch branches with
git switch -c <branch>. - fast-forward creates a straight history, and 3-way creates a merge commit.
- Conflicts are resolved in the following order: Edit marker →
git add→git commit. - The merged branches are organized as
git branch -d.
Try it yourself
cd 03_Merge_with_branch/src
chmod +x *.sh
./ex01_branch.sh
./ex02_merge_ff.sh
./ex03_merge_3way.sh
./ex04_conflict.sh
Application:
- Compare how the graph changes before and after merging with
git log --oneline --graph --all. - Use
git merge --abortto create a scenario to abort conflict merging.