Library
00/07 · ~32 min
GUIDEDECK · how teams track, share, and recover every change

Git &
Version Control
from the model up.

A 32-minute working session on what Git actually does under the hood — the snapshot model and commit graph, the three areas, branching and merging, rebase trade-offs, collaboration with remotes and pull requests, the workflow that fits your team, and how to undo almost anything safely.

~32 MINBEGINNER → INTERMEDIATETOOL-AGNOSTIC
SCROLL
01 · How Git models history 4 min

A commit is a snapshot,
and history is a graph of them.

Most confusion with Git comes from picturing it wrong. Git does not store a list of file edits or diffs. It stores a series of full snapshots of your project, each one pointing back to the snapshot it came from. Get this one picture right and almost every command stops feeling like magic.

Version controla system that records the history of a project so you can see what changed, when, by whom, and roll back or branch off any point. Git is a distributed version control system: every clone is a full copy of the entire history, so you can commit, branch, and inspect the log with no server and no network.

Snapshots, not diffs

  • Each commit captures the whole tree of files at that moment, plus a pointer to its parent commit.
  • Every commit is named by a hash of its content (a 40-char SHA). Same content, same hash — which is how Git de-duplicates unchanged files instead of copying them.
  • Follow the parent pointers backward and you have read the entire history. That chain of pointers is the repository.
C1 a1b2c3 C2 d4e5f6 C3 7890ab parent parent SNAPSHOT whole tree HEAD

Each commit names its parent and a full snapshot. Newest first, the arrows point backward in time.

# what a commit object actually contains tree 9c1f… # the full snapshot (files + folders) parent d4e5… # the commit this one builds on author Dani <dani@team.dev> 1719600000 committer Dani <dani@team.dev> 1719600000 Add checkout total to cart # the message
commit 7890ab tree cart.ts total.ts index.ts blobs · file content

commit → tree → blobs. Unchanged files reuse the same blob, so a snapshot is cheap.

HEAD

Where you are now

A pointer to the commit (usually via a branch) that your next commit will build on. "Detached HEAD" just means it points straight at a commit, not a branch.

ref / branch

A name for a commit

A branch like main is just a movable label pointing at one commit. Making a commit moves the label forward — that is the whole mechanism.

DAG

The shape of history

History is a directed acyclic graph — commits with parent edges, no cycles. Merges give a commit two parents; that is the only way the graph branches and rejoins.

02 · The three areas 4 min

Working tree → staging → repository.

A commit doesn't snapshot your files directly. There is a deliberate middle step — the staging area — that lets you choose exactly what goes into the next commit. Understanding these three places is the difference between fighting Git and steering it.

Staging area (also called the index) — a draft of your next commit that you build up piece by piece. git add copies a file's current content into the stage; git commit seals whatever is staged into a new snapshot. Files you edited but didn't stage are simply left out.
WORKING TREE edited files on disk STAGING / INDEX next commit draft REPOSITORY sealed snapshots .git add commit checkout / restore

add stages a draft; commit seals it; restore pulls a clean copy back the other way.

Why the middle step earns its keep

  • You changed three files but only two belong to this fix — stage those two and commit a clean, reviewable unit.
  • git add -p stages parts of a file (hunks), so a single messy editing session becomes several focused commits.
  • git status always shows the split: staged, modified-but-unstaged, and untracked. Read it before every commit.
# see the three buckets at a glance git status -s # M cart.ts (modified, unstaged) # ?? notes.txt (untracked) # stage only what belongs together git add cart.ts total.ts git commit -m "Fix rounding in cart total"
STAGED total.ts · cart.ts MODIFIED readme.md UNTRACKED notes.txt

Only the staged bucket enters the commit. The rest stays in your working tree, untouched.

03 · Branches & merging 5 min

A branch is a movable label — merging joins two of them.

Because a branch is just a pointer to a commit, creating one is instant and costs nothing. You work on a branch, then merge it back to bring its commits into another line of history. There are two ways that join can happen.

Brancha lightweight, movable pointer to a commit. git switch -c feature creates a new label at your current commit and moves HEAD onto it. New commits push that label forward while main stays put.
Fast-forward — no merge commit
main main → main had no new commits just slide the label forward

If main hasn't moved, Git just slides its label up to the branch tip. History stays linear.

Three-way — a real merge commit
M both lines advanced 2 parents

When both branches have new commits, Git makes a merge commit M with two parents to tie them together.

