Skip to content
GitTree

Lesson 12 · Everyday work

Pull with fast-forward, merge or rebase

Choose an explicit pull policy after inspecting divergence. Compare fast-forward-only refusal, merge and private rebase using three copies and one isolated local server.

Progress saved in this browser.

Playing loads the video from YouTube (privacy-enhanced mode).

English narration · English and Arabic captions

Open the video pageWatch on YouTube

What you will learn

  • Separate fetch from pull integration.
  • Compare three explicit policy choices.
  • Recognize ff-only refusal as a policy result.
  • Review upstream and persistent app policy settings.

Before you start

Start from the course project as lesson 11 left it, with main brought up to Leo's fetched commit; call that commit M. You then have three ordinary copies of one divergence: a private topic with one commit S, and a server branch updated-main with one different commit N, both on M. The private topic is current and clean. Each points origin only to a new bare server containing updated-main/N and tracks origin/updated-main; old published pricing/main remote is not mutated.

The video was recorded with Git 2.52.0 and GitTree 2.0 on Ubuntu 24.04: the divergence, the rescue branch and the new server were built in the terminal; the three pulls, the result views, the policy file and the Pull-mode restore were done in the app; the results were checked in the terminal. Nothing was run on macOS or Windows, and Ubuntu is the only Linux system recorded. GitTree's source Git floor is 2.39.0.

GitTree asks you to sign in; the Free plan then covers every step here. No provider account, Pro/trial, API credential or internet service is needed. A local bare origin is not GitLab or CI. All identities use example.com; never run this lesson inside a source checkout.

Make mutations through either GitTree or the native path at identical inputs, not both consecutively. Read-only verification can follow either. Any preparation described as terminal work is labeled native preparation, never an invented app feature.

Concept and state effects

Pull is fetch followed by the selected integration policy; it is not a synonym for refresh. A fetch phase may update tracking refs even when ff-only later refuses to move the local branch.

On diverged private S/N, ff-only refuses, merge preserves both paths, and rebase replays the private commit on the fetched base. Do not silently change mode to make a refusal disappear.

The palette Pull mode commands update general.pullMode, an app-wide preference, not a one-shot Git flag. Record/restore the prior setting in the recording app. Native explicit pull flags below do not change that app setting.

Each pull fetches its sandbox origin. FF-only leaves topic/index/work at S; merge and rebase update only their own topic and files. No server branch is pushed. Choosing palette policy also changes the app-wide preference until restored.

Prepare the exact project state

Begin from a separate ordinary copy of the clean course project as lesson 11 left it. The original gittree-features stays preserved; do not run this scenario in the original or a source checkout. (An earlier plan of the course started this lesson from a later merge lesson's result; that lesson is not recorded yet, so the video uses lesson 11's project and the same steps.) Then perform the exact preparation below. Repo-local demo signing is off and GitTree Demo <demo@example.com> is the identity unless a teammate is explicitly named.

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 — SIGNUP

File excerpt · html
<!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>
    <section aria-labelledby="signup-title">
      <h2 id="signup-title">Try the demo form</h2>
      <form id="signup-form" novalidate>
        <label for="demo-email">Demo email</label>
        <input id="demo-email" name="email" type="email" autocomplete="off" aria-describedby="signup-hint">
        <p id="signup-hint">Use a fictional example.com address. Nothing is sent.</p>
        <button type="submit">Check the address locally</button>
        <p id="signup-result" role="status"></p>
      </form>
    </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>

docs/integration-note.md — DOC_MERGE

Output
# Integration review

This note was written on main while signup work was private.

docs/pull-policy.md — DOC_PULL

Output
# Pull policy

Fetch first and inspect ancestry.
Use fast-forward only when no integration decision is needed.
Merge preserves existing commit identities; rebase recreates private commits.
Never rewrite someone else's published work without coordination.

In GitTree

Palette: Command+Shift+P (Ctrl+Shift+P on Ubuntu); type the exact named command and select it. Named-ref actions use that row's context menu, not an argument-free palette shortcut.

  1. Prepare the three actual isolated copies and their own server/upstream below; record previous general.pullMode before choosing a policy.
  2. In FF copy, palette Pull: Fast-Forward Only, then the toolbar Pull button; wait for the genuine divergence refusal. The app's notice reads “feature/signup-form cannot fast-forward”, says both sides have commits the other does not, that nothing was changed, and offers Resolve… to merge, rebase or reset. It stays until you dismiss it, so dismiss it before the next copy.
  3. Inspect topic tip/work unchanged even though fetched origin/updated-main is current; do not press Force Push….
  4. In untouched Merge copy, choose Pull: Fast-Forward if Possible, Else Merge, then the toolbar Pull button; inspect actual two-parent result.
  5. In untouched Rebase copy, choose Pull: Rebase, then Pull; current topic is private, no autostash needed.
  6. Verify recreated S′ on N, merge parents and equal full trees. Preserve each result independently.
  7. Save full DOC_PULL as docs/pull-policy.md in a practice copy only after preserving the compared snapshots; leave it uncommitted here.
  8. Restore the recording app's previous Pull mode explicitly; verify no automatic push or original-main change occurred.

For every ordinary commit: Summary is the stated subject; Description empty; Amend Previous Commit, Skip Hooks, Add Signed-off-by, Different Author, Push After Commit and Allow Empty Commit off; no co-authors. Stage only the named files, then Commit Changes to N Files once (File for one), or Command+Enter as an alternative. Wait for the real result. A lesson that explicitly amends changes only that stated option.

In the terminal

In a separate clean copy of the project, first bring main to Leo's fetched commit (that is M), then create the private topic:

