All tutorials

Tutorial 5 of 8

Remotes: Clone, Fetch, Rebase, Push

Copy a project, catch up with your team's new commits, and share your own without making a mess.

About 6 minutes

What you will be able to do

  1. Clone a repository, and explain origin and remote-tracking branches such as origin/main.
  2. Use git fetch to download new commits without changing your own branch or files.
  3. Rebase your local commits onto the updated origin/main, then push them.
  4. Explain why you should never rebase commits you have already shared.

Title

Remotes. Clone, fetch, rebase, push.

This tutorial shows how your repository talks to a shared copy on a server.

What a remote is

A remote is another copy of the repository, usually on a server. When you clone, Git names it origin.

A remote is simply a named link to another copy of your repository, and origin is the usual name for the one you cloned from.

Analogy: a photocopied library

git clone copies the whole project, with all its history, and remembers where it came from.

git clone https://github.com/OWNER/notes-app.git
cd notes-app
git remote -v

A clone is a full, independent copy of the repository that records the source as a remote named origin.

The analogy, and where it breaks

Cloning is like photocopying an entire library, including its register of every past edition.

A photocopy never updates, and a clone does not update itself either, but a clone remembers its source as origin so you can fetch new commits any time.

origin/main is a bookmark

origin/main is your clone's memory of main on the server. It moves only when you fetch or push.

Remote-tracking branches like origin/main record what the server looked like the last time you talked to it.

Meanwhile, on the server

A teammate pushes C3 to the server. You commit L1 on your laptop. Neither of you sees the other's work yet.

Your laptop only knows what the server had at the last fetch, so C3 is still invisible to you.

Analogy: collecting mail without opening it

git fetch downloads new commits and moves origin/main. Your main and your files stay as they are.

git fetch origin
git status

Fetch is always safe, because it only downloads commits and updates remote-tracking branches like origin/main.

The analogy, and where it breaks

Fetching is like collecting your letters from the post box and putting them on the table without opening them.

Unopened letters sit in a separate pile, but fetched commits are already stored in your repository; they just are not part of your branch until you merge or rebase.

Analogy: re-stacking plates

git rebase origin/main lifts your commits off and re-stacks them on top of the new base.

Rebase replays your commits on top of the updated branch, creating new commits so your history becomes a straight line.

The analogy, and where it breaks

Rebasing is like lifting your plates off one stack and placing them, in the same order, on a new base plate.

Moved plates are the same plates, but rebased commits are brand-new commits with new IDs, and the old ones are left behind.

Fetch, then rebase

Fetch first, then rebase onto origin/main. Conflict? Fix the file, git add it, then continue.

git fetch origin
git rebase origin/main
# if a conflict stops the rebase:
#   fix the file, then
#   git add <file>
#   git rebase --continue      (or git rebase --abort to cancel)
git log --oneline --graph

After fetching, rebasing onto origin/main puts your commit after your teammate's, and --abort takes you back if things go wrong.

Push

git push sends your new commits to the server. If the server has commits you lack, push is rejected. Fetch first.

git push origin main
# first push of a new branch:
git push -u origin my-branch

Push only succeeds when it adds to the server's history, which is why you fetch and rebase before pushing.

The golden rule of rebase

Never rebase commits you have already pushed and shared. Teammates have built on the old IDs.

Rebase is safe for local commits that nobody else has, but rewriting shared history causes duplicates and lost work for your team.

The analogy, and where it breaks

Re-stacking your own plates, before anyone else uses them, is harmless.

Once others hold copies of those commits, rewriting them leaves everyone with two versions of the same work, so rebase only your own unshared commits.

Recap

clone copies and remembers origin. fetch updates origin/main only. rebase re-stacks with new IDs. push shares your commits.

{"commits": [{"id": "C1", "parents": []}, {"id": "C2", "parents": ["C1"]}, {"id": "L1", "parents": ["C2"]}, {"id": "C3", "parents": ["C2"]}], "refs": [{"name": "main", "target": "L1", "kind": "branch"}, {"name": "origin/main", "target": "C3", "kind": "remote"}], "head": {"ref": "main"}}

You can now work with a shared repository, and the last tutorial uses these steps to contribute to open source.

Check yourself

0 of 5 answered

Check 1 of 5

What does git fetch change in your repository?

Check 2 of 5

You run git rebase origin/main. Click the commit that becomes the parent of your new copy of L1.

C1C2L1HEAD: mainC3origin/main

C1 then C2. Your main has L1 on top of C2. origin/main is at C3, also on top of C2. HEAD is on main.

Check 3 of 5

Your push was rejected because the server has new commits. Put the needed commands in order. One card is not needed.

Tap the commands in the order you would run them. One is not needed.

Check 4 of 5

Why should you avoid rebasing commits that are already pushed to a shared branch?

Check 5 of 5

What does git clone set up for you?

mkdir -p ~/git-practice/t05-remotes && cd ~/git-practice/t05-remotes
git init --bare -b main server.git          # pretend this is the server

# Teammate Asha starts the project and pushes it
git init -b main asha
cd asha
git config user.name "Asha (practice)"      # only for this practice repo
echo "Welcome" > readme.txt && git add readme.txt && git commit -m "Add readme"
git remote add origin ../server.git
git push -u origin main
cd ..

# You clone the project
git clone server.git you

# Asha pushes another commit
cd asha
echo "Be kind" > rules.txt && git add rules.txt && git commit -m "Add rules"
git push
cd ../you

# You commit without knowing about Asha's new commit
echo "My idea" > idea.txt && git add idea.txt && git commit -m "Add idea"
git push                                    # rejected: fetch first

git fetch
git status                                  # your branch and origin/main have diverged
git log --oneline --graph --all
git rebase origin/main
git log --oneline --graph --all             # a straight line now; note the new ID of "Add idea"
git push