Git & Version Control

How Git Actually Works

Interactive guide for developers who want to understand the real mechanics, not just the commands.

📸
The Basics
Repos, commits, staging, and how Git thinks
📁
Repository
A folder tracked by Git. Contains your files plus a hidden .git/ folder storing the entire history.
📷
Commit
A snapshot of your project at a moment in time. Every commit has a unique 40-character SHA hash like a1b2c3d4...
🎭
Staging Area
A middle zone between your edits and the commit. You choose exactly which changes go into each snapshot.
🌍
Remote
A copy of your repo hosted online. You push to it and pull from it.
The 3 states of a file in Git:
Modified you changed it but have not told Git yet  →  Staged queued up for the next commit  →  Committed safely stored in Git history
bash · your first git workflow
# 1. Create a new repo
git init my-project
cd my-project

# 2. See what git sees
git status

# 3. Stage specific files (or use . for everything)
git add index.html
git add . # stages all changed files

# 4. Commit with a message
git commit -m "Add homepage layout"

# 5. View your commit history
git log --oneline
a1b2c3d Add homepage layout
f9e8d7c Initial commit

🔍 What's inside a commit?

Every commit stores a pointer to the project snapshot, the author's name and email, a timestamp, your message, and a pointer to the previous commit. That chain of pointers is what makes Git history work, like a linked list of snapshots.

bash · going back in time
# See what changed in a commit
git show a1b2c3d

# Undo last commit (keep the changes)
git reset --soft HEAD~1

# Undo last commit (throw away changes) ⚠️
git reset --hard HEAD~1

# Safely undo with a new reverse commit
git revert a1b2c3d
🌿
Branching
Parallel universes for your code

What is a branch, really?

A branch is just a lightweight pointer to a specific commit. Git is not copying files when you make one. Every new commit moves that pointer forward, while main stays where it was.

HOVER OVER COMMITS TO INSPECT
main
A · Initial commit author: you · 3 days ago
B · Add homepage author: you · 2 days ago
C · Fix navbar bug author: alex · 5 hrs ago
G · Merge: add-login → main author: you · just now
HEAD
add-login
D · Start login page author: you · 4 hrs ago
E · Add form validation author: you · 2 hrs ago
F · Style login UI author: you · 1 hr ago
add-login
Branch splits at B and rejoins at G with a merge commit.
bash · working with branches
# List all branches (* = current)
git branch
* main
add-login

# Create and switch to a new branch
git checkout -b add-login
# Modern syntax
git switch -c add-login

# Switch between branches
git switch main
git switch add-login

# Merge feature branch into main
git switch main
git merge add-login

# Delete branch after merging
git branch -d add-login
HEAD is just a pointer to whatever branch you are currently on. When you switch branches, HEAD moves. When you commit, HEAD and the branch pointer move forward together.

🔀 Merge vs Rebase

Two ways to combine branches, each leaving a different history.

bash · merge vs rebase
# Merge creates a merge commit and preserves full history
git merge add-login
# Result: A → B → C → G
# ↑
# D → E → F ↗

# Rebase replays commits on top for a linear history
git switch add-login
git rebase main
# Result: A → B → C → D' → E' → F'
# Commits D, E, and F get rewritten onto C

⚠️ Never rebase a branch other people are using.
🔍
Pull Requests
Code review, collaboration, and merging safely

Why not just merge directly?

On a team, no one should push straight to main. Pull Requests create a review step so someone else reads the code, asks questions, and approves before anything lands.

bash · the PR workflow
# 1. Create your feature branch
git switch -c feat/user-auth

# 2. Do your work and make commits
git add .
git commit -m "Add JWT login endpoint"
git commit -m "Add password hashing"

# 3. Push your branch to GitHub
git push -u origin feat/user-auth

# 4. Open the PR in your browser
# Compare and pull request appears after the push

# 5. After approval, merge
git switch main
git merge feat/user-auth
git push origin main
💬
Review Comments
Reviewers leave inline comments on specific lines. You respond, make changes, and resolve threads.
Approvals
Most teams require one or two approvals before merging, often enforced with branch protection rules.
🤖
CI Checks
Automated tests run on your branch. If they fail, the PR is blocked before the code reaches main.
🗜️
Squash & Merge
Combines all branch commits into one clean commit before merging so main stays readable.
Good PR habit: Keep PRs small and focused on one thing. Small, obvious changes get reviewed faster and more accurately.
⚔️
Merge Conflicts
When Git cannot decide and you have to

When do conflicts happen?

Git merges automatically most of the time. Conflicts happen when two branches modify the exact same lines in different ways and Git has no safe way to choose between them.

The conflict markers explained:
<<<<<<< HEAD marks your current branch
======= splits the two versions
>>>>>>> other-branch marks the incoming branch
INTERACTIVE: choose a resolution path below
Next steps after resolving:
git add . → git commit -m "Resolve merge conflict in getColor()"
bash · resolving a conflict end-to-end
# You are merging a branch and hit a conflict
git merge alex/feature-colors
CONFLICT (content): Merge conflict in utils.js
Automatic merge failed; fix conflicts and commit.

# See which files are conflicted
git status
both modified: utils.js

# Open utils.js and remove the conflict markers

# Mark as resolved by staging
git add utils.js

# Complete the merge
git commit -m "Merge: resolve color conflict in utils.js"
[main a1b2c3d] Merge branch 'alex/feature-colors'

🛡️ How to avoid conflicts in the first place

Keep branches short-lived. Pull from main often so your branch does not drift. Split large files into smaller modules and communicate with teammates about who is touching which surface area.

bash · stay in sync to avoid conflicts
# Fetch latest changes from remote
git fetch origin

# Pull = fetch + merge
git pull origin main

# Rebase your feature branch onto latest main before opening a PR
git fetch origin
git rebase origin/main