Skip to content

Latest commit

Β 

History

History
411 lines (306 loc) Β· 16.1 KB

File metadata and controls

411 lines (306 loc) Β· 16.1 KB

πŸ“Œ Lecture 2 β€” Version Control Deep Dive: Git Internals & Recovery


πŸ“ Slide 1 – πŸ’₯ The --hard That Cost a Demo

  • πŸ—“οΈ Friday, 4:48 p.m. β€” engineer realizes a feature branch has the wrong starting commit
  • πŸͺ“ Runs git reset --hard origin/main to "clean it up" β€” without committing the four hours of unstaged work
  • πŸ’€ git status is suddenly empty. So is the working tree. So is git log
  • πŸͺ¦ Demo on Monday. No backups
  • πŸ”¦ At 4:55 p.m. a senior engineer types git reflog. Everything is still there. Five minutes of recovery vs five hours of rewriting

πŸ€” Think: Git almost never actually deletes anything. Knowing where it hides things is the difference between panic and a 5-minute recovery.


πŸ“ Slide 2 – 🎯 Learning Outcomes

By the end of this lecture you will:

# πŸŽ“ Outcome
1 βœ… Explain Git's three object types: blob, tree, commit
2 βœ… Read what a ref is, and what HEAD really points at
3 βœ… Recover from a "lost commit" using the reflog
4 βœ… Use reset --soft / --mixed / --hard deliberately
5 βœ… Choose between merge, rebase, and bisect for the job at hand
6 βœ… Use modern Git ergonomics: switch, restore, worktree, maintenance

πŸ“ Slide 3 – πŸ—ΊοΈ Lecture Overview

graph LR
    A["πŸ“¦ .git/"] --> B["🧱 Object Model"]
    B --> C["🏷️ Refs & HEAD"]
    C --> D["πŸͺ€ Reflog"]
    D --> E["βͺ Reset Modes"]
    E --> F["πŸ”€ Merge vs Rebase"]
    F --> G["πŸ› οΈ bisect / worktree"]
Loading
  • πŸ“ Slides 1-4 β€” Inside .git/
  • πŸ“ Slides 5-9 β€” Refs, reflog, and three flavors of reset
  • πŸ“ Slides 10-14 β€” Merge, rebase, bisect, worktree
  • πŸ“ Slides 15-18 β€” Tags, stash, hooks, modern commands
  • πŸ“ Slides 19-22 β€” Antipatterns, real incidents, what's next

πŸ“ Slide 4 – πŸ“¦ What's Inside .git/?

.git/
β”œβ”€β”€ HEAD                # current branch ref
β”œβ”€β”€ config              # local repo config
β”œβ”€β”€ objects/            # blobs, trees, commits β€” the whole history
β”‚   β”œβ”€β”€ 4b/             # subdirs by first 2 chars of SHA
β”‚   β”‚   └── 825dc642cb6eb9a060e54bf8d69288fbee4904
β”‚   β”œβ”€β”€ info/
β”‚   └── pack/           # packed objects after `git gc`
β”œβ”€β”€ refs/
β”‚   β”œβ”€β”€ heads/main      # β†’ SHA of latest commit
β”‚   β”œβ”€β”€ tags/
β”‚   └── remotes/origin/
└── logs/               # the reflog lives here
  • πŸ—„οΈ The .git/ directory is your repository. Delete it and the project is just a folder
  • πŸ” Every commit, blob, branch, and tag is reachable from this tree
  • πŸ§ͺ Try in your QuickNotes fork: find .git/objects/ -type f | head β€” those are your blobs and commits

πŸ“ Slide 5 – 🧱 Three Object Types

graph LR
    C["πŸ“ Commit<br/>= snapshot + parent(s)"] --> T["🌳 Tree<br/>= directory listing"]
    T --> B["πŸ“„ Blob<br/>= file contents"]
    C -. parent .-> C2["πŸ“ Earlier Commit"]
Loading
Type Holds Real example
🟒 Blob A file's bytes (no name, no metadata) app/main.go content
🌳 Tree Names + modes + SHAs of blobs (and sub-trees) The app/ directory listing
πŸ“ Commit Tree SHA + parent(s) + author + message "feat(app): add /metrics"
  • πŸ”‘ Every object is identified by the SHA-1 of its contents (Git is moving to SHA-256; new repos can opt in via git init --object-format=sha256)
  • πŸ” Identical content β†’ identical SHA β†’ deduplication for free

