Infra, Illustrated.
GIT · VERSION CONTROL CONSOLE

Git: Snapshots and Moving Labels

Start with one file and a line of saved versions. Then add staging, branches, merging, remotes, and undo to the same picture, one idea at a time.

PRACTICE REPO · my-appFOUNDATIONAL · DEEP DIVEPublished Sep 19, 2026 · Reviewed Oct 8, 2026
ACTIVE VIEWWhy Git
saved version points back at its parent no label points here
my-app/ · app.yaml, notes.md
01 / SAVE VERSIONS

One folder, or one folder and its history

Two ways to keep old versions of the same file.

OPTION 1 OF 2 · Copies in a folder

Every copy is a guess at what might matter later. The names can't tell you what changed, in what order, or which one was running last Tuesday.

Your folder
app.yaml
replicas: 4
app-old.yaml
replicas: 2
app-final.yaml
replicas: 3
app-final-v2.yaml
replicas: 3
app-USE-THIS.yaml
replicas: 4
OPTION 2 OF 2 · One file and its history

The folder holds only the current file. Each dot is a saved version you can open again, and the line keeps them in the order they were made.

Your folder
app.yaml
replicas: 4
Saved versions
Saved versions and the labels on themthe Monday version. the Tuesday version comes after v1. the today version comes after v2.MondayTuesdaytoday
LESSON INSPECTOR

Git keeps the history so the folder doesn't have to

Renamed copies put the history into file names, where it gets lost. Git stores each saved version outside your working files and keeps them in order, so the folder can stay small and current.

In the picture
A dot is one saved version
New word
None yet
What you will builda folder → a line of dots → labels on the dots
MODEL NOTE

The rest of this page adds one thing at a time to this picture. Git's own vocabulary arrives after you have seen what each word names.

Pro Git: What is Git? ↗

THE POINT TO KEEPGit is a history of your project, kept beside the files instead of inside their names.

02 / SAVE VERSIONS

A snapshot is a copy of everything, frozen

What does a saved version actually hold?

STEP 1 OF 3 · Start with two files

A small project folder. Nothing has been saved yet, so there is no way back from the next edit.

Your folder
app.yaml
name: shopreplicas: 2
notes.md
- add a cache
Saved versions

Nothing saved yet

STEP 2 OF 3 · Save a snapshot

A dot appears. It holds a copy of every file exactly as it was at that moment.

Your folder
app.yaml
name: shopreplicas: 2
notes.md
- add a cache
Saved versions
Saved versions and the labels on themA.A
Inside A
app.yaml
name: shopreplicas: 2
notes.md
- add a cache
STEP 3 OF 3 · Edit app.yaml

Your folder has moved on to 3. Snapshot A still says 2, and it always will.

Your folder
app.yamledited
name: shopreplicas: 3
notes.md
- add a cache
Saved versions
Saved versions and the labels on themA.A
Inside A
app.yaml
name: shopreplicas: 2
notes.md
- add a cache
LESSON INSPECTOR

Saving takes a picture of the whole project

A snapshot is not a list of edits. It records what every file contained at one moment, and nothing you do afterwards changes it. Git calls a snapshot a commit.

In the picture
The dot, and the files inside it
New word
commit: one saved snapshot
Look inside a commitgit show A:app.yaml
MODEL NOTE

“Save a snapshot” stands for two Git commands run together. View 4 separates them, once there is a reason to.

Pro Git: What is Git? ↗Pro Git: Recording Changes to the Repository ↗

THE POINT TO KEEPA commit is a frozen copy of the project. Your folder keeps changing; the commit does not.

03 / SAVE VERSIONS

Each snapshot points back at the one before

How do saved versions relate to each other?

STEP 1 OF 4 · One snapshot so far

A is saved. The folder already holds an edit that A doesn't have.

Your folder
app.yamledited
name: shopreplicas: 3
notes.md
- add a cache
Saved versions
Saved versions and the labels on themA.A
STEP 2 OF 4 · Save again

B is the new snapshot. Its arrow points back at A: every snapshot remembers the one it was made from.

Your folder
app.yaml
name: shopreplicas: 3
notes.md
- add a cache
Saved versions
Saved versions and the labels on themA. B comes after A.AB
Inside B
app.yaml
name: shopreplicas: 3
notes.md
- add a cache
STEP 3 OF 4 · Edit to 4 and save

C points back at B. The line only ever grows at its newest end.

