Chapter 07Lesson 05~135 minutes

Checkpoint Lab — Remotes, Refspecs, Fetch, Pull, Push, SSH, HTTPS, and Protocol Behavior

Checkpoint remote mechanics with one bare repository and two clones: stale observations, non-fast-forward rejection, safe integration, dry-run push, explicit-lease rewrite, and stale-lease protection.

CheckpointTwo clonesSafe integrationLease protection

Learning objectives

  • Predict and verify how live remote refs and local remote-tracking refs diverge and resynchronize.
  • Trigger a non-fast-forward push rejection and repair it through fetch, graph inspection, merge, and normal push.
  • Verify that fetch and local integration move different refs at different times.
  • Perform one controlled rewritten-history push using an exact expected-OID lease.
  • Prove the same lease rejects after a second clone advances the remote branch.

1. Checkpoint scenario — one bare remote, two independent clones

You will create remote.git, then clones named alice and bob. The lab has three phases: observe stale remote-tracking state, trigger and safely repair a non-fast-forward rejection, then demonstrate both the success and protective rejection of an explicit force-with-lease.

2. Write predictions before commands change refs

  1. After Bob pushes while Alice does nothing, will Alice's origin/trunk move automatically?
  2. When Alice and Bob both commit from the same old base, will Alice's normal push be accepted after Bob pushes first?
  3. After git fetch origin, which should change first in Alice: trunk or origin/trunk?
  4. If Alice supplies an explicit lease OID and Bob moves the branch afterward, should Alice's lease-protected force push succeed?

3. Setup: seed the bare remote and make two clones

Git Bash, Bash, or zsh

mkdir git-remote-checkpoint
cd git-remote-checkpoint
git init --bare --initial-branch=trunk remote.git
git init -b trunk seed
printf "baseline
" > seed/shared.txt
git -C seed config user.name "Seed User"
git -C seed config user.email "seed@example.invalid"
git -C seed add shared.txt
git -C seed commit -m "Create shared baseline"
git -C seed remote add origin ../remote.git
git -C seed push -u origin trunk
git clone remote.git alice
git clone remote.git bob
git -C alice config user.name "Alice"
git -C alice config user.email "alice@example.invalid"
git -C bob config user.name "Bob"
git -C bob config user.email "bob@example.invalid"

PowerShell file creation

New-Item -ItemType Directory git-remote-checkpoint | Out-Null
Set-Location git-remote-checkpoint
git init --bare --initial-branch=trunk remote.git
git init -b trunk seed
Set-Content seed/shared.txt 'baseline'
# Continue with the same git -C commands shown above.

4. Preflight — prove URLs, upstreams, live remote HEAD, and local refs

git -C alice status --short --branch
git -C alice remote -v
git -C alice branch -vv
git -C alice config --get-regexp '^(remote\.origin|branch\.trunk)'
git -C alice ls-remote --symref origin HEAD refs/heads/trunk
git -C alice rev-parse trunk
git -C alice rev-parse origin/trunk

At this point Alice's local branch and remote-tracking ref should agree with the live remote trunk.

5. Bob pushes independent work; Alice's tracking ref becomes stale

Git Bash / Bash / zsh

printf "bob change
" > bob/bob.txt
git -C bob add bob.txt
git -C bob commit -m "Add Bob operational note"
git -C bob push origin trunk

git -C alice rev-parse origin/trunk
git -C alice ls-remote origin refs/heads/trunk

PowerShell file creation

Set-Content bob/bob.txt 'bob change'
git -C bob add bob.txt
git -C bob commit -m "Add Bob operational note"
git -C bob push origin trunk

Verify prediction 1: the live remote OID changed, but Alice's origin/trunk still records what Alice last knew.

6. Alice creates a different commit from the old base

Git Bash / Bash / zsh

printf "alice change
" > alice/alice.txt
git -C alice add alice.txt
git -C alice commit -m "Add Alice deployment note"
git -C alice status --short --branch
git -C alice log --graph --decorate --oneline --all -6

Alice has not fetched Bob's commit, so her local graph does not yet include the remote branch's new tip.

7. Trigger the non-fast-forward rejection intentionally

This push is safe to attempt in the lab because Git should reject it without changing the remote.
git -C alice push origin trunk

Verify prediction 2: the push is rejected because remote trunk contains Bob's commit while Alice proposes a different descendant of the old base. Do not add --force.

8. Fetch, inspect divergence, merge safely, then push

git -C alice fetch origin
git -C alice rev-list --left-right --count trunk...origin/trunk
git -C alice log --graph --decorate --oneline --all -8
git -C alice diff trunk...origin/trunk
git -C alice merge --no-edit origin/trunk
git -C alice log --graph --decorate --oneline --all -8
git -C alice push --dry-run origin trunk
git -C alice push origin trunk

Verify prediction 3: fetch moved origin/trunk first; the merge then moved local trunk to a new merge commit because the branches had diverged; the final push advanced the remote normally.

9. Bob fetches the integrated remote without moving his local branch

git -C bob rev-parse trunk
git -C bob rev-parse origin/trunk
git -C bob fetch origin
git -C bob rev-parse trunk
git -C bob rev-parse origin/trunk
git -C bob log --graph --decorate --oneline --all -8
git -C bob merge --ff-only origin/trunk

Again, fetch updates Bob's remote-tracking ref first. Because Alice's merge commit contains Bob's earlier commit, Bob can then fast-forward his local trunk.

10. Controlled rewrite with a successful explicit lease

Disposable branch only. This phase deliberately rewrites a pushed commit. Never copy it onto a shared protected branch without explicit team policy.

Git Bash / Bash / zsh

