Learn how to combine branches with git merge, understand different merge strategies, and confidently resolve merge conflicts when they arise.
After working on branches, you need to bring changes back together. Merging combines the work from different branches into one. While Git handles most merges automatically, sometimes it needs your help to resolve conflicts.
Merge conflicts occur when Git can't automatically determine which changes to keep - typically when two branches modify the same lines of code. Learning to resolve conflicts confidently is an essential developer skill.
This lesson will teach you everything about merging: from simple fast-forward merges to complex conflict resolution scenarios.
Merging integrates changes from one branch into another. Git offers several ways to do this.
Basic Merge Command:
1# Switch to the branch you want to merge INTO
2git switch main
3
4# Merge another branch into your current branch
5git merge feature/loginTypes of Merges:
1. Fast-Forward Merge
When there's a direct path from the current branch to the merged branch:
1Before:
2main ────A────B
3 \
4 C────D feature
5
6After (fast-forward):
7main ────A────B────C────D
8 ↑
9 feature1git switch main
2git merge feature
3# Fast-forward merge - no merge commit created2. Three-Way Merge
When branches have diverged and both have new commits:
1Before:
2main ────A────B────E────F
3 \
4 C────D feature
5
6After (three-way merge):
7main ────A────B────E────F────M
8 \ /
9 C────────D
10 feature1git switch main
2git merge feature
3# Creates a merge commit M combining both historiesMerge Output Examples:
1# Fast-forward merge
2$ git merge feature/login
3Updating a1b2c3d..d4e5f6g
4Fast-forward
5 login.js | 45 +++++++++++++++++++++
6 auth.js | 12 ++++++
7 2 files changed, 57 insertions(+)
8
9# Three-way merge
10$ git merge feature/api
11Merge made by the 'ort' strategy.
12 api.js | 120 ++++++++++++++++++++++++++++++
13 routes.js | 34 +++++++++++
14 2 files changed, 154 insertions(+)Git provides various options to control how merges are performed.
Common Merge Options:
1# Standard merge (creates merge commit if needed)
2git merge feature
3
4# Force a merge commit even for fast-forward
5git merge --no-ff feature
6
7# Fast-forward only (fail if not possible)
8git merge --ff-only feature
9
10# Merge without committing (review changes first)
11git merge --no-commit feature
12
13# Squash all commits into one (no merge commit)
14git merge --squash feature
15git commit -m "Add feature X"No Fast-Forward (--no-ff):
1With --no-ff:
2main ────A────B────────────M
3 \ /
4 C────D───┘
5 feature
6
7Always creates a merge commit, preserving feature branch historySquash Merge:
1# Combines all feature commits into one
2git merge --squash feature
3git commit -m "Add complete login feature"
4
5# Result: Clean linear history
6main ────A────B────S
7 ↑
8 All feature changes in one commitMerge Strategies:
1# Recursive (default) - handles most cases
2git merge -s recursive feature
3
4# Ours - keep our version, ignore theirs
5git merge -s ours feature
6
7# Theirs (via option) - prefer their changes in conflicts
8git merge -X theirs feature
9
10# Ours (via option) - prefer our changes in conflicts
11git merge -X ours featureWhen to Use Each:
| Strategy | Use Case |
|----------|----------|
| Standard merge | Regular feature integration |
| --no-ff | Preserve feature branch history |
| --squash | Clean up messy feature branches |
| --ff-only | CI/CD pipelines requiring linear history |
| -X theirs/ours | Auto-resolve conflicts with preference |
Merge conflicts occur when Git can't automatically reconcile changes from different branches.
When Conflicts Happen:
Conflict Scenario:
1# main branch has:
2function greet() {
3 return "Hello, World!";
4}
5
6# feature branch has:
7function greet() {
8 return "Hi there!";
9}
10
11# When you merge, Git doesn't know which version to keepWhat You See When Conflicts Occur:
1$ git merge feature
2Auto-merging app.js
3CONFLICT (content): Merge conflict in app.js
4Automatic merge failed; fix conflicts and then commit the result.Checking Conflict Status:
1$ git status
2On branch main
3You have unmerged paths.
4 (fix conflicts and run "git commit")
5 (use "git merge --abort" to abort the merge)
6
7Unmerged paths:
8 (use "git add <file>..." to mark resolution)
9 both modified: app.jsConflict Markers in Files:
1function greet() {
2<<<<<<< HEAD
3 return "Hello, World!";
4=======
5 return "Hi there!";
6>>>>>>> feature
7}Understanding the Markers:
<<<<<<< HEAD: Start of your current branch's version=======: Separator between versions>>>>>>> feature: End of incoming branch's versionTypes of Conflicts:
1# Content conflict (both modified same lines)
2both modified: file.js
3
4# Delete/modify conflict
5deleted by us: removed-file.js
6
7# Add/add conflict (both created same file)
8both added: new-file.jsResolving conflicts is a matter of deciding which changes to keep and cleaning up the conflict markers.
Manual Resolution Steps:
Step 1: Identify Conflicted Files
1git status
2# Shows files with conflictsStep 2: Open and Edit Conflicted Files
1// Before (with conflict markers):
2function greet() {
3<<<<<<< HEAD
4 return "Hello, World!";
5=======
6 return "Hi there!";
7>>>>>>> feature
8}
9
10// After (resolved - you choose what to keep):
11function greet() {
12 return "Hello, World! How are you?"; // Combined both ideas
13}Step 3: Mark as Resolved
1git add app.jsStep 4: Complete the Merge
1git commit
2# Opens editor with pre-filled merge message
3# Or:
4git commit -m "Merge feature into main, resolved greeting conflict"Resolution Options:
1// Option 1: Keep ours (HEAD)
2function greet() {
3 return "Hello, World!";
4}
5
6// Option 2: Keep theirs (feature)
7function greet() {
8 return "Hi there!";
9}
10
11// Option 3: Keep both
12function greet() {
13 return "Hello, World!";
14}
15
16function greetCasual() {
17 return "Hi there!";
18}
19
20// Option 4: Write something new
21function greet(formal = true) {
22 return formal ? "Hello, World!" : "Hi there!";
23}Using Command Line to Choose:
1# Accept current branch's version for a file
2git checkout --ours app.js
3git add app.js
4
5# Accept incoming branch's version for a file
6git checkout --theirs app.js
7git add app.jsViewing Conflict Details:
1# See what both sides changed
2git diff
3
4# See original, ours, theirs
5git checkout --conflict=diff3 app.jsWith diff3 style:
1<<<<<<< HEAD
2 return "Hello, World!";
3||||||| merged common ancestors
4 return "Hello"; // Original version
5=======
6 return "Hi there!";
7>>>>>>> featureGit integrates with visual merge tools, and you can always abort a problematic merge.
Using Merge Tools:
1# Launch configured merge tool
2git mergetool
3
4# Use specific tool
5git mergetool --tool=vscodeConfigure Merge Tool:
1# Set VS Code as merge tool
2git config --global merge.tool vscode
3git config --global mergetool.vscode.cmd 'code --wait $MERGED'
4
5# Set VS Code as diff tool
6git config --global diff.tool vscode
7git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'
8
9# Other popular tools
10git config --global merge.tool meld
11git config --global merge.tool kdiff3
12git config --global merge.tool p4mergeAborting a Merge:
1# During a merge with conflicts
2git merge --abort
3# Returns to pre-merge state
4
5# If you already committed but want to undo
6git reset --hard HEAD~1
7# WARNING: Loses the merge commitUndoing a Merge After Commit:
1# Create a new commit that undoes the merge
2git revert -m 1 HEAD
3# -m 1 specifies to keep the first parent (usually main)
4
5# This is safer than reset for shared branchesPreventing Conflicts:
1 git switch feature
2 git merge main # Keep feature up to dateRe-resolving the Same Conflicts:
1# Enable rerere (reuse recorded resolution)
2git config --global rerere.enabled true
3
4# Git will remember how you resolved conflicts
5# and auto-apply the same resolution next timeMerge Tips:
1# See merge base (common ancestor)
2git merge-base main feature
3
4# Preview what would be merged
5git log main..feature
6
7# See which files would conflict
8git merge --no-commit feature
9git diff --name-only --diff-filter=U
10git merge --abortPractice resolving merge conflicts in a simulated environment. See how conflicts look and learn to resolve them confidently.
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Merge Conflict Resolver</title>
7 <style>
8 body {
9 font-family: 'Segoe UI', system-ui, sans-serif;
10 background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
11 min-height: 100vh;
12 color: #e0e0e0;
13 padding: 20px;
14 }
15 .container { max-width: 1100px; margin: 0 auto; }
16 h1 { text-align: center; color: #f97316; margin-bottom: 10px; }
17 .subtitle { text-align: center; color: #9ca3af; margin-bottom: 25px; }
18
19 .scenario-panel {
20 background: rgba(255, 255, 255, 0.03);
21 border-radius: 12px;
22 padding: 20px;
23 margin-bottom: 20px;
24 }
25 .scenario-panel h3 { color: #60a5fa; margin-bottom: 15px; }
26
27 .branch-comparison {
28 display: grid;
29 grid-template-columns: 1fr 1fr;
30 gap: 20px;
31 margin-bottom: 20px;
32 }
33
34 .branch-panel {
35 background: rgba(0, 0, 0, 0.3);
36 border-radius: 10px;
37 overflow: hidden;
38 }
39
40 .branch-header {
41 padding: 10px 15px;
42 font-weight: 600;
43 font-size: 14px;
44 }
45 .branch-header.main { background: #22c55e; color: white; }
46 .branch-header.feature { background: #3b82f6; color: white; }
47
48 .branch-code {
49 padding: 15px;
50 font-family: 'Fira Code', monospace;
51 font-size: 13px;
52 white-space: pre-wrap;
53 min-height: 100px;
54 }
55
56 .conflict-editor {
57 background: #0f172a;
58 border-radius: 10px;
59 overflow: hidden;
60 margin-bottom: 20px;
61 }
62
63 .editor-header {
64 background: #ef4444;
65 color: white;
66 padding: 10px 15px;
67 font-weight: 600;
68 }
69
70 .editor-content {
71 padding: 15px;
72 font-family: 'Fira Code', monospace;
73 font-size: 13px;
74 }
75
76 .conflict-marker { color: #f97316; font-weight: bold; }
77 .ours-code { background: rgba(34, 197, 94, 0.2); display: block; padding: 5px 10px; margin: 5px 0; border-radius: 4px; }
78 .theirs-code { background: rgba(59, 130, 246, 0.2); display: block; padding: 5px 10px; margin: 5px 0; border-radius: 4px; }
79 .separator { color: #6b7280; }
80
81 .resolution-panel {
82 background: rgba(255, 255, 255, 0.03);
83 border-radius: 12px;
84 padding: 20px;
85 margin-bottom: 20px;
86 }
87
88 .resolution-panel h3 { color: #22c55e; margin-bottom: 15px; }
89
90 .resolution-options {
91 display: grid;
92 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
93 gap: 15px;
94 margin-bottom: 20px;
95 }
96
97 .resolution-btn {
98 padding: 15px;
99 border: 2px solid rgba(255, 255, 255, 0.1);
100 border-radius: 10px;
101 background: rgba(255, 255, 255, 0.03);
102 color: #e0e0e0;
103 cursor: pointer;
104 text-align: left;
105 transition: all 0.3s;
106 }
107
108 .resolution-btn:hover {
109 border-color: #f97316;
110 background: rgba(249, 115, 22, 0.1);
111 }
112
113 .resolution-btn.selected {
114 border-color: #22c55e;
115 background: rgba(34, 197, 94, 0.1);
116 }
117
118 .resolution-btn h4 {
119 color: #f97316;
120 margin-bottom: 8px;
121 font-size: 14px;
122 }
123
124 .resolution-btn code {
125 display: block;
126 background: rgba(0, 0, 0, 0.3);
127 padding: 10px;
128 border-radius: 6px;
129 font-size: 12px;
130 white-space: pre-wrap;
131 }
132
133 .custom-resolution {
134 margin-top: 20px;
135 }
136
137 .custom-resolution textarea {
138 width: 100%;
139 height: 100px;
140 background: #0f172a;
141 border: 1px solid rgba(255, 255, 255, 0.1);
142 border-radius: 8px;
143 color: #e0e0e0;
144 font-family: 'Fira Code', monospace;
145 font-size: 13px;
146 padding: 15px;
147 resize: vertical;
148 }
149
150 .action-buttons {
151 display: flex;
152 gap: 15px;
153 justify-content: center;
154 margin-top: 20px;
155 }
156
157 .btn {
158 padding: 12px 30px;
159 border: none;
160 border-radius: 8px;
161 cursor: pointer;
162 font-weight: 600;
163 transition: all 0.3s;
164 }
165
166 .btn:hover { transform: translateY(-2px); }
167
168 .btn-resolve { background: #22c55e; color: white; }
169 .btn-abort { background: #ef4444; color: white; }
170 .btn-next { background: #3b82f6; color: white; }
171
172 .result-panel {
173 background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05));
174 border: 1px solid #22c55e;
175 border-radius: 12px;
176 padding: 20px;
177 text-align: center;
178 display: none;
179 }
180
181 .result-panel h3 { color: #22c55e; margin-bottom: 10px; }
182
183 .terminal {
184 background: #0f172a;
185 border-radius: 8px;
186 padding: 15px;
187 font-family: monospace;
188 font-size: 13px;
189 margin-top: 15px;
190 text-align: left;
191 }
192
193 .cmd { color: #22c55e; }
194 .output { color: #9ca3af; }
195 .success { color: #4ade80; }
196
197 @media (max-width: 768px) {
198 .branch-comparison { grid-template-columns: 1fr; }
199 }
200 </style>
201</head>
202<body>
203 <div class="container">
204 <h1>Merge Conflict Resolver</h1>
205 <p class="subtitle">Practice resolving real-world merge conflicts</p>
206
207 <div class="scenario-panel">
208 <h3>Scenario: Merging feature/greeting into main</h3>
209 <p>Both branches modified the same function. You need to resolve the conflict.</p>
210 </div>
211
212 <div class="branch-comparison">
213 <div class="branch-panel">
214 <div class="branch-header main">main branch (HEAD)</div>
215 <div class="branch-code" id="main-code">function greet(name) {
216 return "Hello, " + name + "!";
217}</div>
218 </div>
219 <div class="branch-panel">
220 <div class="branch-header feature">feature/greeting branch</div>
221 <div class="branch-code" id="feature-code">function greet(name) {
222 return `Welcome, ${name}!`;
223}</div>
224 </div>
225 </div>
226
227 <div class="conflict-editor">
228 <div class="editor-header">⚠️ CONFLICT in greeting.js</div>
229 <div class="editor-content">
230 <span class="conflict-marker"><<<<<<< HEAD</span>
231 <span class="ours-code">function greet(name) {
232 return "Hello, " + name + "!";
233}</span>
234 <span class="separator">=======</span>
235 <span class="theirs-code">function greet(name) {
236 return `Welcome, ${name}!`;
237}</span>
238 <span class="conflict-marker">>>>>>>> feature/greeting</span>
239 </div>
240 </div>
241
242 <div class="resolution-panel">
243 <h3>Choose Resolution</h3>
244 <div class="resolution-options">
245 <button class="resolution-btn" onclick="selectResolution(0)">
246 <h4>Keep Ours (main)</h4>
247 <code>function greet(name) {
248 return "Hello, " + name + "!";
249}</code>
250 </button>
251 <button class="resolution-btn" onclick="selectResolution(1)">
252 <h4>Keep Theirs (feature)</h4>
253 <code>function greet(name) {
254 return `Welcome, ${name}!`;
255}</code>
256 </button>
257 <button class="resolution-btn" onclick="selectResolution(2)">
258 <h4>Keep Both</h4>
259 <code>function greetFormal(name) {
260 return "Hello, " + name + "!";
261}
262
263function greetCasual(name) {
264 return `Welcome, ${name}!`;
265}</code>
266 </button>
267 <button class="resolution-btn" onclick="selectResolution(3)">
268 <h4>Combine Ideas</h4>
269 <code>function greet(name, formal = true) {
270 return formal
271 ? "Hello, " + name + "!"
272 : `Welcome, ${name}!`;
273}</code>
274 </button>
275 </div>
276
277 <div class="custom-resolution">
278 <h4 style="color: #f97316; margin-bottom: 10px;">Or write your own resolution:</h4>
279 <textarea id="custom-code" placeholder="Write your resolved code here..."></textarea>
280 </div>
281
282 <div class="action-buttons">
283 <button class="btn btn-abort" onclick="abortMerge()">Abort Merge</button>
284 <button class="btn btn-resolve" onclick="resolveConflict()">Resolve & Commit</button>
285 </div>
286 </div>
287
288 <div class="result-panel" id="result">
289 <h3>✅ Conflict Resolved!</h3>
290 <p>The merge was completed successfully.</p>
291 <div class="terminal">
292 <div class="cmd">$ git add greeting.js</div>
293 <div class="cmd">$ git commit -m "Merge feature/greeting into main"</div>
294 <div class="success">[main abc1234] Merge feature/greeting into main</div>
295 </div>
296 <button class="btn btn-next" onclick="resetScenario()" style="margin-top: 15px;">Try Another Scenario</button>
297 </div>
298 </div>
299
300 <script>
301 let selectedResolution = null;
302
303 const resolutions = [
304 'function greet(name) {\n return "Hello, " + name + "!";\n}',
305 'function greet(name) {\n return `Welcome, ${name}!`;\n}',
306 'function greetFormal(name) {\n return "Hello, " + name + "!";\n}\n\nfunction greetCasual(name) {\n return `Welcome, ${name}!`;\n}',
307 'function greet(name, formal = true) {\n return formal \n ? "Hello, " + name + "!"\n : `Welcome, ${name}!`;\n}'
308 ];
309
310 function selectResolution(index) {
311 selectedResolution = index;
312 document.querySelectorAll('.resolution-btn').forEach((btn, i) => {
313 btn.classList.toggle('selected', i === index);
314 });
315 document.getElementById('custom-code').value = resolutions[index];
316 }
317
318 function resolveConflict() {
319 const customCode = document.getElementById('custom-code').value.trim();
320 if (!customCode) {
321 alert('Please select a resolution or write custom code');
322 return;
323 }
324
325 document.getElementById('result').style.display = 'block';
326 document.querySelector('.resolution-panel').style.display = 'none';
327 document.querySelector('.conflict-editor').innerHTML =
328 '<div class="editor-header" style="background: #22c55e;">✅ greeting.js - Resolved</div>' +
329 '<div class="editor-content"><pre>' + customCode + '</pre></div>';
330 }
331
332 function abortMerge() {
333 alert('Merge aborted. Returned to pre-merge state.');
334 resetScenario();
335 }
336
337 function resetScenario() {
338 location.reload();
339 }
340 </script>
341</body>
342</html>This interactive tool helps you practice resolving merge conflicts:
What You See:
Resolution Strategies:
Key Takeaways:
You now understand merging and conflict resolution! In the next lesson, we'll explore professional Git workflows like Feature Branch, Gitflow, and GitHub Flow. You'll learn how teams organize their branching strategies for efficient collaboration.
Your progress is saved in this browser, on this device only.