Your folder
app.yaml
name: shopreplicas: 4
notes.md
- add a cache
Saved versions
Saved versions and the labels on themA. B comes after A. C comes after B.ABC
Inside C
app.yaml
name: shopreplicas: 4
notes.md
- add a cache
STEP 4 OF 4 · Open A again

A still holds the files as they were. Adding B and C changed nothing inside it, and looking at it changes nothing in your folder.

Your folder
app.yaml
name: shopreplicas: 4
notes.md
- add a cache
Saved versions
Saved versions and the labels on themA. B comes after A. C comes after B.ABC
Inside A
app.yaml
name: shopreplicas: 2
notes.md
- add a cache
LESSON INSPECTOR

History is a chain of snapshots

The arrow from a commit leads to its parent, the commit it was built on. Follow the arrows from the newest dot and you walk back through every earlier version.

In the picture
An arrow from newer to older
New word
parent: the commit this one came from
List the chaingit log --oneline
MODEL NOTE

Arrows point backwards because a commit knows its parent, while an older commit knows nothing about what came after it.

Pro Git: What is Git? ↗Git documentation: git-commit ↗

THE POINT TO KEEPNew commits are added at the end of the chain. Existing commits are never edited.

04 / SAVE VERSIONS

You choose what goes into the next snapshot

Two files changed, and only one is ready to save.

STEP 1 OF 3 · Two edits, one ready

app.yaml is finished. notes.md has half an idea in it. Saving everything would put the half-idea into history.

Your folderworking directory
app.yamledited
name: shopreplicas: 5
notes.mdedited
- add a cache- half an idea
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themA. B comes after A. C comes after B.ABC
STEP 2 OF 3 · git add app.yaml

Git copies app.yaml, as it is right now, into the staging area. notes.md stays where it is.

Your folderworking directory
app.yamledited
name: shopreplicas: 5
notes.mdedited
- add a cache- half an idea
Ready to savestaging area
app.yamlstaged
name: shopreplicas: 5
Saved versionshistory
Saved versions and the labels on themA. B comes after A. C comes after B.ABC
STEP 3 OF 3 · git commit

D is built from the staging area. It has the new app.yaml and the old notes.md; the half-idea is still only in your folder.

Your folderworking directory
app.yaml
name: shopreplicas: 5
notes.mdedited
- add a cache- half an idea
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themA. B comes after A. C comes after B. D comes after C.ABCD
Inside D
app.yaml
name: shopreplicas: 5
notes.md
- add a cache
LESSON INSPECTOR

A change lives in one of three places

Your folder holds what you are editing. The staging area holds what the next commit will contain. History holds what is already saved. git add moves a file's current contents from the first to the second; git commit turns the second into a new dot.

New words
working directory, staging area
Commands
git add, then git commit
See all three at oncegit status
MODEL NOTE

git add copies the file as it is at that moment. If you edit it again afterwards, the staging area still holds the earlier contents until you add it again.

Pro Git: Recording Changes to the Repository ↗Git documentation: git-add ↗Git documentation: git-commit ↗

THE POINT TO KEEPA commit is built from the staging area, not straight from your folder.

05 / BRANCHES

A branch is a label stuck on one snapshot

How does Git know which snapshot is the latest?

STEP 1 OF 3 · Which one is newest?

You can see that D is the newest dot. Git needs a name it can look up.

Saved versionshistory
Saved versions and the labels on themC. D comes after C.…CD
STEP 2 OF 3 · Show the label

This label has been here since your first commit. It is called main, and it sits on the newest snapshot.

Saved versionshistory
Saved versions and the labels on themC. D comes after C. main points at D.…CDmain
STEP 3 OF 3 · git commit

E appears and main slides onto it. That is all a commit does to a label.

Saved versionshistory
Saved versions and the labels on themC. D comes after C. E comes after D. main points at E.…CDEmain
LESSON INSPECTOR

The label moves; the snapshots stay put

Git calls this label a branch. It holds nothing except the name of one commit. When you commit, Git adds the new dot and moves the label onto it.

In the picture
A tag sitting on a dot
New word
branch: a movable name for one commit
See where each label pointsgit branch -v
MODEL NOTE

main is only the usual name for the first branch. Git treats it like any other label.

Pro Git: Branches in a Nutshell ↗Git documentation: git-branch ↗

THE POINT TO KEEPA branch is a name that points at one commit and moves forward when you commit.

