Lesson 23 · Recovery
Resolve merge and rebase conflicts
Resolve the same competing headline in a merge and in a rebase, see why "ours" and "theirs" swap between them, save one deliberate combined result and finish each operation without losing either person's idea.
- Level: Intermediate
- 12 min read
- 7 min video
- Updated
- Before this lesson: Merge branches with diverged history, Rebase unpublished work onto a new base
Progress saved in this browser.
Playing loads the video from YouTube (privacy-enhanced mode).
What you will learn
- Read a real conflict: the base, the current side and the incoming side.
- Explain why "ours" and "theirs" swap between a merge and a rebase.
- Write one deliberate resolution, then mark it resolved.
- Continue the operation and check that nothing is left half-done.
Before you start
Two teammates changed the same line of the page. On conflict/sara, Sara changed the hero heading to "Review changes with confidence." On conflict/omar, Omar changed the same line to "Understand each saved change." Both branches start from the commit M, where the heading was "See your work clearly.", and each branch has exactly one commit. You make two identical copies of this divergence: one for the merge (gittree-conflict-merge) and one for the rebase (gittree-conflict-rebase). In both, conflict/sara is checked out and the work tree is clean. No merge or rebase is running.
Everything is local and on the Free plan (GitTree asks you to sign in): no hosting account, no provider, no internet. Every identity is fictional (example.com). Never run this lesson in a real project.
Make the changes in GitTree or with the native Git commands, not both in the same copy. Reading the result is always safe. The menu labels come from GitTree 2.0's own strings and the native commands were run with Git 2.43 in disposable copies. The GitTree steps were not run on macOS, Windows or Linux.
Concept and state effects
A content conflict means Git cannot pick one result on its own: both sides changed the same lines differently. Git stops and keeps three versions of the file in the index: stage 1 is the common base, stage 2 is "ours" and stage 3 is "theirs". It also writes conflict markers into the file in your work tree. Removing the markers in an editor does not resolve anything for Git: the file is resolved only when you save the result and stage it, which brings back stage 0.
"Ours" and "theirs" are positions, not people. In a merge you are on Sara's branch, so ours is Sara's version (the current side) and theirs is Omar's (the incoming side). In a rebase of Sara onto Omar, Git first moves to Omar's branch and then replays Sara's commit, so ours is Omar's version (the branch you are rebasing onto) and theirs is Sara's commit. GitTree prints the role next to each name so you do not have to remember: "(current)" and "(incoming)" in a merge, "(rebasing onto)" and "(your commit)" in a rebase.
The right result is rarely one side. Here the intended resolution is "Review each change with confidence.", neither Sara's line nor Omar's. Keep every other line of the page exactly as it was. Marking the file resolved stages your result; continuing is a separate step that finishes the merge commit or recreates Sara's commit on Omar's.
While the operation is stopped, the branch is where the operation left it and the file is conflicted. Nothing reaches a server.
Prepare the exact project state
Make one divergence, then copy it twice. Start from a separate ordinary copy of the project as lesson 18 left it, and leave your original gittree-features untouched. The page versions are below: MERGED is the shared base, SARA and OMAR are the two competing versions, and RESOLVED is the result you will save.
Complete file inputs
Save only at the phase named below. These are full UTF-8/LF bodies with a final newline; never paste Markdown fences. Unchanged runnable files inherit the named input. Versions in separate variants must not overwrite the original.
index.html — MERGED
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GitTree Features — course demo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="site-header">
<a href="#main">GitTree Features</a>
</header>
<main id="main" tabindex="-1">
<section class="hero" aria-labelledby="hero-title">
<p class="eyebrow">GitTree Learn demo project</p>
<h1 id="hero-title">See your work clearly.</h1>
<p id="hero-copy">Choose one change, then check what Git saved.</p>
</section>
<section class="features" aria-labelledby="features-title">
<h2 id="features-title">Follow your work</h2>
<div class="cards">
<article class="feature-card">
<h3>History</h3>
<p>Read commits and branches together.</p>
</article>
<article class="feature-card">
<h3>Changes</h3>
<p>Review the exact content selected for a commit.</p>
</article>
<article class="feature-card">
<h3>Recovery</h3>
<p>Inspect the scope of a recovery point before using it.</p>
</article>
</div>
</section>
<section aria-labelledby="plans-title">
<h2 id="plans-title">Choose the workflow you need</h2>
<div class="cards">
<article class="feature-card">
<h3>Local Git</h3>
<p>Local Git workflows work without signing in.</p>
</article>
<article class="feature-card">
<h3>Connected workflows</h3>
<p>Provider integrations and Nour require Pro or an applicable trial.</p>
</article>
</div>
</section>
</main>
<footer><p>Built by a fictional team for GitTree Learn. Review one change at a time.</p></footer>
<script type="module" src="site.js"></script>
</body>
</html>index.html — SARA
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GitTree Features — course demo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="site-header">
<a href="#main">GitTree Features</a>
</header>
<main id="main" tabindex="-1">
<section class="hero" aria-labelledby="hero-title">
<p class="eyebrow">GitTree Learn demo project</p>
<h1 id="hero-title">Review changes with confidence.</h1>
<p id="hero-copy">Choose one change, then check what Git saved.</p>
</section>
<section class="features" aria-labelledby="features-title">
<h2 id="features-title">Follow your work</h2>
<div class="cards">
<article class="feature-card">
<h3>History</h3>
<p>Read commits and branches together.</p>
</article>
<article class="feature-card">
<h3>Changes</h3>
<p>Review the exact content selected for a commit.</p>
</article>
<article class="feature-card">
<h3>Recovery</h3>
<p>Inspect the scope of a recovery point before using it.</p>
</article>
</div>
</section>
<section aria-labelledby="plans-title">
<h2 id="plans-title">Choose the workflow you need</h2>
<div class="cards">
<article class="feature-card">
<h3>Local Git</h3>
<p>Local Git workflows work without signing in.</p>
</article>
<article class="feature-card">
<h3>Connected workflows</h3>
<p>Provider integrations and Nour require Pro or an applicable trial.</p>
</article>
</div>
</section>
</main>
<footer><p>Built by a fictional team for GitTree Learn. Review one change at a time.</p></footer>
<script type="module" src="site.js"></script>
</body>
</html>index.html — OMAR
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GitTree Features — course demo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="site-header">
<a href="#main">GitTree Features</a>
</header>
<main id="main" tabindex="-1">
<section class="hero" aria-labelledby="hero-title">
<p class="eyebrow">GitTree Learn demo project</p>
<h1 id="hero-title">Understand each saved change.</h1>
<p id="hero-copy">Choose one change, then check what Git saved.</p>
</section>
<section class="features" aria-labelledby="features-title">
<h2 id="features-title">Follow your work</h2>
<div class="cards">
<article class="feature-card">
<h3>History</h3>
<p>Read commits and branches together.</p>
</article>
<article class="feature-card">
<h3>Changes</h3>
<p>Review the exact content selected for a commit.</p>
</article>
<article class="feature-card">
<h3>Recovery</h3>
<p>Inspect the scope of a recovery point before using it.</p>
</article>
</div>
</section>
<section aria-labelledby="plans-title">
<h2 id="plans-title">Choose the workflow you need</h2>
<div class="cards">
<article class="feature-card">
<h3>Local Git</h3>
<p>Local Git workflows work without signing in.</p>
</article>
<article class="feature-card">
<h3>Connected workflows</h3>
<p>Provider integrations and Nour require Pro or an applicable trial.</p>
</article>
</div>
</section>
</main>
<footer><p>Built by a fictional team for GitTree Learn. Review one change at a time.</p></footer>
<script type="module" src="site.js"></script>
</body>
</html>index.html — RESOLVED
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GitTree Features — course demo</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="site-header">
<a href="#main">GitTree Features</a>
</header>
<main id="main" tabindex="-1">
<section class="hero" aria-labelledby="hero-title">
<p class="eyebrow">GitTree Learn demo project</p>
<h1 id="hero-title">Review each change with confidence.</h1>
<p id="hero-copy">Choose one change, then check what Git saved.</p>
</section>
<section class="features" aria-labelledby="features-title">
<h2 id="features-title">Follow your work</h2>
<div class="cards">
<article class="feature-card">
<h3>History</h3>
<p>Read commits and branches together.</p>
</article>
<article class="feature-card">
<h3>Changes</h3>
<p>Review the exact content selected for a commit.</p>
</article>
<article class="feature-card">
<h3>Recovery</h3>
<p>Inspect the scope of a recovery point before using it.</p>
</article>
</div>
</section>
<section aria-labelledby="plans-title">
<h2 id="plans-title">Choose the workflow you need</h2>
<div class="cards">
<article class="feature-card">
<h3>Local Git</h3>
<p>Local Git workflows work without signing in.</p>
</article>
<article class="feature-card">
<h3>Connected workflows</h3>
<p>Provider integrations and Nour require Pro or an applicable trial.</p>
</article>
</div>
</section>
</main>
<footer><p>Built by a fictional team for GitTree Learn. Review one change at a time.</p></footer>
<script type="module" src="site.js"></script>
</body>
</html>In GitTree
Open the palette with Command+Shift+P (Ctrl+Shift+P on Ubuntu). Actions on a named branch use that branch's right-click menu.
- Build the two copies as the next section shows. Open gittree-conflict-merge, with
conflict/sarachecked out. - Right-click
conflict/omarand choose Merge conflict/omar into conflict/sara. Under Record it as choose Always a merge commit. The dialog warns "Will stop with a conflict in 1 file"; choose Merge. The merge stops, which is the expected result. - The banner reads "Merging conflict/omar into conflict/sara" with "1 conflict", and Continue is not available yet: "Resolve the 1 conflicts first". Choose Resolve.
- Open
index.htmlfrom Conflicted files. Read the two sides, conflict/sara (current) and conflict/omar (incoming), and use Show Base to read the Base (common ancestor). - Choose Edit Output, replace the conflicting heading with
Review each change with confidence.and keep every other line. Then choose Save and Mark Resolved. Deleting the markers and closing the file is not enough. - Back on the banner choose Continue. If a message is offered, keep the proposed one. Wait for the merge to finish: the new commit has two parents.
- Open gittree-conflict-rebase, with
conflict/sarachecked out. Right-clickconflict/omarand choose Rebase conflict/sara onto conflict/omar. Leave the options under Options for this rebase off, then choose Rebase conflict/sara. It stops with a conflict. - Choose Resolve and open
index.html. The sides are now conflict/omar (rebasing onto) and conflict/sara (your commit): the roles swapped. Do the same Edit Output → Save and Mark Resolved, then Continue. - Check both copies: the file equals RESOLVED, no conflicted file is left and no banner is showing. Keep the two results apart and do not push.
For every ordinary commit in the course: Summary is the stated subject, Description is empty and the options Amend Previous Commit, Skip Hooks, Add Signed-off-by, Different Author, Push After Commit and Allow Empty Commit stay off.
In the terminal
In a separate clean copy of lesson 18's result:
git switch --create conflict/sara
BASE_SHA="$(git rev-parse HEAD)"Save SARA as index.html, then:
git add -- index.html
git -c user.name="Sara Lindqvist" -c user.email=sara@example.com commit --quiet -m "feat: Sara clarifies the hero"
git switch --quiet --create conflict/omar "$BASE_SHA"Save OMAR as index.html, then:
git add -- index.html
git -c user.name="Omar Haddad" -c user.email=omar@example.com commit --quiet -m "feat: Omar explains the hero"
git switch --quiet conflict/saraBASE_SHA is M, before either competing commit. Make two clean copies of this divergence, one for the merge and one for the rebase (cd .. && cp -R gittree-features gittree-conflict-merge && cp -R gittree-features gittree-conflict-rebase). In the merge copy:
git merge --no-ff --no-edit conflict/omar
git ls-files --unmerged
git show :2:index.html
git show :3:index.htmlThe merge stops with exit code 1 and git ls-files --unmerged lists three entries, stages 1, 2 and 3. git show :2:index.html is Sara's version (ours) and :3: is Omar's (theirs); :1: is the base. Save RESOLVED as index.html, then:
git add -- index.html
git -c core.editor=true merge --continue
git ls-files --unmerged
git status --shortThe last two commands print nothing. In the untouched rebase copy:
git rebase conflict/omar
git show :2:index.html
git show :3:index.htmlThe rebase stops. Now :2: is Omar's version and :3: is Sara's: the roles swapped. Save RESOLVED as index.html, then:
git add -- index.html
git -c core.editor=true rebase --continue
git ls-files --unmerged
git status --shortBoth files now equal RESOLVED and nothing is unmerged. core.editor=true only accepts the existing commit message for this one command; it does not change your editor setting. To leave a stopped operation without resolving, use git merge --abort or git rebase --abort.
Result
Where you end: the merge copy has a new commit with two parents and the combined heading; the rebase copy has Sara's commit recreated on Omar's with the same combined heading. In both, index.html equals RESOLVED, no conflict entry is left, the operation is over and the status is clean.
Common mistakes
- Assuming "ours" always means your own feature. It means the side Git is standing on, and that changes with the operation.
- Taking one side wholesale ("Take All") before reading both versions and the rest of the file.
- Deleting the conflict markers but never saving and marking the file resolved.
- Repeating a merge or rebase that has already finished.
If something goes wrong
If a stop looks wrong, Abort… from the banner restores the state the operation started from and drops the resolutions made so far; GitTree keeps a recovery snapshot first. If the file changed while you were editing, use the Compare and Use as Output choices rather than forcing an old save. Keep a copy of your draft before replacing it.
Check your understanding
Answer each question, then read why. Questions marked “Required” count toward completing the lesson.
0 of 6 correct
Answer the required questions correctly to complete the lesson.
Checked in this browser.
Practice
Prove the stage roles for both operations with git show :2:index.html and git show :3:index.html, then save the same RESOLVED file and finish each operation. Predict what would be left in the page if you blindly took "ours" in both copies.
Your own confirmation: the site cannot see your computer.
To complete the lesson, answer the required questions correctly and confirm the practical exercise.
Recap
- The roles of ours and theirs depend on the operation.
- Save and mark resolved first, then continue.
- Check the whole file and the operation state afterwards.
Related
Glossary terms introduced
Conflict — تعارض; base — الأساس; ours — نسختنا; theirs — نسختهم; resolve — حلّ.
Video transcript
The narration of the video, cue by cue.
Show the transcript
- A conflict is not an error. It is a question only you can answer.
- Two people changed the same line of the same file.
- Sara wrote one heading. Omar wrote another. Both started from the same page.
- Git cannot guess which line wins, so it stops and asks you.
- First, build Sara's and Omar's branches, and two identical copies.
- In the first copy we merge. In the second, we rebase.
- Open the merge copy, on Sara's branch.
- Right-click Omar's branch and choose its Merge command.
- The dialog already warns: this will stop with a conflict in one file.
- Choose Always a merge commit, then Merge once. It stops, as expected.
- The banner shows one conflict. Continue is not available yet.
- Choose Resolve, and open index.html.
- Two sides: Sara's branch is current, Omar's is incoming.
- Show Base reveals the common ancestor, the page before either change.
- Now Edit Output. Replace the heading with one deliberate line.
- Save and Mark Resolved. Only now does Git treat the file as resolved.
- Back on the banner, choose Continue. The merge finishes.
- The new commit has two parents, and the combined heading.
- Now the rebase copy, also on Sara's branch.
- Right-click Omar's branch, and rebase Sara's branch onto it.
- Leave the options off, and rebase once. It stops with a conflict.
- Look at the labels. Now Omar's branch is the one we rebase onto.
- Sara's commit is the one being replayed. The roles have swapped.
- Same resolution: Edit Output, then Save and Mark Resolved, then Continue.
- Sara's commit is recreated on top of Omar's, with the same heading.
- In the terminal, list the unmerged entries of a stopped merge.
- Stage two is ours: Sara's heading. Stage three is theirs: Omar's.
- In a stopped rebase, stages two and three are the other way round.
- After the fix, nothing is unmerged, and the status is empty.
- The merge copy's tip has two parents; the rebase copy's tip has one.
- Change the input: delete the markers and save, but do not mark resolved.
- Git still sees three stages, so Continue stays unavailable.
- Stuck? Abort puts the branch back where the operation began.
- GitTree saves a recovery snapshot first, so even an abort is safe.
- Never take all from one side before you read both versions.
- Practice: prove the stage roles in both copies, then finish both.
- Next, continue, skip or abort an operation that has stopped.