git switch -c add-search # new branch at HEAD # …commit some work… git switch main git merge add-search # fast-forward or 3-way # a conflict pauses the merge: # <<<<<<< HEAD … ======= … >>>>>>> add-search git add resolved.ts && git commit # finish it
<<<<<<< HEAD price = base * 1.2 ======= price = base * tax >>>>>>> add-search

A conflict isn't an error — it's Git asking you to pick between two edits to the same lines. Edit, stage, commit.

Conflicts only happen when two branches change the same lines of the same file. Small, frequent merges keep them rare and tiny — which is the whole argument behind the workflows in section 06.

04 · Merge vs rebase 5 min

Same goal, two histories:
preserve it or replay it.

Both bring one branch's work onto another. Merge records what really happened — two lines that met. Rebase rewrites your commits as if you had started from the latest tip, producing a clean straight line. Neither is "correct"; they optimize for different things.

Rebasetake your commits, set them aside, fast-forward to the new base, then re-apply your commits one by one on top. The result looks linear, but each replayed commit is a brand-new commit with a new hash — the originals are rewritten, not moved.
Merge — honest, non-destructive
M true history · merge commit

Keeps every commit and records the join. History shows the branch really existed — at the cost of a busier graph.

Rebase — linear, rewritten
A' B' replayed on the new base new hashes

A' and B' are copies of your commits with new hashes. The graph is clean; the originals are gone.

Don't — rebase shared history
# main is already pushed and others have it git switch main git rebase feature # rewrites public commits! git push --force # everyone else's history # now diverges — painful for the whole team
Do — rebase your own local work
# tidy your unshared feature before review git switch feature git rebase main # replay onto latest main # resolve any conflicts once, linearly git push --force-with-lease # safe on your own branch
The Golden Rule of rebasingnever rebase commits that other people already have. Rewriting shared history forces everyone else into a painful reconciliation. Rebase freely on your own un-pushed branch; merge when history is shared.

Pick by what you value

  • Want a faithful record and zero risk? Merge. It's the safe default and never rewrites anything.
  • Want a clean, linear log that's easy to read and bisect? Rebase your feature branch onto main before merging.
  • Many teams combine both: rebase locally to tidy up, then merge via a pull request (often a squash merge) into main.

A squash merge collapses every commit on a branch into a single new commit on the target. You get one tidy entry per feature in main, and the branch's noisy work-in-progress commits never land in shared history. Popular on GitHub and GitLab for exactly that reason — just know you lose the per-commit granularity of the branch.

05 · Remotes, pull requests & collaboration 5 min

A remote is just
another copy of the repo.

Git is distributed, so a server isn't special — it's one more clone that everyone happens to agree on. You fetch to download its commits, push to upload yours, and use a pull request as the place where humans review before code joins the shared line.

Remotea named bookmark for another copy of the repository, conventionally called origin. Your local origin/main is a read-only snapshot of where the server's main was the last time you talked to it — not a live view.
YOUR CLONE full history + working tree REMOTE · origin shared history on a server fetch / pull push

fetch downloads without touching your work; pull = fetch + merge; push uploads your commits.