06 / BRANCHES

With two labels, Git tracks which one you are on

You want to try an idea without disturbing main.

STEP 1 OF 5 · One label

main sits on the newest snapshot, E.

Saved versionshistory
Saved versions and the labels on themD. E comes after D. main points at E.…DEmain
STEP 2 OF 5 · git branch idea

A second label lands on the same snapshot. No files were copied and no dot was added.

Saved versionshistory
Saved versions and the labels on themD. E comes after D. main points at E. idea points at E.…DEmainidea
STEP 3 OF 5 · Show where you are

Two labels on one dot: which will the next commit move? Git marks the one you are on. It calls that marker HEAD.

Saved versionshistory
Saved versions and the labels on themD. E comes after D. main points at E. idea points at E. HEAD is on main.…DEmainideaHEAD
STEP 4 OF 5 · git switch idea

HEAD moves to idea. Both labels still point at E, so no file needs to change.

Saved versionshistory
Saved versions and the labels on themD. E comes after D. main points at E. idea points at E. HEAD is on idea.…DEmainideaHEAD
STEP 5 OF 5 · git commit

F appears and only the label under HEAD moves. main still points at E.

Saved versionshistory
Saved versions and the labels on themD. E comes after D. F comes after E. main points at E. idea points at F. HEAD is on idea.…DEFmainideaHEAD
LESSON INSPECTOR

HEAD says which label the next commit moves

Making a branch adds a label and nothing else, which is why it is instant. HEAD is Git's “you are here” marker. A commit moves the branch HEAD is on and leaves every other branch where it was.

New word
HEAD: the branch you are on
Commands
git branch idea, git switch idea
Create and switch in one stepgit switch -c idea
MODEL NOTE

HEAD can also sit directly on a commit with no branch between them, which Git calls a detached HEAD. Commits made there have no label holding them until you create one.

Pro Git: Branches in a Nutshell ↗Git documentation: git-branch ↗Git documentation: git-switch ↗

THE POINT TO KEEPCreating a branch copies nothing. Committing moves only the branch that HEAD is on.

07 / BRANCHES

Switching rewrites your folder from a snapshot

What happens to your files when HEAD moves?

STEP 1 OF 4 · On idea

HEAD is on idea, so your folder shows snapshot F. F added a cache line that E does not have.

Your folderworking directory
app.yaml
name: shopreplicas: 5cache: on
Saved versionshistory
Saved versions and the labels on themE. F comes after E. main points at E. idea points at F. HEAD is on idea.…EFmainideaHEAD
STEP 2 OF 4 · git switch main

HEAD moves to main, and Git rewrites app.yaml to match snapshot E. The cache line is gone from the folder, not from F.

Your folderworking directory
app.yamlrewritten
name: shopreplicas: 5
Saved versionshistory
Saved versions and the labels on themE. F comes after E. main points at E. idea points at F. HEAD is on main.…EFmainideaHEAD
STEP 3 OF 4 · git switch idea

Back on idea, the folder matches F again and the cache line returns.

Your folderworking directory
app.yamlrewritten
name: shopreplicas: 5cache: on
Saved versionshistory
Saved versions and the labels on themE. F comes after E. main points at E. idea points at F. HEAD is on idea.…EFmainideaHEAD
STEP 4 OF 4 · Edit app.yaml, then git switch main

Switching would have to overwrite an edit you haven't saved, so Git refuses and moves nothing. Commit the edit or undo it, then switch.

Your folderworking directory
app.yamledited
name: shopreplicas: 7cache: on
Saved versionshistory
Saved versions and the labels on themE. F comes after E. main points at E. idea points at F. HEAD is on idea.…EFmainideaHEAD

Refusederror: Your local changes to the following files would be overwritten by checkout: app.yaml

LESSON INSPECTOR

Your folder shows one snapshot at a time

Switching moves HEAD to another branch and then makes your files match the commit that branch points at. Both versions stay in history; only the folder changes.

In the picture
HEAD hops; the file is rewritten
Command
git switch main
Check before you switchgit status
MODEL NOTE

Git refuses only when an unsaved edit touches a file that differs between the two commits. An edit to a file that is the same on both sides travels with you.

Git documentation: git-switch ↗

THE POINT TO KEEPSwitching branches changes which snapshot your folder shows, and Git won't overwrite unsaved work to do it.

08 / COMBINE

