Master git rebase for clean, linear history. Learn interactive rebasing, when to rebase vs merge, and how to safely rewrite history.
Rebasing is one of Git's most powerful features - and one of its most misunderstood. At its core, rebasing moves or combines commits to create a cleaner, more linear project history.
While merging preserves the exact history of how branches evolved, rebasing rewrites history to tell a cleaner story. This makes the project history easier to read and understand, but requires care to avoid losing work or confusing teammates.
In this lesson, you'll learn when rebasing is the right choice, how to use interactive rebasing to clean up your commits, and the golden rule that keeps you out of trouble.
Rebasing means moving or combining a sequence of commits to a new base commit. It's like saying "pretend I started this work from here instead."
Basic Concept:
1Before rebase:
2main A ← B ← C ← D
3 ↘
4feature X ← Y ← Z
5
6After: git switch feature && git rebase main
7main A ← B ← C ← D
8 ↘
9feature X' ← Y' ← Z'Notice: X, Y, Z become X', Y', Z' - they're new commits with new hashes!
Basic Rebase Commands:
1# Rebase current branch onto main
2git switch feature
3git rebase main
4
5# Rebase specific branch onto another
6git rebase main feature
7
8# Rebase onto specific commit
9git rebase abc1234What Rebase Actually Does:
1# Step by step visualization:
2
3# 1. Starting state
4main A ← B ← C
5 ↘
6feature X ← Y
7
8# 2. Git finds common ancestor (B)
9# 3. Saves X and Y as patches
10# 4. Resets feature to C (tip of main)
11# 5. Applies X patch → creates X'
12# 6. Applies Y patch → creates Y'
13
14# Final result:
15main A ← B ← C
16 ↘
17feature X' ← Y'Why New Commits?
Commits include their parent commit hash. When the parent changes (from B to C), the commit hash must change. This is why rebased commits get new hashes.
1# Before rebase
2git log feature --oneline
3# abc1234 Y (parent: def5678)
4# def5678 X (parent: 111222 - commit B)
5
6# After rebase
7git log feature --oneline
8# 999888 Y' (parent: 777666)
9# 777666 X' (parent: 333444 - commit C)Both rebase and merge integrate changes between branches, but they do it very differently.
Visual Comparison:
1MERGE:
2main A ← B ← C ← D ← M (merge commit)
3 ↘ ↗
4feature X ← Y
5
6REBASE:
7main A ← B ← C ← D
8 ↘
9feature X' ← Y'Comparison Table:
| Aspect | Merge | Rebase |
|--------|-------|--------|
| History | Preserves true history | Creates linear history |
| Merge commits | Creates merge commit | No merge commits |
| Commit hashes | Preserved | Changed |
| Conflicts | Once at merge time | Potentially per commit |
| Complexity | Simpler | More complex |
| Safe for shared branches | Yes | No |
When to Merge:
1# Merge when:
2# - Working on shared branches
3# - You want to preserve the exact history
4# - The branch history tells an important story
5# - You're merging a feature into main
6
7git switch main
8git merge --no-ff feature # Preserve feature branch historyWhen to Rebase:
1# Rebase when:
2# - Updating a local feature branch with main changes
3# - Cleaning up commits before merging
4# - You want a linear, readable history
5# - The branch is only yours (not shared)
6
7git switch feature
8git rebase main # Update feature with latest mainThe Golden Rule:
Never rebase commits that have been pushed to a shared repository.
1# ✅ OK to rebase
2git switch my-local-feature
3git rebase main # Only I have these commits
4
5# ❌ DANGEROUS
6git switch shared-feature # Others are working on this
7git rebase main # DON'T DO THIS!
8# This rewrites commits others depend onTypical Workflow:
1# 1. Work on feature branch
2git switch -c feature/new-thing
3git commit -m "Add new thing"
4git commit -m "Fix new thing"
5
6# 2. Main branch moves forward
7# (others merge their features)
8
9# 3. Before merging, update with rebase
10git fetch origin
11git rebase origin/main
12
13# 4. Now merge to main (fast-forward possible)
14git switch main
15git merge feature/new-thingInteractive rebase lets you modify commits as they're replayed. This is incredibly powerful for cleaning up your commit history before sharing.
Starting Interactive Rebase:
1# Rebase last 4 commits
2git rebase -i HEAD~4
3
4# Rebase from specific commit
5git rebase -i abc1234
6
7# Rebase onto main interactively
8git rebase -i mainThe Interactive Editor:
When you run git rebase -i, Git opens your editor with:
1pick abc1234 Add login form
2pick def5678 Fix typo in login
3pick 111222 Add validation
4pick 333444 Update styles
5
6# Commands:
7# p, pick = use commit
8# r, reword = use commit, but edit message
9# e, edit = use commit, but stop for amending
10# s, squash = meld into previous commit
11# f, fixup = like squash, but discard message
12# d, drop = remove commitCommon Operations:
Reorder Commits:
1# Original order
2pick abc1234 Add login form
3pick def5678 Fix typo in login
4pick 111222 Add validation
5
6# Reordered (just move lines)
7pick abc1234 Add login form
8pick 111222 Add validation
9pick def5678 Fix typo in loginSquash Commits:
1# Combine multiple commits into one
2pick abc1234 Add login form
3squash def5678 Fix typo in login
4squash 111222 Add validation
5
6# Result: One commit with combined messageFixup (Squash without message):
1# Fix typo gets absorbed into parent
2pick abc1234 Add login form
3fixup def5678 Fix typo in login # Discards this message
4pick 111222 Add validationReword Commit Message:
1reword abc1234 Add login form # Will prompt for new message
2pick def5678 Fix typo in loginEdit a Commit:
1edit abc1234 Add login form # Stops here to let you modify
2pick def5678 Fix typo in login
3
4# Git stops after abc1234
5# Make changes, then:
6git add .
7git commit --amend
8git rebase --continueDrop a Commit:
1pick abc1234 Add login form
2drop def5678 WIP: debugging # Remove this commit entirely
3pick 111222 Add validationReal Example - Clean Up Before PR:
1# Your messy commits
2git log --oneline
3# 333444 fix
4# 222333 WIP
5# 111222 Add validation
6# def5678 Fix typo
7# abc1234 Add login form
8
9# Clean up
10git rebase -i HEAD~5
11
12# Edit to:
13pick abc1234 Add login form
14fixup def5678 Fix typo
15pick 111222 Add validation
16drop 222333 WIP
17drop 333444 fix
18
19# Result: Clean 2-commit historyRebase can encounter conflicts just like merge. The difference is conflicts may occur multiple times - once per commit being replayed.
When Conflicts Occur:
1git rebase main
2
3# Output:
4# Applying: Add feature
5# CONFLICT (content): Merge conflict in app.js
6# error: could not apply abc1234... Add feature
7
8# Git stops and waits for you to resolveResolution Process:
1# 1. See what's conflicted
2git status
3# Unmerged paths:
4# both modified: app.js
5
6# 2. Open and fix conflicts (same as merge)
7# Edit app.js, remove conflict markers
8
9# 3. Stage the resolution
10git add app.js
11
12# 4. Continue rebasing
13git rebase --continue
14
15# If more conflicts exist, repeat steps 1-4Rebase Control Commands:
1# Continue after resolving conflicts
2git rebase --continue
3
4# Skip this commit (careful - loses changes!)
5git rebase --skip
6
7# Abort entire rebase (go back to start)
8git rebase --abortConflict per Commit:
1# Rebasing 3 commits, conflicts in commit 1 and 3
2
3# Commit 1: CONFLICT
4git status # See conflicts
5# Fix conflicts
6git add .
7git rebase --continue
8
9# Commit 2: Applied successfully
10
11# Commit 3: CONFLICT
12git status # See conflicts
13# Fix conflicts
14git add .
15git rebase --continue
16
17# Done!Using Merge Strategy:
1# Prefer your version for conflicts
2git rebase -X theirs main
3
4# Prefer main's version for conflicts
5git rebase -X ours mainRecovery Options:
1# Abort rebase in progress
2git rebase --abort
3
4# If you already finished but want to undo
5git reflog
6# Find the commit before rebase
7git reset --hard HEAD@{5}
8
9# Or use ORIG_HEAD
10git reset --hard ORIG_HEADBeyond basic rebasing, there are powerful techniques for managing complex histories.
Rebase Onto:
Move commits from one base to another:
1# Current state:
2# A ← B ← C (main)
3# ↘
4# D ← E (feature-a)
5# ↘
6# F ← G (feature-b, depends on feature-a)
7
8# You want feature-b based on main, not feature-a
9git rebase --onto main feature-a feature-b
10
11# Result:
12# A ← B ← C (main)
13# ↘ ↘
14# D ← E F' ← G' (feature-b)Split a Commit:
1# Use interactive rebase with edit
2git rebase -i HEAD~3
3
4# Mark the commit to split as 'edit'
5edit abc1234 Add login and validation # This should be 2 commits
6
7# When Git stops:
8git reset HEAD~1 # Undo commit, keep changes staged
9git add login.js
10git commit -m "Add login form"
11git add validation.js
12git commit -m "Add form validation"
13git rebase --continueAutosquash:
1# Create fixup commits that auto-squash
2git commit --fixup abc1234 # Creates: fixup! Original message
3
4# Later, rebase with autosquash
5git rebase -i --autosquash main
6# Fixup commits automatically placed after their targetsPreserving Merge Commits:
1# Default rebase flattens history
2# Use --rebase-merges to preserve merge structure
3git rebase --rebase-merges mainPull with Rebase:
1# Instead of merge when pulling
2git pull --rebase origin main
3
4# Make this the default
5git config --global pull.rebase true
6
7# Per-repository
8git config pull.rebase trueExec During Rebase:
1# Run command after each commit
2git rebase -i --exec "npm test" HEAD~5
3
4# In interactive editor:
5pick abc1234 Add feature
6exec npm test
7pick def5678 Add tests
8exec npm testRebase Preserving Committer Date:
1# Keep original commit dates
2git rebase --committer-date-is-author-date mainRebasing rewrites history, which can be dangerous. Follow these practices to stay safe.
The Golden Rule (Repeated!):
1# NEVER rebase commits that have been pushed and shared
2# Unless you're the only one working on that branch
3
4# Safe:
5git rebase main # Local-only feature branch
6
7# Dangerous:
8git rebase main # Branch others are using
9git push --force # Forces your rewritten history
10# Now everyone else has conflicts!Force Push Safely:
1# If you must push after rebase (your own branch)
2# Use --force-with-lease instead of --force
3
4git push --force-with-lease
5
6# This fails if someone else pushed to the branch
7# Prevents accidentally overwriting their workBefore Starting Rebase:
1# 1. Make sure working directory is clean
2git status
3# Should be: nothing to commit
4
5# 2. Create a backup branch
6git branch backup-feature
7
8# 3. Now rebase safely
9git rebase main
10
11# If something goes wrong:
12git reset --hard backup-featureCommunication:
1# If rebasing a shared branch is necessary:
2# 1. Tell your team BEFORE
3# 2. Do it during low-activity time
4# 3. Tell everyone AFTER
5
6# Team members need to:
7git fetch origin
8git reset --hard origin/branch
9# Or delete local and re-checkoutRecovery Checklist:
1# Lost commits after bad rebase?
2
3# 1. Check reflog
4git reflog
5# Shows all HEAD movements, even "lost" commits
6
7# 2. Find the commit before rebase
8# Look for "rebase finished" or your last good commit
9
10# 3. Reset to that point
11git reset --hard HEAD@{5}
12
13# 4. Or cherry-pick specific commits
14git cherry-pick abc1234When NOT to Rebase:
When to Rebase:
Practice interactive rebasing by reordering, squashing, and editing commits. See how your changes affect the commit history.
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>Interactive Rebase Simulator</title>
7 <style>
8 * {
9 margin: 0;
10 padding: 0;
11 box-sizing: border-box;
12 }
13
14 body {
15 font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
16 background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
17 min-height: 100vh;
18 color: #e0e0e0;
19 padding: 20px;
20 }
21
22 .container {
23 max-width: 1000px;
24 margin: 0 auto;
25 }
26
27 h1 {
28 text-align: center;
29 color: #f093fb;
30 margin-bottom: 10px;
31 font-size: 1.8rem;
32 }
33
34 .subtitle {
35 text-align: center;
36 color: #888;
37 margin-bottom: 30px;
38 }
39
40 .panels {
41 display: grid;
42 grid-template-columns: 1fr 1fr;
43 gap: 20px;
44 margin-bottom: 25px;
45 }
46
47 .panel {
48 background: rgba(0, 0, 0, 0.4);
49 border-radius: 15px;
50 padding: 20px;
51 }
52
53 .panel h2 {
54 color: #00d4aa;
55 margin-bottom: 15px;
56 font-size: 1rem;
57 }
58
59 .commit-list {
60 min-height: 250px;
61 }
62
63 .commit {
64 display: flex;
65 align-items: center;
66 gap: 10px;
67 padding: 12px;
68 background: rgba(255, 255, 255, 0.05);
69 border-radius: 8px;
70 margin-bottom: 8px;
71 cursor: move;
72 transition: all 0.2s ease;
73 }
74
75 .commit:hover {
76 background: rgba(255, 255, 255, 0.1);
77 }
78
79 .commit.dragging {
80 opacity: 0.5;
81 background: rgba(240, 147, 251, 0.2);
82 }
83
84 .commit.squashed {
85 background: rgba(107, 203, 119, 0.2);
86 border-left: 3px solid #6bcb77;
87 }
88
89 .commit.dropped {
90 background: rgba(255, 107, 107, 0.2);
91 text-decoration: line-through;
92 opacity: 0.5;
93 }
94
95 .commit-hash {
96 background: #ffd93d;
97 color: #1a1a2e;
98 padding: 3px 8px;
99 border-radius: 4px;
100 font-size: 0.75rem;
101 font-weight: bold;
102 min-width: 60px;
103 text-align: center;
104 }
105
106 .commit-message {
107 flex: 1;
108 color: #ddd;
109 font-size: 0.85rem;
110 }
111
112 .commit-actions {
113 display: flex;
114 gap: 5px;
115 }
116
117 .action-btn {
118 padding: 4px 8px;
119 border: none;
120 border-radius: 4px;
121 cursor: pointer;
122 font-size: 0.7rem;
123 font-weight: bold;
124 transition: all 0.2s ease;
125 }
126
127 .action-btn:hover {
128 transform: scale(1.1);
129 }
130
131 .btn-squash {
132 background: #6bcb77;
133 color: #1a1a2e;
134 }
135
136 .btn-drop {
137 background: #ff6b6b;
138 color: white;
139 }
140
141 .btn-reword {
142 background: #4facfe;
143 color: white;
144 }
145
146 .result-commit {
147 display: flex;
148 align-items: center;
149 gap: 10px;
150 padding: 12px;
151 background: rgba(107, 203, 119, 0.1);
152 border-radius: 8px;
153 margin-bottom: 8px;
154 border-left: 3px solid #6bcb77;
155 }
156
157 .result-hash {
158 background: #6bcb77;
159 color: #1a1a2e;
160 padding: 3px 8px;
161 border-radius: 4px;
162 font-size: 0.75rem;
163 font-weight: bold;
164 }
165
166 .controls {
167 display: flex;
168 gap: 15px;
169 justify-content: center;
170 margin-bottom: 20px;
171 }
172
173 .btn {
174 padding: 12px 24px;
175 border: none;
176 border-radius: 8px;
177 cursor: pointer;
178 font-weight: bold;
179 font-family: inherit;
180 font-size: 0.85rem;
181 transition: all 0.3s ease;
182 }
183
184 .btn:hover {
185 transform: translateY(-2px);
186 }
187
188 .btn-rebase {
189 background: linear-gradient(135deg, #f093fb, #f5576c);
190 color: white;
191 }
192
193 .btn-reset {
194 background: linear-gradient(135deg, #636363, #8e8e8e);
195 color: white;
196 }
197
198 .terminal {
199 background: #0d0d0d;
200 border-radius: 12px;
201 overflow: hidden;
202 }
203
204 .terminal-header {
205 background: #2d2d2d;
206 padding: 10px 15px;
207 display: flex;
208 align-items: center;
209 gap: 8px;
210 }
211
212 .terminal-dot {
213 width: 12px;
214 height: 12px;
215 border-radius: 50%;
216 }
217
218 .terminal-dot.red { background: #ff5f56; }
219 .terminal-dot.yellow { background: #ffbd2e; }
220 .terminal-dot.green { background: #27ca40; }
221
222 .terminal-body {
223 padding: 15px;
224 font-size: 0.85rem;
225 line-height: 1.6;
226 max-height: 200px;
227 overflow-y: auto;
228 }
229
230 .cmd { color: #f093fb; }
231 .output { color: #888; }
232 .success { color: #6bcb77; }
233
234 .legend {
235 display: flex;
236 justify-content: center;
237 gap: 20px;
238 margin-top: 20px;
239 flex-wrap: wrap;
240 }
241
242 .legend-item {
243 display: flex;
244 align-items: center;
245 gap: 8px;
246 font-size: 0.8rem;
247 color: #888;
248 }
249
250 .legend-dot {
251 width: 12px;
252 height: 12px;
253 border-radius: 3px;
254 }
255
256 @media (max-width: 700px) {
257 .panels {
258 grid-template-columns: 1fr;
259 }
260 }
261 </style>
262</head>
263<body>
264 <div class="container">
265 <h1>Interactive Rebase</h1>
266 <p class="subtitle">Drag to reorder, click buttons to squash/drop commits</p>
267
268 <div class="controls">
269 <button class="btn btn-rebase" onclick="performRebase()">
270 Execute Rebase
271 </button>
272 <button class="btn btn-reset" onclick="resetCommits()">
273 Reset
274 </button>
275 </div>
276
277 <div class="panels">
278 <div class="panel">
279 <h2>Original Commits (drag to reorder)</h2>
280 <div class="commit-list" id="original-commits"></div>
281 </div>
282 <div class="panel">
283 <h2>Result After Rebase</h2>
284 <div class="commit-list" id="result-commits">
285 <p style="color: #666; text-align: center; padding: 40px;">
286 Click "Execute Rebase" to see results
287 </p>
288 </div>
289 </div>
290 </div>
291
292 <div class="terminal">
293 <div class="terminal-header">
294 <div class="terminal-dot red"></div>
295 <div class="terminal-dot yellow"></div>
296 <div class="terminal-dot green"></div>
297 </div>
298 <div class="terminal-body" id="terminal">
299 <span class="cmd">$ git rebase -i HEAD~5</span><br>
300 <span class="output">Waiting for your edit instructions...</span>
301 </div>
302 </div>
303
304 <div class="legend">
305 <div class="legend-item">
306 <div class="legend-dot" style="background: #6bcb77;"></div>
307 <span>Squash (combine with previous)</span>
308 </div>
309 <div class="legend-item">
310 <div class="legend-dot" style="background: #ff6b6b;"></div>
311 <span>Drop (remove commit)</span>
312 </div>
313 <div class="legend-item">
314 <div class="legend-dot" style="background: #4facfe;"></div>
315 <span>Reword (edit message)</span>
316 </div>
317 </div>
318 </div>
319
320 <script>
321 let commits = [
322 { hash: 'abc123', message: 'Add user authentication', action: 'pick' },
323 { hash: 'def456', message: 'Fix typo in login form', action: 'pick' },
324 { hash: 'ghi789', message: 'WIP: debugging', action: 'pick' },
325 { hash: 'jkl012', message: 'Add form validation', action: 'pick' },
326 { hash: 'mno345', message: 'Update styles', action: 'pick' }
327 ];
328
329 let draggedItem = null;
330
331 function renderCommits() {
332 const container = document.getElementById('original-commits');
333 container.innerHTML = commits.map((c, i) => `
334 <div class="commit ${c.action === 'squash' ? 'squashed' : ''} ${c.action === 'drop' ? 'dropped' : ''}"
335 draggable="true"
336 data-index="${i}"
337 ondragstart="handleDragStart(event)"
338 ondragover="handleDragOver(event)"
339 ondrop="handleDrop(event)"
340 ondragend="handleDragEnd(event)">
341 <span class="commit-hash">${c.hash}</span>
342 <span class="commit-message">${c.message}</span>
343 <div class="commit-actions">
344 <button class="action-btn btn-squash" onclick="toggleAction(${i}, 'squash')" title="Squash">S</button>
345 <button class="action-btn btn-drop" onclick="toggleAction(${i}, 'drop')" title="Drop">D</button>
346 </div>
347 </div>
348 `).join('');
349 }
350
351 function handleDragStart(e) {
352 draggedItem = e.target;
353 e.target.classList.add('dragging');
354 e.dataTransfer.effectAllowed = 'move';
355 }
356
357 function handleDragOver(e) {
358 e.preventDefault();
359 const afterElement = getDragAfterElement(e.clientY);
360 const container = document.getElementById('original-commits');
361 if (afterElement == null) {
362 container.appendChild(draggedItem);
363 } else {
364 container.insertBefore(draggedItem, afterElement);
365 }
366 }
367
368 function getDragAfterElement(y) {
369 const draggableElements = [...document.querySelectorAll('.commit:not(.dragging)')];
370 return draggableElements.reduce((closest, child) => {
371 const box = child.getBoundingClientRect();
372 const offset = y - box.top - box.height / 2;
373 if (offset < 0 && offset > closest.offset) {
374 return { offset: offset, element: child };
375 } else {
376 return closest;
377 }
378 }, { offset: Number.NEGATIVE_INFINITY }).element;
379 }
380
381 function handleDrop(e) {
382 e.preventDefault();
383 updateCommitsFromDOM();
384 }
385
386 function handleDragEnd(e) {
387 e.target.classList.remove('dragging');
388 updateCommitsFromDOM();
389 }
390
391 function updateCommitsFromDOM() {
392 const elements = document.querySelectorAll('.commit');
393 const newOrder = [];
394 elements.forEach(el => {
395 const index = parseInt(el.dataset.index);
396 newOrder.push(commits.find((c, i) => {
397 // Find by hash since order changed
398 const hash = el.querySelector('.commit-hash').textContent;
399 return c.hash === hash;
400 }));
401 });
402 commits = newOrder.filter(c => c);
403 renderCommits();
404 }
405
406 function toggleAction(index, action) {
407 if (commits[index].action === action) {
408 commits[index].action = 'pick';
409 } else {
410 commits[index].action = action;
411 }
412 renderCommits();
413 }
414
415 function performRebase() {
416 const terminal = document.getElementById('terminal');
417 const resultContainer = document.getElementById('result-commits');
418
419 // Build rebase instructions
420 let instructions = commits.map(c => `${c.action} ${c.hash} ${c.message}`).join('\n');
421
422 terminal.innerHTML = `425`;
426
427 // Process commits
428 let resultCommits = [];
429 let squashBuffer = [];
430
431 commits.forEach((c, i) => {
432 if (c.action === 'drop') {
433 terminal.innerHTML += ``;
434 } else if (c.action === 'squash') {
435 if (resultCommits.length > 0) {
436 squashBuffer.push(c);
437 terminal.innerHTML += ``;
438 } else {
439 resultCommits.push({ ...c, hash: generateHash() });
440 }
441 } else {
442 if (squashBuffer.length > 0) {
443 const last = resultCommits[resultCommits.length - 1];
444 last.message += ' (+ ' + squashBuffer.length + ' squashed)';
445 squashBuffer = [];
446 }
447 resultCommits.push({ ...c, hash: generateHash() });
448 terminal.innerHTML += ``;
449 }
450 });
451
452 // Handle remaining squash buffer
453 if (squashBuffer.length > 0 && resultCommits.length > 0) {
454 const last = resultCommits[resultCommits.length - 1];
455 last.message += ' (+ ' + squashBuffer.length + ' squashed)';
456 }
457
458 terminal.innerHTML += ``;
459
460 // Render result
461 resultContainer.innerHTML = resultCommits.map(c => `
462 <div class="result-commit">
463 <span class="result-hash">${c.hash}</span>
464 <span class="commit-message">${c.message}</span>
465 </div>
466 `).join('');
467
468 if (resultCommits.length === 0) {
469 resultContainer.innerHTML = '<p style="color: #ff6b6b; text-align: center; padding: 40px;">All commits were dropped!</p>';
470 }
471 }
472
473 function generateHash() {
474 return Math.random().toString(36).substring(2, 8);
475 }
476
477 function resetCommits() {
478 commits = [
479 { hash: 'abc123', message: 'Add user authentication', action: 'pick' },
480 { hash: 'def456', message: 'Fix typo in login form', action: 'pick' },
481 { hash: 'ghi789', message: 'WIP: debugging', action: 'pick' },
482 { hash: 'jkl012', message: 'Add form validation', action: 'pick' },
483 { hash: 'mno345', message: 'Update styles', action: 'pick' }
484 ];
485
486 renderCommits();
487
488 document.getElementById('result-commits').innerHTML = `492`;
493
494 document.getElementById('terminal').innerHTML = `497`;
498 }
499
500 // Initialize
501 renderCommits();
502 </script>
503</body>
504</html>This simulator demonstrates interactive rebasing:
How to Use:
What You'll See:
Try These Exercises:
Key Insights:
Great work mastering rebasing! In the next lesson, we'll cover tags, releases, and semantic versioning - essential skills for managing software releases and deployment versions.
Your progress is saved in this browser, on this device only.