πŸ’‘ Same index.html in two branches? One blob, two trees referencing it.


πŸ“ Slide 6 – πŸ” Cat-File: Seeing the Plumbing

# βœ… what's in HEAD?
$ git cat-file -t HEAD
commit

$ git cat-file -p HEAD
tree   a1b2c3...
parent 4d5e6f...
author Dmitrii Creed <...> 1716728400 +0300
committer ...
feat(app): introduce QuickNotes

# βœ… peek into the tree
$ git cat-file -p a1b2c3
100644 blob d4e5f6...  README.md
040000 tree 7a8b9c...  app
  • πŸ§ͺ cat-file -t <sha> prints the type; -p pretty-prints
  • 🧰 These are the plumbing commands β€” what the porcelain (add, commit, log) calls underneath
  • πŸ”¬ The Pro Git book, chapter 10, walks the whole object model β€” required reading this week

πŸ“ Slide 7 – 🏷️ Refs: Where the Names Live

A ref is a human-readable name pointing at a commit SHA. That's it.

Ref What it points at Where it lives
HEAD The current commit (usually via a branch) .git/HEAD
refs/heads/main Tip of main .git/refs/heads/main
refs/tags/v1.0.0 A frozen commit .git/refs/tags/v1.0.0
refs/remotes/origin/main Last known tip of remote main Updated by git fetch
$ cat .git/HEAD
ref: refs/heads/feature/lab2
$ cat .git/refs/heads/feature/lab2
4b825dc642cb6eb9a060e54bf8d69288fbee4904
  • 🎯 A "detached HEAD" simply means HEAD stores a SHA directly, not a branch ref

πŸ“ Slide 8 – πŸͺ€ The Reflog: Git's Time Machine

The reflog is a per-ref history of where it has been. Default retention: 90 days for reachable commits, 30 days for unreachable.

$ git reflog
b8fc480 HEAD@{0}: commit: feat(app): introduce QuickNotes
6f044dd HEAD@{1}: checkout: moving from main to s26-refactor
6f044dd HEAD@{2}: pull: Fast-forward
0a87e1c HEAD@{3}: commit: refactor: reduce prescriptiveness
  • πŸ†˜ If your branch tip "disappears" β€” git reflog, find the SHA, git reset --hard <sha> or git branch rescue <sha>
  • πŸ§ͺ Even after reset --hard, the discarded commits are unreachable but still in .git/objects/ until git gc runs

πŸ’¬ "Reflog is the most reassuring thing I learned in my first year with Git." β€” every senior engineer


πŸ“ Slide 9 – βͺ Three Flavors of Reset

graph LR
    Cur["πŸ“ HEAD now"] -- "reset --soft" --> A["⬅️ HEAD moves<br/>πŸ“‹ Index unchanged<br/>πŸ“‚ Working tree unchanged"]
    Cur -- "reset --mixed (default)" --> B["⬅️ HEAD moves<br/>πŸ“‹ Index reset<br/>πŸ“‚ Working tree unchanged"]
    Cur -- "reset --hard" --> C["⬅️ HEAD moves<br/>πŸ“‹ Index reset<br/>πŸ’₯ Working tree overwritten"]
Loading
Mode Touches HEAD Touches Index Touches Working tree When to use
--soft βœ… ❌ ❌ Re-arrange last few commits, keep changes staged
--mixed βœ… βœ… ❌ "Un-add" files; keep edits in working tree
--hard βœ… βœ… βœ… Burn down to a clean state β€” dangerous

⚠️ Only --hard is destructive to uncommitted work. Always check git status before running it.


πŸ“ Slide 10 – πŸ†• switch and restore β€” Modern Ergonomics

Git 2.23 (Aug 2019) split the overloaded git checkout into two clearer commands:

Old (still works) New What it does
git checkout main git switch main Change branch
git checkout -b feat/x git switch -c feat/x Create + switch
git checkout main app/main.go git restore --source=main app/main.go Restore a file to a version
git checkout -- app/main.go git restore app/main.go Discard working-tree changes
  • βœ… Prefer switch / restore in new tutorials β€” intent is explicit
  • ❌ Avoid git checkout for file restore; the same command for "change branch" and "destroy my edits" is a footgun