git -C alice switch -c rewrite-demo trunk
printf "version one
" > alice/rewrite.txt
git -C alice add rewrite.txt
git -C alice commit -m "Add lease demonstration"
git -C alice push -u origin rewrite-demo
printf "version two
" > alice/rewrite.txt
git -C alice add rewrite.txt
git -C alice commit -m "Update lease demonstration"
git -C alice push origin rewrite-demo
EXPECTED=$(git -C alice rev-parse origin/rewrite-demo)
printf "version two corrected
" > alice/rewrite.txt
git -C alice add rewrite.txt
git -C alice commit --amend -m "Correct lease demonstration"
git -C alice push --dry-run --force-with-lease=refs/heads/rewrite-demo:$EXPECTED origin HEAD:refs/heads/rewrite-demo
git -C alice push --force-with-lease=refs/heads/rewrite-demo:$EXPECTED origin HEAD:refs/heads/rewrite-demo

PowerShell lease variable

$EXPECTED = git -C alice rev-parse origin/rewrite-demo
git -C alice push --dry-run "--force-with-lease=refs/heads/rewrite-demo:$EXPECTED" origin HEAD:refs/heads/rewrite-demo
git -C alice push "--force-with-lease=refs/heads/rewrite-demo:$EXPECTED" origin HEAD:refs/heads/rewrite-demo

The force is permitted only because the remote still equals the exact OID Alice recorded. The amended commit has a new OID, so a normal push would reject the non-fast-forward replacement.

11. Prove the lease blocks Alice after Bob advances the same branch

git -C bob fetch origin
git -C bob switch -c rewrite-demo --track origin/rewrite-demo

Have Bob add bob-lease.txt, commit, and push. Alice intentionally does not fetch. Her local origin/rewrite-demo therefore remains the old expected value. Then:

Git Bash / Bash / zsh

STALE_EXPECTED=$(git -C alice rev-parse origin/rewrite-demo)
git -C alice push --force-with-lease=refs/heads/rewrite-demo:$STALE_EXPECTED origin HEAD:refs/heads/rewrite-demo

PowerShell

$STALE_EXPECTED = git -C alice rev-parse origin/rewrite-demo
git -C alice push "--force-with-lease=refs/heads/rewrite-demo:$STALE_EXPECTED" origin HEAD:refs/heads/rewrite-demo

Verify prediction 4: the push is rejected because Bob moved the remote branch after Alice's observation. A plain --force could move the branch backward to Alice's tip and make Bob's new commit disappear from that remote branch. The checkpoint intentionally does not do that.

12. Refresh Alice's observation after the protected rejection

git -C alice ls-remote origin refs/heads/rewrite-demo
git -C alice rev-parse origin/rewrite-demo
git -C alice fetch origin
git -C alice rev-parse origin/rewrite-demo
git -C alice log --graph --decorate --oneline --all -12
git -C alice switch trunk

The stale remote-tracking ref now catches up to the live remote branch, preserving Bob's work.

13. Produce a compact remote-state report

git -C alice status --short --branch
git -C alice branch -vv
git -C alice remote -v
git -C alice config --get-regexp '^(remote\.origin|branch\.)'
git -C alice ls-remote --symref origin HEAD refs/heads/trunk refs/heads/rewrite-demo
git -C alice log --graph --decorate --oneline --all -12

Write beside the report which values are local refs, which are live remote observations, and which config keys determine fetch/push behavior. That annotation is part of the checkpoint.

14. Verification checklist

  • The bare remote's HEAD names trunk.
  • Alice and Bob both began with trunk tracking origin/trunk.
  • Bob's first push changed the remote while Alice's remote-tracking ref remained stale.
  • Alice's first push after divergence was rejected non-fast-forward.
  • Alice fetched, inspected divergence, created a merge commit, dry-ran the push, and then pushed normally.
  • Bob fetched before his local branch moved, then fast-forwarded explicitly.
  • The first rewrite-demo force update used an explicit expected OID and succeeded.
  • After Bob advanced rewrite-demo, Alice's stale lease rejected the overwrite.
  • Plain --force was never used.
  • No hosted account, real credential, or network server was required.

15. Cleanup

Preflight: confirm you are deleting only the disposable git-remote-checkpoint directory.

Git Bash / Bash / zsh

git -C alice status --short --branch
cd ..
pwd
rm -rf git-remote-checkpoint

PowerShell

git -C alice status --short --branch
Set-Location ..
Get-Location
Remove-Item -Recurse -Force git-remote-checkpoint

16. Knowledge check

Question 1. Bob pushed, but Alice's origin/trunk did not change. Is that evidence that Bob's push failed?

Question 2. Why was Alice's normal push rejected after both clones committed independently?

Question 3. Why did Bob's fetch not immediately move his local trunk?

Question 4. What safety property did the stale explicit lease demonstrate?

Question 5. In CI, why might git pull be less desirable than an explicit fetch plus ref/OID selection?

17. What Chapter 07 adds to a production Git operating model

You can now reason about a remote as an independent ref/object database, distinguish live remote state from local tracking state, inspect and configure ref mappings/upstreams, choose pull behavior explicitly, validate push destinations before writes, classify SSH/HTTPS failures by trust/auth layer, and use a lease when a controlled rewrite is genuinely necessary.

18. Chapter checkpoint summary

Fetch changes local observations; integration changes your local branch/history; push asks another repository to update refs; authentication merely establishes who is communicating, while server policy decides what that principal may update. Those mechanics are the foundation for collaboration workflows in Chapter 08.

Next chapter

Collaborative Workflows: Forks, Upstreams, Pull Requests, and Reviewable History

Chapter 08 layers team review conventions and hosting-platform governance on top of the remote/ref mechanics you can now reproduce entirely with local Git.

Authoritative references

 git-fetch
 git-push
 git-pull
 git-remote
 git-config

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.