SovranCode
HomeCourses Git & GitHub Git Project: Two Clone Sync
This device
Course contentsGit Project: Two Clone Sync · 41 topics

1. Git Fundamentals

Git IntroductionGit Version ControlGit vs GitHubGit InstallationGit Working TreeGit RepositoryGit CommitGit .gitignoreGit Project: Learning Journal

2. Git History

Git LogGit DiffGit ResetGit RevertGit StashGit Project: Messy Repository

3. Branches & History

Git BranchGit MergeGit Merge ConflictsGit RebaseGit Cherry-PickGit TagsGit Project: Feature Branch

4. Remotes and GitHub

Git RemoteGitHub AuthenticationGitHub RepositoryGitHub ForkGitHub IssuesGitHub PagesGit Project: Two Clone Sync

5. GitHub Collaboration

GitHub Pull RequestsGitHub Code ReviewGit Branching WorkflowGitHub Branch ProtectionGitHub CollaborationGitHub ContributingGit Project: Reviewed Pull Request

6. Automation and Professional Git

GitHub ActionsGit InternalsGit RecoveryGitHub SecurityGit Project: Team Repository CIProject
Learn Git & GitHub40 complete · 1 planned

1. Git Fundamentals

Git IntroductionGit Version ControlGit vs GitHubGit InstallationGit Working TreeGit RepositoryGit CommitGit .gitignoreGit Project: Learning Journal

2. Git History

Git LogGit DiffGit ResetGit RevertGit StashGit Project: Messy Repository

3. Branches & History

Git BranchGit MergeGit Merge ConflictsGit RebaseGit Cherry-PickGit TagsGit Project: Feature Branch

4. Remotes and GitHub

Git RemoteGitHub AuthenticationGitHub RepositoryGitHub ForkGitHub IssuesGitHub PagesGit Project: Two Clone Sync

5. GitHub Collaboration

GitHub Pull RequestsGitHub Code ReviewGit Branching WorkflowGitHub Branch ProtectionGitHub CollaborationGitHub ContributingGit Project: Reviewed Pull Request

6. Automation and Professional Git

GitHub ActionsGit InternalsGit RecoveryGitHub SecurityGit Project: Team Repository CIProject
PREVIOUS LESSONGitHub Pages
NEXT LESSONGitHub Pull Requests
4. Remotes and GitHub 120 min

Git Project: Two Clone Sync

Push a repository, clone it a second time, exchange commits, and prove both copies share the intended history.

UNIT 04 · REMOTES120 MINGITHUB REMOTE YOU OWN

Publish one small repository to GitHub, clone it a second time, exchange commits in both directions, recover a rejected push, and prove both working copies share the same tip.

MISSION PARAMETERS
Working copiesExactly 2
Shared remotesOne GitHub origin
Take-turns history3 commits
Force-pushForbidden
THE SETUP

One remote, two folders

Laptop and desk are two clones of the same GitHub repository. GitHub stores the objects. Each folder is a working copy. A second clone is not a fork.

THE OUTCOME

Tips you can prove

After the exchange and the recovered rejection, git rev-parse HEAD on both clones equals git ls-remote origin refs/heads/main. Copying files between folders is not the proof.

Project brief

You keep a shared log in log.md. You write from a laptop folder, then from a desk folder. GitHub is the named remote both folders push to and fetch from. The Git Remote lesson already practiced this with a bare folder. This project uses a GitHub repository you own, so authentication, clone URLs, and rejected pushes are real.

PublishCreate an empty GitHub repository and push the first local history.
ExchangeSend one commit from each clone and integrate it on the other side.
InspectFetch and read the graph before you merge or pull.
ProveMatch commit IDs across laptop, desk, and origin. Never force-push.
PROJECT WORKSPACE

Evidence tracker

Mark a gate complete only after the named command or review proves it. Progress is saved in this browser.

0%0/7 verified
  1. Open gate
  2. Open gate
  3. Open gate
  4. Open gate
  5. Open gate
  6. Open gate
  7. Open gate
Next evidence gate: Local shared log

Safety boundary

Disposable repository only

Create a new parent folder named two-clone-sync. Never run these commands inside SovranCode, your home directory, or valuable work.

A GitHub remote you own