Merging joins two lines of work

idea worked. Now bring it into main.

STEP 1 OF 4 · idea is ahead

idea has one snapshot that main lacks. You are on main.

Saved versionshistory
Saved versions and the labels on themE. F comes after E. idea points at F. main points at E. HEAD is on main.…EFideamainHEAD
STEP 2 OF 4 · git merge idea

There was nothing to combine, so main slides forward to F. Git calls this a fast-forward. No new dot was needed.

Saved versionshistory
Saved versions and the labels on themE. F comes after E. idea points at F. main points at F. HEAD is on main.…EFideamainHEAD

DoneFast-forward

STEP 3 OF 4 · Commit on both branches

G raised replicas on main. H added a timeout on idea. Each label now has a snapshot the other lacks: history has forked at F.

Saved versionshistory
Saved versions and the labels on themE. F comes after E. G comes after F. H comes after F. main points at G. idea points at H. HEAD is on main.…EFGHmainideaHEAD
STEP 4 OF 4 · git merge idea

M is a snapshot with two parents. It contains both changes, and idea has not moved.

Saved versionshistory
Saved versions and the labels on themE. F comes after E. G comes after F. H comes after F. M comes after G and H. main points at M. idea points at H. HEAD is on main.…EFGHMmainideaHEAD
Inside M
app.yaml
name: shopreplicas: 6cache: ontimeout: 30
LESSON INSPECTOR

A merge either slides a label or adds a dot with two parents

If the other branch is simply ahead, Git moves your label forward. If both branches have new commits, Git combines the two sets of changes into one new commit that points back at both.

New words
fast-forward, merge commit
Command
git merge idea
See the fork and the joingit log --oneline --graph
MODEL NOTE

You merge into the branch HEAD is on. The branch you name is read, never moved.

Git documentation: git-merge ↗

THE POINT TO KEEPA merge commit is an ordinary snapshot that happens to have two parents.

09 / COMBINE

A conflict is Git asking you to choose

Both branches changed the same line.

STEP 1 OF 4 · Same line, two answers

A new branch, tune, starts at M. Then main sets replicas to 8 in I, and tune sets the same line to 10 in J.

Your folderworking directory
app.yaml
name: shopreplicas: 8cache: ontimeout: 30
Saved versionshistory
Saved versions and the labels on themM. I comes after M. J comes after M. main points at I. tune points at J. HEAD is on main.…MIJmaintuneHEAD
Inside J
app.yaml
name: shopreplicas: 10cache: ontimeout: 30
STEP 2 OF 4 · git merge tune

Git cannot pick a number for you. It stops, adds no dot, and writes both versions into the file between markers.

Your folderworking directory
app.yamlconflict
name: shop<<<<<<< HEADreplicas: 8=======replicas: 10>>>>>>> tunecache: ontimeout: 30
Saved versionshistory
Saved versions and the labels on themM. I comes after M. J comes after M. main points at I. tune points at J. HEAD is on main.…MIJmaintuneHEAD

PausedCONFLICT (content): Merge conflict in app.yaml

STEP 3 OF 4 · Edit the file: keep 10

You decide. Delete the markers and leave the line you want.

Your folderworking directory
app.yamledited
name: shopreplicas: 10cache: ontimeout: 30
Saved versionshistory
Saved versions and the labels on themM. I comes after M. J comes after M. main points at I. tune points at J. HEAD is on main.…MIJmaintuneHEAD

PausedThe merge is still open until you stage the file and commit

STEP 4 OF 4 · git add app.yaml, then git commit

Staging the file tells Git the question is settled. The commit is an ordinary merge snapshot with two parents.

Your folderworking directory
app.yaml
name: shopreplicas: 10cache: ontimeout: 30
Saved versionshistory
Saved versions and the labels on themM. I comes after M. J comes after M. N comes after I and J. main points at N. tune points at J. HEAD is on main.…MIJNmaintuneHEAD
Inside N
app.yaml
name: shopreplicas: 10cache: ontimeout: 30
LESSON INSPECTOR

Git merges what it can and stops where it can't

Changes to different lines combine on their own, as they did in the last view. When both sides change the same lines, Git has no rule for choosing, so it hands the file to you and waits.

In the file
Yours above =======, theirs below
To finish
edit, git add, git commit
Back out and try latergit merge --abort
MODEL NOTE

Until you commit or abort, the repository is in the middle of a merge. Nothing on either branch has moved, so backing out loses only your conflict edits.