πŸ“ Slide 11 – πŸ’Ύ git stash β€” The Suspense Account

# βœ… save current uncommitted work
$ git stash push -m "wip on /metrics"

# βœ… list stashes
$ git stash list
stash@{0}: On feature/lab2: wip on /metrics

# βœ… pop the most recent back (apply + delete)
$ git stash pop
  • πŸͺ€ Stash is not a substitute for a branch β€” entries don't survive git gc once they fall out of the reflog (≀ 30 days unreachable)
  • πŸ’‘ Use git stash --keep-index to stash unstaged changes while keeping staged ones
  • 🚨 Common bug: stash on branch A, switch to B, pop β†’ conflicts. Pop on the same branch you stashed on

πŸ“ Slide 12 – 🏷️ Tags: Lightweight vs Annotated

# ❌ lightweight β€” just a ref, no metadata
git tag v1.0.0

# βœ… annotated β€” tagged object with message, author, signature
git tag -a -s v1.0.0 -m "First production release"

# πŸ“€ push tags (they don't go with regular push)
git push origin v1.0.0
Lightweight Annotated
Just refs/tags/X β†’ SHA Full Git object with message
Cannot be signed Can be GPG/SSH signed
Useful as private bookmarks Use for all public releases

πŸ’‘ Releases in CI/CD (next lecture) trigger on annotated, signed tags β€” that's how you prove the artifact really came from this codebase.


πŸ“ Slide 13 – πŸ”€ Merge vs Rebase: Two Truths About History

graph LR
    subgraph "merge"
        M1["A"] --> M2["B"] --> M3["C"]
        M1 --> M4["D"] --> M5["E"]
        M3 --> MM["M ⬅️ merge commit"]
        M5 --> MM
    end
    subgraph "rebase"
        R1["A"] --> R2["B"] --> R3["C"] --> R4["D'"] --> R5["E'"]
    end
Loading
git merge git rebase
Resulting history Preserves branch topology Linear, easier to read
Creates new commits? One merge commit Rewrites every commit on the rebased branch
Safe on shared branches? βœ… ❌ β€” never rebase pushed commits others depend on
Conflicts resolved Once Once per commit on the rebased branch
  • πŸ§ͺ Rule of thumb in this course: rebase your feature/labN onto main before opening the PR; merge the PR itself
  • ⚠️ Never rebase main itself β€” that's a public branch

πŸ“ Slide 14 – πŸ› git bisect: Binary Search for the Bug

QuickNotes returned a 500 yesterday but worked last week. Where did it break?

$ git bisect start
$ git bisect bad HEAD                       # current is broken
$ git bisect good v1.0.0                    # this tag was fine
# Git checks out a commit ~midway
$ go test ./...                             # or any reproducer
$ git bisect good   # or `bad`
# Git narrows further...
$ git bisect reset                          # when found
  • 🎯 With N commits between good and bad, you find the culprit in logβ‚‚(N) steps
  • πŸ€– Want it automatic? git bisect run go test ./... β€” Git iterates until the test starts failing
  • πŸͺ€ Real-world story: the Linux kernel team uses bisect to find regressions across tens of thousands of commits, multiple times a week

πŸ“ Slide 15 – 🌲 git worktree: Multiple Branches, Same Repo, No Stashing

# βœ… keep main checked out in ., add an extra checkout for a hotfix
$ git worktree add ../quicknotes-hotfix hotfix/auth
$ cd ../quicknotes-hotfix
# do hotfix work...
$ cd -
# main checkout untouched the whole time
$ git worktree list
/home/you/quicknotes        b8fc480 [feature/lab2]
/home/you/quicknotes-hotfix  a1b2c3d [hotfix/auth]
  • πŸ†• Available since Git 2.5 (Jul 2015), polished by 2.30+
  • ⚑ Faster than stashing + switching for "I need to look at another branch right now"
  • 🧹 Clean up with git worktree remove ../quicknotes-hotfix

πŸ’‘ We'll use worktrees in Lab 7 to keep the Ansible playbook and the app side-by-side without juggling stashes.


πŸ“ Slide 16 – 🩺 git maintenance: Keep the Repo Fast

# βœ… schedule weekly maintenance (cron / launchd / systemd)
git maintenance start