Create sovrancode-two-clone-sync under your account. Do not push to someone else’s repository. Delete the practice repository when the evidence is recorded.

No force-push

A rejected push is a signal to fetch and integrate. git push --force and --force-with-lease fail this project.

No pull requests or forks

Stay on main. Do not open a pull request, fork, or add a second GitHub repository. The second folder is a clone of the same origin.

Confirm the root before every push

In the folder you are about to push from, run git rev-parse --show-toplevel. It must end in shared-log-laptop or shared-log-desk. Stop if it does not. Do not paste tokens, passwords, or private URLs into files or chat.

Repository contract

Two sibling folders

Create history in shared-log-laptop. Clone GitHub into shared-log-desk beside it. Do not copy log.md between folders by hand.

README.md

Tells a stranger how to clone the GitHub URL and the one rule: pull before you push.

log.md

Append-only dated lines. Each intended commit adds exactly one new line.

Empty GitHub create

No GitHub-generated README, license, or gitignore. Those files would be a different first commit than yours.

two-clone-sync/
├── shared-log-laptop/     ← first working copy (you create this)
└── shared-log-desk/       ← second working copy (git clone of GitHub)

GitHub repository (origin): sovrancode-two-clone-sync
Both folders talk to that one remote. They are not two GitHub repositories.

GitHub is the host. Git is the tool that records commits and talks to that host. The GitHub website is not a third working copy. A fork would be a different GitHub repository; this project has one.

Required graphs

AFTER TAKE-TURNS

Three commits, one line

* <afternoon> (HEAD -> main, origin/main) Add afternoon desk note
* <morning> Add morning laptop note
* <initial> Initialize shared log
AFTER REJECTION RECOVERY

One merge, both notes kept

*   <sync> (HEAD -> main, origin/main) Merge concurrent log notes
|\
| * <desk-extra> Add desk concurrent note
* | <laptop-extra> Add laptop concurrent note
|/
* <afternoon> Add afternoon desk note
* <morning> Add morning laptop note
* <initial> Initialize shared log
IDENTITY GATE

Same tip everywhere

Laptop HEAD, desk HEAD, and origin/main name one SHA.

git rev-parse HEAD
EXCHANGE GATE

Both directions

A laptop commit reached desk through origin. A desk commit reached laptop through origin.

git log --oneline --decorate --graph --all
SAFETY GATE

Rejected, then recovered

Evidence of a non-fast-forward rejection, then a merge that keeps both concurrent lines. No force-push.

git ls-remote origin refs/heads/main

Milestone 1 · Build the laptop copy

Make a parent directory, then initialize only the laptop folder. Commit the README contract and the first log line together. Record the commit ID. There is no remote yet.

mkdir -p two-clone-sync/shared-log-laptop
cd two-clone-sync/shared-log-laptop
git init -b main
git status

# Write README.md and log.md from the contracts below, then:
git add README.md log.md
git diff --cached
git commit -m "Initialize shared log"
git status
git rev-parse HEAD
README CONTRACT

Clone instructions and the one rule

# Shared log

A tiny notebook two clones keep in sync through GitHub.

## Clone

git clone https://github.com/OWNER/sovrancode-two-clone-sync.git

## Rule

Pull before you push. Never force-push. Append one dated line to log.md per commit.
LOG CONTRACT

One dated starting line

# Shared log

- 2026-09-21 initialize: project opened on laptop

Replace OWNER in the README with your GitHub username after the repository exists. The first commit can say OWNER; a later commit may correct the URL. Do not invent a second project inside this folder.

Milestone 2 · Publish an empty GitHub remote

Stay in shared-log-laptop. Create the GitHub repository empty, then inspect origin before you trust the push. Use the credential you already proved in GitHub Authentication. Public or private is your choice for a disposable practice repo; public is easier to delete later without leftover access lists.

git rev-parse --show-toplevel
gh auth status
gh repo create sovrancode-two-clone-sync --public --source=. --remote=origin --push

git remote -v
git branch -vv
git ls-remote origin
git rev-parse HEAD

gh repo create ... --push creates the GitHub repository, names it origin, and sends main. Omit any flag that adds a README. If you used the website instead, add origin yourself with the HTTPS or SSH URL that matches your credential, then run git push -u origin main.