Git documentation: git-merge ↗

THE POINT TO KEEPA conflict pauses the merge. You settle the file, stage it, and commit to finish.

10 / COMBINE

Rebase copies your work onto a new starting point

You would rather have one straight line than a fork and a join.

STEP 1 OF 3 · A fork again

A new branch, fix, started at N. Since then main gained O and fix gained P. You are on fix.

Saved versionshistory
Saved versions and the labels on themN. O comes after N. P comes after N. main points at O. fix points at P. HEAD is on fix.…NOPmainfixHEAD
STEP 2 OF 3 · git rebase main

Git replays P's change on top of O as a new snapshot, P′, and moves fix onto it. The old P is still there, but no label points at it.

Saved versionshistory
Saved versions and the labels on themN. O comes after N. P (no label points here) comes after N. P′ comes after O. main points at O. fix points at P′. HEAD is on fix.…NOPP′mainfixHEAD
STEP 3 OF 3 · Compare with a merge

The files end up the same either way. Rebase gives a straight line made of new snapshots; merge keeps both lines and joins them.

After git rebase main

Saved versionshistory
Saved versions and the labels on themN. O comes after N. P (no label points here) comes after N. P′ comes after O. main points at O. fix points at P′. HEAD is on fix.…NOPP′mainfixHEAD

If you had run git merge main

Saved versionshistory
Saved versions and the labels on themN. O comes after N. P comes after N. X comes after P and O. main points at O. fix points at X. HEAD is on fix.…NOPXmainfixHEAD
LESSON INSPECTOR

Rebase makes new commits; it does not move old ones

P was built on N. A commit cannot change its parent, so Git creates P′ with the same change and O as its parent, then moves your branch to it. The original is left behind without a label.

In the picture
Dashed dot: the old copy
Command
git rebase main
Stop a rebase part-waygit rebase --abort
MODEL NOTE

Rebase only work that nobody else has yet. If the old commits were already shared, other people are still building on them, and view 12 shows the disagreement that causes.

Git documentation: git-rebase ↗Git documentation: git-merge ↗

THE POINT TO KEEPMerge preserves how the work happened. Rebase rewrites it as if it had started later.

11 / SHARE

A remote is another copy of the same timeline

A shared server holds the project too.

STEP 1 OF 6 · Two copies

The shared copy is called origin. It has its own dots and its own main. Right now both copies end at Q.

Your computer

Saved versionshistory
Saved versions and the labels on themQ. main points at Q. HEAD is on main.…QmainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themQ. main points at Q.…Qmain
STEP 2 OF 6 · git commit

Your main moves to R. The dashed label origin/main has been on Q since you copied the project: it is your note of where the shared main was when you last checked.

Your computer

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. origin/main points at Q. main points at R. HEAD is on main.…QRorigin/mainmainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themQ. main points at Q.…Qmain
STEP 3 OF 6 · git push

Push sends R to origin and moves the shared main onto it. Your note, origin/main, is updated to match.

Your computer

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. main points at R. origin/main points at R. HEAD is on main.…QRmainorigin/mainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. main points at R.…QRmain
STEP 4 OF 6 · A teammate pushes S

origin has moved on. Your copy has not heard: origin/main still says R.

Your computer

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. main points at R. origin/main points at R. HEAD is on main.…QRmainorigin/mainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. S comes after R. main points at S.…QRSmain
STEP 5 OF 6 · git fetch

Fetch downloads S and moves only origin/main. Your main and your files are untouched.

Your computer

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. S comes after R. main points at R. origin/main points at S. HEAD is on main.…QRSmainorigin/mainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. S comes after R. main points at S.…QRSmain
STEP 6 OF 6 · git merge origin/main

main fast-forwards to S, the same slide you saw in view 8. git pull runs fetch and this step together.

Your computer

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. S comes after R. origin/main points at S. main points at S. HEAD is on main.…QRSorigin/mainmainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themQ. R comes after Q. S comes after R. main points at S.…QRSmain
LESSON INSPECTOR

Push and fetch carry dots and labels between copies

Each copy is a complete repository. Nothing travels between them until you ask. Push uploads your commits and moves the shared branch; fetch downloads theirs and moves your origin/ labels.

New words
remote, origin, origin/main
Commands
git push, git fetch, git pull
Compare your branch with the notegit status -sb
MODEL NOTE