# βœ… run it manually
git maintenance run --task=gc --task=loose-objects --task=incremental-repack
  • 🧹 Compacts loose objects, prunes the reflog, refreshes commit-graph for fast log/merge/bisect
  • πŸ“‰ On a multi-GB repo (Linux kernel, Chromium), this is the difference between git log taking 2s vs 45s
  • πŸ†• Available since Git 2.29 (Oct 2020); enabled by default in many distros from 2.42+

πŸ“ Slide 17 – πŸͺ Pre-commit Hooks: Catch It Before You Push

.git/hooks/ is local to the repo. The pre-commit framework manages shareable hooks across the team:

# βœ… .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: trailing-whitespace
      - id: check-merge-conflict
      - id: no-commit-to-branch
        args: [--branch, main]
  • πŸ›‘οΈ Block bad commits at your laptop before CI burns minutes finding them
  • πŸ§ͺ In Lab 3, your CI will run gofmt/go vet/go test β€” pre-commit lets the same checks run before push
  • ❌ Avoid running heavy test suites in pre-commit β€” engineers will disable hooks they find slow

πŸ“ Slide 18 – 🧹 Common Antipatterns

πŸ”₯ Antipattern βœ… Better
git push --force to a shared branch git push --force-with-lease (refuses if remote moved)
git reset --hard without checking git status first git stash push then reset; reflog will save you anyway
git pull (which is fetch + merge) on a feature branch git fetch && git rebase origin/main for linear history
Committing secrets ("I'll remove it next push") Add to .gitignore first; if leaked, rotate, then BFG-clean
50-line commit messages with no subject Subject ≀ 50 chars, blank line, body explains why
Squash-merging a 200-commit PR Split into smaller PRs; squash hides bisect points

πŸ“ Slide 19 – πŸ“œ Real Story: AWS Keys in Git History

  • πŸ—“οΈ 2015 β€” A developer commits AWS access keys to a public repo
  • πŸ€– Within minutes, bots scrape GitHub for credentials and spin up cryptominers on their account
  • πŸ’Έ The student's $0 AWS account ends up with a $2,300 bill by morning
  • πŸ› οΈ Fixing leaked secrets means rewriting history with BFG Repo-Cleaner or git filter-repo β€” and rotating the credential, because once it's pushed, assume it's harvested
  • πŸͺ¦ Lesson: .gitignore your .env, never commit *.pem, and run a secret scanner in CI (we will, Lab 9)

πŸ€” Think: Why is "just delete the commit" not enough?

(Because everyone who cloned still has it. And bots cache it. And GitHub's API still serves the diff.)


πŸ“ Slide 20 – 🧠 Key Takeaways

  1. 🧱 Three objects, one truth: blobs are bytes, trees are listings, commits are snapshots
  2. 🏷️ A ref is just a name on a SHA β€” HEAD, branches, tags, all the same idea
  3. πŸͺ€ The reflog saves your job β€” Git almost never throws anything away
  4. βͺ --soft / --mixed / --hard are different tools β€” pick deliberately
  5. πŸ”€ Rebase your feature branch onto main, merge the PR β€” the best of both worlds
  6. πŸ› οΈ Modern commands (switch, restore, worktree, maintenance) are not optional in 2026

πŸ’¬ "Git is hard. Print this lecture and put it on the wall." β€” every junior engineer eventually


πŸ“ Slide 21 – πŸš€ What's Next + πŸ“š Resources

  • πŸ“ Next lecture: CI/CD β€” turning every push into a test, build, and (eventually) deploy
  • πŸ§ͺ Lab 2: Explore Git's object model on the QuickNotes repo, force a reset --hard, recover via reflog, tag a release, rebase a feature branch
  • πŸ“– Read this week:
  • πŸ› οΈ Tools to try:
    • πŸ” tig β€” a curses interface over git log (apt install tig)
    • 🎨 git log --oneline --graph --all --decorate β€” the only graph you need
    • 🧰 git-absorb β€” auto-creates fixup commits
graph LR
    P["πŸ“ Last Week<br/>DevOps + Git"] --> Y["πŸ“ You Are Here<br/>Git Internals"]
    Y --> N["πŸ€– Week 3<br/>CI/CD"]
    N --> M["πŸ’» Week 4<br/>OS & Networking"]
Loading

🎯 Remember: You don't need to memorize every Git command. You need to know what's in .git/, what HEAD points at, and how to find the reflog when something explodes.