Proof: git ls-remote origin lists the same object as git rev-parse HEAD. The GitHub page showing files is a website. The remote is the Git URL in git remote -v.

Milestone 3 · Clone a second working copy

Leave the laptop folder. Clone from GitHub into a sibling directory. Do not copy the laptop folder. Do not add a second remote. The new folder’s origin must be the same GitHub repository.

cd ..
git clone https://github.com/OWNER/sovrancode-two-clone-sync.git shared-log-desk
cd shared-log-desk

git remote -v
git branch -vv
git log --oneline --decorate --graph --all
git rev-parse HEAD
git ls-remote origin refs/heads/main

If you authenticated with SSH, clone git@github.com:OWNER/sovrancode-two-clone-sync.git instead. The transport must match a credential that works. After this milestone, laptop HEAD, desk HEAD, and origin/main are one commit. Record that SHA. You now have two working copies of one history.

Milestone 4 · Laptop publishes; desk pulls

On laptop, append one morning line to log.md, commit, and push. On desk, fetch first. Desk main has not moved yet. origin/main has. Then pull so desk fast-forwards.

# shared-log-laptop
echo "- 2026-09-21 morning: wrote from laptop" >> log.md
git add log.md
git diff --cached
git commit -m "Add morning laptop note"
git push origin main
git ls-remote origin refs/heads/main

# shared-log-desk — inspect before integrating
git fetch origin
git status
git log --oneline --decorate --graph --all
git branch -vv
git pull origin main
git rev-parse HEAD

After fetch and before pull, desk status should show that local main is behind origin/main by one commit. Pull here is fetch-plus-merge of a fast-forward. The morning line must appear in desk log.md because Git brought the commit, not because you pasted the sentence.

Milestone 5 · Desk publishes; laptop pulls

Reverse the direction. Desk appends an afternoon line, commits, and pushes. Laptop is now behind. Fetch, read the graph, then pull. Record the three commit IDs. Both clones and origin must name the afternoon tip.

# shared-log-desk
echo "- 2026-09-21 afternoon: wrote from desk" >> log.md
git add log.md
git diff --cached
git commit -m "Add afternoon desk note"
git push origin main

# shared-log-laptop
git fetch origin
git log --oneline --decorate --graph --all
git pull origin main
git rev-parse HEAD
git ls-remote origin refs/heads/main

This is the take-turns graph: initialize, morning, afternoon. Keep that evidence. The next milestone will add concurrent commits on purpose. Do not skip the three-commit proof to rush the conflict.

Milestone 6 · Recover a rejected push

Both clones now add a different extra line without pulling first. Laptop pushes successfully. Desk push is rejected as a non-fast-forward. That rejection is required evidence. Recover on desk with fetch, inspect, and merge. Keep both new lines. Then push. Laptop pulls the merge. Never force-push.

# shared-log-laptop — do not pull desk first
echo "- 2026-09-21 laptop concurrent: extra laptop line" >> log.md
git add log.md
git commit -m "Add laptop concurrent note"
git push origin main

# shared-log-desk — this push must fail
echo "- 2026-09-21 desk concurrent: extra desk line" >> log.md
git add log.md
git commit -m "Add desk concurrent note"
git push origin main

# Recover on desk. Do not run git push --force.
git fetch origin
git log --oneline --decorate --graph --all
git merge origin/main
# If log.md conflicts, keep both concurrent lines, then:
git add log.md
git diff --cached
git commit -m "Merge concurrent log notes"
git push origin main

# shared-log-laptop
git fetch origin
git pull origin main
git rev-parse HEAD
git ls-remote origin refs/heads/main

The rejection message means origin has a commit desk does not have. Fetch downloads it. Merge combines it with desk’s local commit. Both notes belong in log.md. If you take only one side, the project fails the exchange contract.

Milestone 7 · Prove the tips match

Run the audit in both folders. The SHA from git rev-parse HEAD must equal the object listed by git ls-remote origin refs/heads/main. Status is clean. git remote -v still names the same GitHub URL. The README still explains how to clone.

git rev-parse --show-toplevel
git remote -v
git status
git branch -vv
git log --oneline --decorate --graph --all
git rev-parse HEAD
git ls-remote origin refs/heads/main
git merge-base --is-ancestor HEAD origin/main