origin/main is stored on your computer and changes only when you fetch, pull, or push. It can be out of date without anything looking wrong.

Git documentation: git-fetch ↗Git documentation: git-push ↗Git documentation: git-pull ↗

THE POINT TO KEEPorigin/main is what you last saw, not what the server holds now. Fetch refreshes it.

12 / SHARE

When both copies moved, combine first and then push

You committed. So did a teammate.

STEP 1 OF 5 · Both copies moved

You committed T. Meanwhile a teammate pushed U. Each copy has a snapshot the other lacks.

Your computer

Saved versionshistory
Saved versions and the labels on themS. T comes after S. origin/main points at S. main points at T. HEAD is on main.…STorigin/mainmainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themS. U comes after S. main points at U.…SUmain
STEP 2 OF 5 · git push

origin refuses. Moving its main to T would drop U from the shared history. Nothing changed on either side.

Your computer

Saved versionshistory
Saved versions and the labels on themS. T comes after S. origin/main points at S. main points at T. HEAD is on main.…STorigin/mainmainHEAD

Refused! [rejected] main -> main (fetch first)

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themS. U comes after S. main points at U.…SUmain
STEP 3 OF 5 · git fetch

Now your copy has U and knows where the shared main is. Your own main is still on T.

Your computer

Saved versionshistory
Saved versions and the labels on themS. T comes after S. U comes after S. main points at T. origin/main points at U. HEAD is on main.…STUmainorigin/mainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themS. U comes after S. main points at U.…SUmain
STEP 4 OF 5 · git merge origin/main

The same join as view 8: V has your T and their U as parents.

Your computer

Saved versionshistory
Saved versions and the labels on themS. T comes after S. U comes after S. V comes after T and U. main points at V. origin/main points at U. HEAD is on main.…STUVmainorigin/mainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themS. U comes after S. main points at U.…SUmain
STEP 5 OF 5 · git push

origin already had U, so it receives T and V. The shared main moves forward and nobody's work is dropped.

Your computer

Saved versionshistory
Saved versions and the labels on themS. T comes after S. U comes after S. V comes after T and U. main points at V. origin/main points at V. HEAD is on main.…STUVmainorigin/mainHEAD

Shared copy · origin

Saved versionshistory
Saved versions and the labels on themS. U comes after S. T comes after S. V comes after T and U. main points at V.…SUTVmain
LESSON INSPECTOR

A push may only move the shared branch forward

origin accepts a push when the commit you send has the current shared commit somewhere behind it. If it doesn't, you are missing someone's work, so you fetch it, combine it with yours, and push the result.

Why it bounced
T does not have U behind it
The loop
fetch, merge or rebase, push
Fetch and replay yours on topgit pull --rebase
MODEL NOTE

git push --force makes origin take your branch anyway and discards what was there. This is how a rebase of shared commits loses a teammate's work.

Git documentation: git-push ↗Git documentation: git-fetch ↗Git documentation: git-pull ↗

THE POINT TO KEEPA rejected push means the server has commits you don't. Bring them in, then push again.

13 / UNDO AND INSIDE

To undo, pick which place to rewind

Four different things people mean by “undo”.

OPTION 1 OF 4 · git restore app.yaml

Rewinds your folder. Git copies the file back from the staging area, and the edit is gone for good because it was never saved anywhere else.

Before

Your folderworking directory
app.yamledited
name: shopreplicas: 14cache: ontimeout: 30
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themV. W comes after V. main points at W. HEAD is on main.…VWmainHEAD

After

Your folderworking directory
app.yamlrestored
name: shopreplicas: 12cache: ontimeout: 30
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themV. W comes after V. main points at W. HEAD is on main.…VWmainHEAD

LostThe edit to 14 cannot be recovered

OPTION 2 OF 4 · git restore --staged notes.md

Rewinds the staging area. The file leaves the next commit, and your edit is still in the folder.

Before

Your folderworking directory
notes.mdedited
- add a cache- ship it
Ready to savestaging area
notes.mdstaged
- add a cache- ship it
Saved versionshistory
Saved versions and the labels on themV. W comes after V. main points at W. HEAD is on main.…VWmainHEAD

After

Your folderworking directory
notes.mdedited
- add a cache- ship it
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themV. W comes after V. main points at W. HEAD is on main.…VWmainHEAD
OPTION 3 OF 4 · git reset HEAD~1