Fetch, then decide

  • fetch updates your origin/* pointers but never changes your branches or files — always safe to run.
  • pull is fetch then merge (or rebase with --rebase). It does change your branch, so it can conflict.
  • A tracking branch links your local main to origin/main so Git can say "ahead 2, behind 1."
git fetch origin git switch -c fix-typo # …work, add, commit… git push -u origin fix-typo # open a PR from this
Pull request (GitLab calls it a merge request) — a proposal to merge one branch into another, wrapped in review, discussion, and automated checks. It's a workflow feature of the hosting platform, not a Git command — the gate where code review and CI/CD pipelines run before anything reaches main.

Hosting landscape

The Git engine is identical everywhere; you're choosing the collaboration platform around it. One line each — pro, con, and when it wins.

GitHub — the default network effect

Pro
Largest community and integration ecosystem; Actions CI and pull-request review are excellent and familiar to almost every developer.
Con
Advanced governance and self-hosting sit behind Enterprise tiers; the platform is proprietary.

Choose when  you want maximum reach, open source visibility, or the path of least resistance for a new team.

GitLab — one integrated DevOps platform

Pro
Source, CI/CD, issues, registry, and security scanning in a single product; strong self-managed option you fully control.
Con
Breadth means more surface to learn and operate; self-hosting carries real maintenance cost.

Choose when  you want one vendor end-to-end, or you need to run the whole stack inside your own infrastructure.

Bitbucket — at home in the Atlassian stack

Pro
Deep, native Jira integration and Atlassian SSO; comfortable if your org already lives in Jira and Confluence.
Con
Smaller community and third-party ecosystem than GitHub or GitLab; less momentum for new tooling.

Choose when  your team is already standardized on Atlassian and you want tight issue-to-code traceability.

06 · Branching workflows 5 min

The workflow is a team agreement, not a Git feature.

Git imposes no process. A branching workflow is the convention your team agrees on for how branches are named, how long they live, and how work reaches production. The honest truth: simpler workflows win for most teams — complexity should be earned by a real constraint.

Branching workflowa shared set of rules for creating, naming, reviewing, and merging branches so everyone integrates predictably. The big variable is branch lifetime: hours and days keep conflicts tiny; weeks invite painful "merge hell." It pairs directly with how your team plans work — Agile & Delivery.

Trunk-based — everyone on one short leash

main / trunk merged within hours
Pro
Tiny, near-constant integration means conflicts stay trivial; pairs naturally with continuous delivery.
Con
Demands strong automated tests and feature flags to keep an unfinished feature off users.

Fits  teams shipping continuously with solid CI — the modern default for product engineering.

GitHub Flow — branch, PR, merge, deploy

main feature branch + PR
Pro
Dead simple: one long-lived branch (main), short feature branches, review by PR, deploy on merge.
Con
Assumes you deploy from main frequently; offers no built-in slot for parallel released versions.

Fits  most web apps and services with continuous deployment — start here unless you have a reason not to.

GitFlow — many long-lived branches

main develop feature release
Pro
Explicit slots for develop, release, and hotfix — useful when you maintain several shipped versions at once.
Con
Long-lived branches breed big merges and overhead; overkill and often harmful for continuous web delivery.

Fits  versioned/desktop/on-prem software with scheduled releases — rarely the right call for a SaaS that ships daily.

The real variable is branch lifetime

  • Short-lived branches (hours/days) → small merges, rare conflicts, fast feedback.
  • Long-lived branches (weeks) → divergence compounds into "merge hell" and risky big-bang integrations.
  • Every modern workflow is, at heart, a push toward integrating small and often.
  • New web app / service → GitHub Flow. Simplest thing that works.
  • Mature team with strong CI and feature flags → Trunk-based. Fastest integration.
  • You ship versioned software and support several releases → GitFlow, and only then.
  • Unsure? Pick the simpler one. You can always add ceremony when a real constraint demands it — YAGNI.
07 · Recovery — reflog, reset, revert, bisect 4 min

Almost nothing in Git
is truly lost.

Commits you make are kept around even after you "lose" them — the reflog remembers where HEAD has been. Knowing four recovery tools turns most scary moments into a two-command fix.

refloga local log of every position HEAD has pointed to, kept for ~90 days. Botched a rebase or a hard reset? git reflog shows the commit you were on a moment ago, and git reset --hard HEAD@{1} puts you back.
reset — moves the branch pointer
main reset --hard

Rewrites local history — great before pushing, dangerous after. --soft keeps changes staged, --hard discards them.

revert — adds an undo commit
R undoes commit 2

Makes a new commit that cancels an old one. Safe on shared branches because it never rewrites history.

# local, not yet pushed → reset is fine git reset --soft HEAD~1 # undo commit, keep changes staged # already pushed / shared → revert git revert a1b2c3 # new commit that undoes it # recover from a lost rebase/reset git reflog # find the old HEAD… git reset --hard HEAD@{2}
good test mid bad binary search the history

git bisect binary-searches between a known-good and known-bad commit to pinpoint the one that broke things — log(n) tests, not n.

Five things to walk out with

1Commits are snapshots in a graph. Each points to its parent and a full tree — that one model explains everything else.
2Stage deliberately. The index lets you craft small, focused commits instead of dumping everything at once.
3Merge preserves, rebase rewrites. Never rebase history other people already have — that's the Golden Rule.
4Integrate small and often. Short-lived branches are the quiet secret behind every healthy workflow.
5reset for private, revert for shared. And the reflog means you can almost always get back.
Knowledge check

Did it stick?

Five quick questions on the commit model, staging, merge vs rebase, remotes, and recovery — instant feedback, no sign-in.

Rate this deck
be the first

Navigate with ← → or scroll · back to library