Run the same block in shared-log-laptop and shared-log-desk. If one clone is behind, fetch and pull. Do not copy files. Do not add a second GitHub repository to “fix” a mismatch.

Required failure drills

Rejected push, no force

Milestone 6 is the drill. Save the rejection text. A successful force-push is an automatic rebuild.

Fetch is not a merge

After a fetch that shows you behind, record status and the graph before you pull. Fetch alone must leave the working tree unchanged.

Wrong URL

Compare git remote -v on both clones. If one points at a different repository, stop. Fix origin; do not push “somewhere” to see what happens.

File copy is not sync

Do not copy log.md from laptop to desk. If the files match but the commit IDs differ, you did not sync Git history.

Evidence package

  • Laptop and desk paths from git rev-parse --show-toplevel, plus matching git remote -v URLs.
  • Empty-create proof: GitHub repository name, no extra initializer commit, first git ls-remote origin.
  • Second-clone command and the shared SHA immediately after git clone.
  • Take-turns graph with morning and afternoon commits, and IDs after each pull.
  • Rejected-push text, the fetch graph that explained it, and the merge that kept both concurrent lines.
  • Final matching SHAs from both clones and git ls-remote origin refs/heads/main, plus clean status.
Sanitize evidence

Redact tokens, passwords, and personal email if they appear. Repository URLs you own are expected. Do not screenshot credential helpers or SSH keys.

Self-assessment rubric: 25 points

NEEDS REVISION0–19 points

Clones disagree, a push was forced, or one direction of the exchange is missing.

SHIP STANDARD20–23 points

Two clones, one GitHub origin, both directions exchanged, rejection recovered, tips match.

DISTINCTION24–25 points

Every integration was predicted from fetch output, both concurrent lines survived, and evidence is exact.

5 · Hosted origin

Empty GitHub create, correct remote URL, first push proven with git ls-remote origin.

5 · Second clone

Desk came from git clone, not a copied folder; remotes and first SHA match.

5 · Two-way exchange

Morning reached desk through origin; afternoon reached laptop through origin; graphs recorded.

5 · Rejection recovery

Non-fast-forward captured; fetch and merge used; both concurrent lines kept; no force-push.

5 · Final audit

Matching SHAs, clean status on both clones, README clone instructions, complete evidence package.

Automatic stop

Force-push, someone else’s repository, a fork used as the second copy, pasted secrets, or unexplained lost commits requires rebuilding safely.

Project checklist

  • I pushed one empty GitHub repository I own and cloned it into a second folder.
  • I published a laptop commit and a desk commit through origin, inspecting with fetch before each pull.
  • I reproduced a rejected push and recovered with fetch and merge, not force-push.
  • Laptop HEAD, desk HEAD, and origin/main are the same SHA.
  • I never copied log.md between folders or opened a pull request.

Final audit commands

git rev-parse --show-toplevel
git remote -v
git status
git branch -vv
git log --oneline --decorate --graph --all
git rev-parse HEAD
git ls-remote origin refs/heads/main
git merge-base --is-ancestor HEAD origin/main

Those commands answer one question: do both working copies and the GitHub remote name the same commit? When they do, and the log contains every intended line, the unit project is complete. Pull requests stay for the next unit.

Related lessons

  • Git Remote — Fetch, pull, and push keep the two clones honest.
  • GitHub Repository — The hosted copy is the shared remote.
  • GitHub Authentication — Push and clone need a credential that already works.
  • GitHub Pull Requests — Two clones can share main; review uses a branch and a GitHub conversation.
PREVIOUS LESSONGitHub Pages
NEXT LESSONGitHub Pull Requests
ON THIS PAGEGit Project: Two Clone SyncProject briefSafety boundaryRepository contractRequired graphsMilestone 1 · Build the laptop copyMilestone 2 · Publish an empty GitHub remoteMilestone 3 · Clone a second working copyMilestone 4 · Laptop publishes; desk pullsMilestone 5 · Desk publishes; laptop pullsMilestone 6 · Recover a rejected pushMilestone 7 · Prove the tips matchRequired failure drillsEvidence packageSelf-assessment rubric: 25 pointsProject checklistFinal audit commandsRelated lessons
Course contents