Rewinds the label. main moves back one snapshot to V. W's change stays in your folder as an unsaved edit, and W itself is left without a label.

Before

Your folderworking directory
app.yaml
name: shopreplicas: 12cache: ontimeout: 30
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themV. W comes after V. main points at W. HEAD is on main.…VWmainHEAD

After

Your folderworking directory
app.yamledited
name: shopreplicas: 12cache: ontimeout: 30
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themV. W (no label points here) comes after V. main points at V. HEAD is on main.…VWmainHEAD
OPTION 4 OF 4 · git revert HEAD

Rewinds nothing. Git adds a new snapshot that reverses W's change, so the history others have already seen stays intact.

Before

Your folderworking directory
app.yaml
name: shopreplicas: 12cache: ontimeout: 30
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themV. W comes after V. main points at W. HEAD is on main.…VWmainHEAD

After

Your folderworking directory
app.yamlrewritten
name: shopreplicas: 10cache: ontimeout: 30
Ready to savestaging area

Nothing waiting

Saved versionshistory
Saved versions and the labels on themV. W comes after V. Y comes after W. main points at Y. HEAD is on main.…VWYundoes WmainHEAD
LESSON INSPECTOR

Undo depends on how far the change has travelled

An edit that is only in your folder can be thrown away. A staged file can be taken back out. A commit only you have can be dropped by moving the label. A commit others already have should be cancelled with a new commit.

HEAD~1
The commit one step before HEAD
Safe after pushing
git revert
Reset, and choose how far it reachesgit reset --soft | --mixed | --hard
MODEL NOTE

git reset --hard also rewrites your folder to match, destroying unsaved edits. Plain git reset (the default, --mixed) keeps them; --soft keeps the staging area as well.

Git documentation: git-restore ↗Git documentation: git-reset ↗Git documentation: git-revert ↗

THE POINT TO KEEPRestore rewinds a file, reset rewinds a label, and revert adds a commit that cancels another.

14 / UNDO AND INSIDE

What a dot is made of

Open a commit and see why the earlier views behaved as they did.

STEP 1 OF 4 · Open a commit

Four things are inside: a snapshot of the folder, the parent, who and when, and a message. The letters were nicknames. The real name is a fingerprint computed from these contents.

commit 4f9c2e1the dot you called W
snapshot
tree 8a31d07
parent
1c7be52
author
you · Tue 14:02
message
Raise replicas to 12
STEP 2 OF 4 · Open the snapshot

The snapshot is a list of file names, each pointing at stored contents. notes.md has not changed since A, so every commit since then points at the same stored copy.

commit 4f9c2e1
snapshot
tree 8a31d07
parent
1c7be52
tree 8a31d07the folder listing
app.yaml
blob e52b9d4
notes.md
blob 0d6f1aa
blob 0d6f1aacontents of notes.md, stored once
- add a cache
STEP 3 OF 4 · Change only the parent

Same files, same message, different parent, so a different fingerprint. This is why rebase had to create P′ instead of moving P.

commit 4f9c2e1original
snapshot
tree 8a31d07
parent
1c7be52
commit b83a0f6the rebased copy
snapshot
tree 8a31d07
parent
97de310
STEP 4 OF 4 · Find a dot with no label

Git keeps a diary of everywhere HEAD has been. A commit that lost its label is still listed there, and a new label brings it back.

git reflognewest first
1c7be52
HEAD@{0}: reset: moving to HEAD~1
4f9c2e1
HEAD@{1}: commit: Raise replicas to 12
1c7be52
HEAD@{2}: commit (merge): Merge origin/main
git branch rescue 4f9c2e1a label on the lost commit
rescue
→ 4f9c2e1
main
→ 1c7be52
LESSON INSPECTOR

A commit's name is computed from what it contains

Because the fingerprint covers the snapshot and the parent, a commit can never be edited in place: any change produces a different commit. That is what makes old dots trustworthy, and why rewriting history always means making new dots.

New words
hash, tree, blob, reflog
A label is
a small file holding one hash
Print a commit's contentsgit cat-file -p HEAD
MODEL NOTE

The hashes here are illustrative. The reflog is local and its entries expire: by default Git keeps entries for commits with no label for at least 30 days. Edits you never committed are not in it.

Pro Git: Git Objects ↗Git documentation: git-reflog ↗

THE POINT TO KEEPCommits are immutable and named by content, so Git only ever adds a snapshot or moves a label.