Command · bash
git merge --ff-only --quiet origin/main
git switch --create feature/signup-form

Put the saved SIGNUP file over index.html (the video saved the lesson's files in ~/Downloads and uses cp ~/Downloads/SIGNUP.html index.html), then:

Command · bash
git add -- index.html
git commit --quiet -m "feat: add the local signup demonstration"
git branch rescue/signup-before-rebase
BASE_SHA="$(git rev-parse HEAD~1)"
git switch --quiet --create updated-main "$BASE_SHA"

On updated-main put DOC_MERGE at docs/integration-note.md (cp ~/Downloads/DOC_MERGE.md docs/integration-note.md), then:

Command · bash
git add -- docs/integration-note.md
git commit --quiet -m "docs: add the integration review"
git switch --quiet feature/signup-form

BASE_SHA is the measured M parent before the private signup commit. Both topic and updated-main now have one independent commit; rescue/signup-before-rebase retains the old topic. Nothing from this topic is published.

From that pre-rebase copy, prepare a new server containing only N:

Command · bash
git clone --quiet --bare --single-branch --branch updated-main --no-local . ../gittree-pull-origin.git

Make three ordinary copies of the same input with cp -R <source-demo-copy> <new-copy> while no Git/app operation is active, or equivalent verified filesystem copies preserving .git. In each copy replace origin with the measured file URL for that new server, fetch and set the topic upstream; never retarget the original:

Command · bash
git remote set-url origin "<measured-file-url>"
git fetch --quiet --prune origin
git branch --set-upstream-to=origin/updated-main feature/signup-form

Use the measured file URL in place of the placeholder. In FF copy:

Command · bash
git pull --quiet --ff-only origin updated-main

Measured exit 128; diagnostic excerpt:

Output
hint: Diverging branches can't be fast-forwarded, you need to either:
hint:
hint: 	git merge --no-ff
hint:
hint: or:
hint:
hint: 	git rebase
hint:
hint: Disable this message with "git config set advice.diverging false"
fatal: Not possible to fast-forward, aborting.

In separate Merge copy:

Command · bash
git pull --quiet --no-rebase --no-edit origin updated-main

In separate Rebase copy:

Command · bash
git pull --quiet --rebase origin updated-main

Both succeeded; actual final tree IDs matched, merge had two parents and replay one. For each inspect:

Command · bash
git rev-list --parents -n 1 HEAD
git rev-parse 'HEAD^{tree}'
git status --short

SHAs vary; statuses were clean before the optional policy-file practice. No command pushes a branch.

Result

Where you end: FF-only copy remains at S with refusal after fetch. Merge copy adds a two-parent integration; rebase copy creates S′ on N. Merge/rebase trees agree; no push. Preserve policy file as uncommitted practice after comparisons only.

Common mistakes

  • Assuming a straight graph means the old commit ID survived.
  • Treating the alternative pane as the original checkpoint.
  • Repeating a completed mutation after app success.

If something goes wrong

FF refusal is not data loss. Inspect fetched/local refs and choose a reviewed policy for that copy. If merge/rebase stops, resolve/Continue or explicitly Abort; do not run another pull over an active operation.

Check your understanding

Answer each question, then read why. Questions marked “Required” count toward completing the lesson.

  1. Question 1 of 6

    What is pull here?

    Choose one answer.

  2. Question 2 of 6

    What can happen on ff-only refusal?

    Choose every answer that applies.

  3. Question 3 of 6
    Required

    Private replay onto N: resulting parent?

    Choose one answer.

  4. Question 4 of 6
    Required

    Change current work to published shared topic: rebase still automatic advice?

    Choose one answer.

  5. Question 5 of 6

    Palette policy command changes only one pull?

    Choose one answer.

  6. Question 6 of 6
    Required

    What proves comparison?

    Choose every answer that applies.

0 of 6 correct

Answer the required questions correctly to complete the lesson.

Checked in this browser.

Practice

Explain which state changed in the refused copy, compare the two successful trees and parents, then write the full DOC_PULL policy. Verify the recording app's preference was restored.

Your own confirmation: the site cannot see your computer.

To complete the lesson, answer the required questions correctly and confirm the practical exercise.

Recap

  • Pull includes an integration policy.
  • Refusal can follow a successful fetch.
  • Policy persistence needs explicit review.

Glossary terms introduced

Rebase — إعادة تأسيس; replay — إعادة تطبيق; rescue ref — مرجع حفظ.

Video transcript

The narration of the video, cue by cue.

Show the transcript
  1. Start from the exact checkpoint.
  2. Name the branch, index and working version.
  3. Prepare only the isolated course copy.
  4. Name the upstream and preserve all three inputs.
  5. Fast-forward only refuses this divergence.
  6. A refusal can follow a successful fetch.
  7. The merge policy preserves both histories.
  8. Replay only the private topic.
  9. Compare parents and complete trees separately.
  10. Write the reviewed pull policy afterward.
  11. Restore the previous app-wide policy.
  12. What is pull here?
  13. Pull has a transport phase and policy-governed local phase.
  14. What can happen on ff-only refusal?
  15. Fetch can finish before integration refuses divergence.
  16. Private replay onto N: resulting parent?
  17. Rebase creates S′ on N, unlike merge K.
  18. Change current work to published shared topic: rebase still automatic advice?
  19. Private-only assumptions no longer hold.
  20. Palette policy command changes only one pull?
  21. The source writes general.pullMode until another choice restores it.
  22. What proves comparison?
  23. Compare actual state rather than command silence.
  24. Verify the actual result, not just a message.
  25. Predict the changed input before acting.
  26. Preserve useful work before recovery.
  27. Prove the checkpoint before continuing.
  28. Keep the verified result for the next step.