Practical Exercise: Simulate a Multi-Developer Merge Conflict and Resolve It
This capstone exercise brings together everything from Module 3 into one realistic, hands-on scenario: two 'developers' (played by you, using two separate branches) independently edit the exact same line of a shared file, guaranteeing a genuine three-way merge conflict. You'll create the conflict deliberately, read and understand it, resolve it correctly, and clean up afterward — reproducing, in miniature, one of the most common real-world situations in collaborative software development.
Learning Objectives
- Deliberately create two diverging branches that edit the same content, guaranteeing a merge conflict.
- Read and correctly interpret the resulting conflict markers.
- Resolve the conflict manually and complete the merge with a proper commit.
- Clean up the simulated feature branch, both locally and (conceptually) on a remote.
Key Terms to Know Before Learning Practical Exercise
- Simulated conflict: A merge conflict deliberately reproduced for learning purposes, by intentionally editing the same content differently on two branches.
- Divergent edit: A change made independently on one branch to content that has also been changed differently on another branch since their common ancestor.
- Resolution commit: The commit that finalizes a merge after a conflict has been manually resolved, typically using Git's pre-populated merge commit message.
How Practical Exercise Actually Works
**Step 1: Set up the shared starting point.** Create a new repository (or use an existing one) with a file `greeting.js` containing one shared line, and commit it to `main`:
```
echo 'const greeting = "Hello, team!";' > greeting.js
git add greeting.js
git commit -m "feat: add initial greeting message"
```
**Step 2: Create 'Developer A's' branch and make a change.** Simulate the first developer starting work:
```
git switch -c feature/formal-greeting
```
Edit `greeting.js` so the line reads:
```
const greeting = "Good day, esteemed colleagues.";
```
Then commit:
```
git add greeting.js
git commit -m "feat: make greeting more formal"
```
**Step 3: Return to main and create 'Developer B's' branch, independently changing the same line.** Simulate a second developer who never saw Developer A's change:
```
git switch main
git switch -c feature/casual-greeting
```
Edit the *same line* in `greeting.js` differently:
```
const greeting = "Hey there, folks!";
```
Then commit:
```
git add greeting.js
git commit -m "feat: make greeting more casual"
```
**Step 4: Merge the first branch into main (this one will fast-forward or merge cleanly).**
```
git switch main
git merge feature/formal-greeting
```
Since `main` hadn't changed since `feature/formal-greeting` was created, this is a fast-forward merge (Lesson 4) — no conflict yet.
**Step 5: Merge the second branch — this is where the conflict occurs.** Now `main` has diverged (it has Developer A's commit), and `feature/casual-greeting` also diverged independently from the same original point:
```
git merge feature/casual-greeting
# CONFLICT (content): Merge conflict in greeting.js
```
This is a genuine three-way merge conflict (Lesson 5), since both branches changed the exact same line differently since their shared ancestor.
**Step 6: Investigate before resolving.** Use tools from earlier in this module to understand the conflict fully:
```
git status
git log --merge --oneline
```
**Step 7: Resolve the conflict manually.** Open `greeting.js`, see the conflict markers, and decide on a final combined resolution:
```
const greeting = "Hello team, good day and welcome!";
```
Remove all marker lines, then:
```
git add greeting.js
git commit
```
**Step 8: Clean up.** Delete both simulated feature branches, since their work is now fully merged:
```
git branch -d feature/formal-greeting
git branch -d feature/casual-greeting
```
**Step 9: Review the final history.**
```
git log --oneline --graph --decorate --all
```
This should clearly show the fast-forwarded first merge, followed by a distinct merge commit (with two parents) representing the resolved conflict — a complete, realistic record of exactly the kind of situation two real developers encounter regularly.
Practical Exercise: Visual Walkthrough
Draw a commit graph: main starts at C1 ('initial greeting'). Branch 'feature/formal-greeting' splits from C1, adds C2 ('formal greeting'). Branch 'feature/casual-greeting' ALSO splits from C1 (independently), adds C3 ('casual greeting'). Show 'git merge feature/formal-greeting' fast-forwarding main's pointer to C2 (no new commit). Then show 'git merge feature/casual-greeting' creating a NEW merge commit C4 with two parent arrows pointing back to C2 (main) and C3 (feature/casual-greeting), labeled 'CONFLICT — both changed the same line since C1'. Caption C4: 'Resolution commit — combined greeting.'
Practical Exercise: Quick Reference Table
| Step | Command(s) | Result |
|---|---|---|
| 1. Shared starting point | Create greeting.js, commit to main | Common ancestor commit (C1) established |
| 2. 'Developer A' branch | git switch -c feature/formal-greeting, edit + commit | C2: formal greeting, diverging from C1 |
| 3. 'Developer B' branch | git switch main, git switch -c feature/casual-greeting, edit + commit | C3: casual greeting, ALSO diverging from C1 independently |
| 4. First merge | git switch main, git merge feature/formal-greeting | Fast-forward — main now at C2, no conflict |
| 5. Second merge (conflict) | git merge feature/casual-greeting | CONFLICT — both C2 and C3 changed the same line since C1 |
| 6. Investigate | git status, git log --merge --oneline | Confirm which file(s) conflict and see relevant commits from both sides |
| 7. Resolve | Edit greeting.js, remove markers, git add, git commit | New merge commit (C4) with two parents, conflict resolved |
| 8. Clean up | git branch -d feature/formal-greeting feature/casual-greeting | Both fully-merged branches removed locally |
Practical Exercise: Command Syntax and Examples
# Step 1: Shared starting point
echo 'const greeting = "Hello, team!";' > greeting.js
git add greeting.js
git commit -m "feat: add initial greeting message"
# Step 2: 'Developer A' branch
git switch -c feature/formal-greeting
echo 'const greeting = "Good day, esteemed colleagues.";' > greeting.js
git add greeting.js
git commit -m "feat: make greeting more formal"
# Step 3: 'Developer B' branch (independently, from main)
git switch main
git switch -c feature/casual-greeting
echo 'const greeting = "Hey there, folks!";' > greeting.js
git add greeting.js
git commit -m "feat: make greeting more casual"
# Step 4: Merge the first branch (fast-forward, no conflict)
git switch main
git merge feature/formal-greeting
# Step 5: Merge the second branch (CONFLICT occurs here)
git merge feature/casual-greeting
# CONFLICT (content): Merge conflict in greeting.js
# Step 6: Investigate
git status
git log --merge --oneline
# Step 7: Resolve manually
# (edit greeting.js, remove markers, decide on final content)
git add greeting.js
git commit
# Step 8: Clean up
git branch -d feature/formal-greeting
git branch -d feature/casual-greeting
# Step 9: Review final history
git log --oneline --graph --decorate --all
Breaking Down the Practical Exercise Example
This exercise deliberately engineers the exact precondition for a conflict described in Lesson 5: two branches independently changing the same line since their shared common ancestor (C1). The first merge (Step 4) succeeds as a fast-forward, since `main` hadn't diverged yet at that point. The second merge (Step 5) is where the real learning happens — `main` (now at C2) and `feature/casual-greeting` (at C3) have both diverged from C1, guaranteeing the conflict. Steps 6 through 7 apply the investigative and resolution skills from earlier in this module, and Step 9's final history review shows the complete, realistic shape of a fast-forward followed by a genuine three-way merge — precisely the kind of history a real team's repository accumulates over time.
How Practical Exercise Is Used on Real Engineering Teams
- This exact scenario — two developers independently editing the same configuration value, greeting string, or shared constant — is one of the most common real-world sources of merge conflicts on actual teams.
- Onboarding exercises at many companies include a deliberately staged conflict simulation almost identical to this one, specifically to build new hires' confidence before they encounter a real conflict under time pressure.
- Pair programming exercises and Git workshops frequently use this same 'two branches, same line, different edits' setup as the canonical teaching example for conflict resolution.
- This simulated exercise mirrors real incidents where two team members work on the same file without communicating, which is exactly the situation the short-lived branch philosophy (Lesson 10) aims to reduce the frequency of.
Practical Exercise Interview Questions and Answers
Q1. How would you deliberately reproduce a merge conflict for teaching or testing purposes?
Create two branches from the same starting commit, then independently edit the exact same line of the same file differently on each branch, committing on both. Merging the first branch into main will succeed cleanly (often as a fast-forward), but merging the second branch will then produce a genuine three-way merge conflict, since both branches changed the same content differently since their shared ancestor.
Q2. In this kind of two-developer conflict scenario, why does the first merge succeed cleanly while the second one conflicts?
The first merge is typically a fast-forward, since main hadn't diverged at all before that merge — there's nothing to reconcile. By the time the second branch is merged, main has already moved forward (via the first merge), so both main and the second branch have independently diverged from their common ancestor, and if they both changed the same content, a genuine three-way merge conflict results.
Q3. What steps would you take, using tools from this module, to confidently resolve a real conflict between two developers' work?
First investigate using git status to identify affected files and git log --merge (optionally with -p) to understand the historical context and intent behind each side's change. Then manually (or with a tool) edit the conflicting file to reflect the correct final content, remove all conflict markers, stage the file with git add, and complete the merge with git commit.
Practical Exercise Quiz: Test Your Understanding
1. In this exercise, why does merging feature/casual-greeting produce a conflict while merging feature/formal-greeting did not?
- Because feature/casual-greeting was created first
- Because by the time feature/casual-greeting is merged, main has already diverged (from the first merge), and both branches changed the same line since their shared ancestor
- Because git merge always conflicts on the second attempt
- Because feature/casual-greeting was never committed
Answer: B. Because by the time feature/casual-greeting is merged, main has already diverged (from the first merge), and both branches changed the same line since their shared ancestor
Explanation: The first merge was a fast-forward with no divergence to reconcile. By the second merge, main has moved forward, creating genuine divergence between main and feature/casual-greeting, and since both changed the same line, a three-way merge conflict results.
2. Which command in this exercise helps you see exactly which commits from both sides are relevant to the conflict?
- git branch -a
- git log --merge --oneline
- git stash list
- git tag
Answer: B. git log --merge --oneline
Explanation: git log --merge specifically filters history down to commits from both branches involved in the current conflict that touched the conflicting content, exactly the investigative tool used in Step 6 of this exercise.
3. What must be true about the final greeting.js file before it can be staged and committed to complete the merge?
- It must contain both original conflicting lines exactly as they were
- All conflict marker lines (<<<<<<<, =======, >>>>>>>) must be completely removed
- It must be renamed to a new filename
- It must be moved to a different branch first
Answer: B. All conflict marker lines (<<<<<<<, =======, >>>>>>>) must be completely removed
Explanation: Before staging and committing a resolved file, every conflict marker must be deleted, leaving only the final, correct, valid content — otherwise the file would contain broken, invalid text.
Practical Exercise: Common Mistakes Beginners Make
- Forgetting to switch back to main before creating the second 'developer's' branch, accidentally branching off the first developer's already-changed content instead of the original shared commit.
- Being surprised that the first merge doesn't produce a conflict, without recognizing it succeeded as a fast-forward due to no prior divergence.
- Rushing to resolve the conflict without first using git status and git log --merge to fully understand what's actually being reconciled.
- Forgetting to delete both feature branches after they've been fully merged, leaving unnecessary clutter in the local branch list.
Practical Exercise: Exam-Ready Quick Notes
- Two branches created from the SAME starting commit, both editing the same line, guarantees a conflict when the second one is merged.
- First merge (no prior divergence) = fast-forward, no conflict. Second merge (after main has moved) = three-way merge, conflict likely if content overlaps.
- Standard resolution sequence: investigate (status, log --merge) → resolve (edit, remove markers) → finalize (add, commit) → clean up (branch -d).
- Final history review with log --graph should show one fast-forward and one distinct two-parent merge commit.
Practical Exercise: Key Takeaways
- Deliberately simulating a conflict is one of the most effective ways to build genuine confidence for when a real one occurs under pressure.
- This exercise concretely demonstrates why the very same two commands (git merge) can behave completely differently — fast-forward vs. three-way — depending on prior divergence.
- Completing this exercise end-to-end, from creation through resolution to cleanup, mirrors the complete real-world lifecycle of collaborative branching and merging.
Frequently Asked Questions About Practical Exercise
Q1. Why does this exercise deliberately create two branches from the same commit?
Branching both feature branches from the same starting commit, and then having each edit the same line differently, is exactly the precondition needed to guarantee a genuine merge conflict — it recreates the real-world situation of two developers unknowingly changing the same content.
Q2. Why does the first merge in this exercise succeed without a conflict, but the second one doesn't?
The first merge succeeds as a simple fast-forward, since main hadn't changed at all before that point — there's no divergence to reconcile. By the time the second branch is merged, main has already moved forward, so both main and the second branch have independently diverged, and since they both changed the same line, Git can't automatically decide which version to keep.
Q3. What is the correct order of steps once a conflict occurs in this kind of scenario?
First investigate using git status (to see which files are affected) and git log --merge (to see relevant commits from both sides). Then resolve by editing the file to decide the correct final content and removing all conflict markers. Finally, stage the resolved file with git add and run git commit to complete the merge.
Q4. What should the final commit history look like after completing this exercise?
Running git log --oneline --graph --decorate --all should show the initial shared commit, followed by a fast-forwarded update from the first branch merge, and then a distinct merge commit (with two parent commits) representing the resolved conflict from the second branch merge.
Q5. What does successfully completing this exercise demonstrate about your Git skills?
It shows you understand exactly why and when conflicts occur (due to divergence and overlapping changes), can confidently read and resolve conflict markers, and can carry out the complete real-world lifecycle of branching, merging, conflict resolution, and cleanup — the core skill set for collaborating safely with other developers on a shared codebase.
Summary
This capstone exercise deliberately engineers a realistic merge conflict by creating two branches from the same starting commit and independently editing the exact same line on each — guaranteeing that once both are merged back into `main`, a genuine three-way merge conflict occurs. The exercise walks through the complete lifecycle: establishing a shared starting point, creating two diverging branches, observing a clean fast-forward merge for the first branch, encountering and investigating the conflict produced by the second merge (using `git status` and `git log --merge`), manually resolving it by editing the file and removing conflict markers, completing the merge with `git add` and `git commit`, and finally cleaning up both fully-merged branches. Reviewing the final history with `git log --graph` reveals the complete, realistic shape of a project's history: one fast-forward followed by one genuine, resolved merge commit — exactly the pattern real teams encounter regularly.