DEV Community

Alex Chen
Alex Chen

Posted on

Git Explained with Diagrams: The Visual Guide Every Developer Needs

Git Explained with Diagrams: The Visual Guide Every Developer Needs

I've taught Git to 5 people. These are the diagrams that finally made it click.

What Actually IS Git?

Git isn't GitHub. Git isn't a folder. Git is a snapshot machine.

Every time you commit, Git takes a photo of your project:

Commit 1: [πŸ“Έ photo of: index.html + style.css]
Commit 2: [πŸ“Έ photo of: index.html + style.css + app.js]  
Commit 3: [πŸ“Έ photo of: index.html (edited) + style.css + app.js]

Each photo is permanent. You can go back to any photo.
You can compare any two photos.
You can branch from any photo.
Enter fullscreen mode Exit fullscreen mode

That's it. Everything else is just convenience around this concept.

The Three States (This Is Where Everyone Gets Confused)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Your Working Directory        β”‚
β”‚   (The actual files on your disk)       β”‚
β”‚                                         β”‚
β”‚   You EDIT files here                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚ git add
               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Staging Area               β”‚
β”‚   (The "waiting room" for commits)      β”‚
β”‚                                         β”‚
β”‚   Files ready to be committed           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚ git commit
               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            Repository (.git)            β”‚
β”‚   (The permanent snapshot history)      β”‚
β”‚                                         β”‚
β”‚   Commits live here forever             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode
# The daily cycle:
git add .          # Move changes β†’ Staging
git commit -m "..." # Move staging β†’ Repository
# Repeat!
Enter fullscreen mode Exit fullscreen mode

The Most Common Workflow

Day starts:
  [Working Dir] ──edit──→ [modified files]
                      β”‚
                      git add .
                      β–Ό
              [Staging Area] ──files ready──→
                      β”‚
                      git commit -m "feat: login page"
                      β–Ό
              [Repository] ──new snapshot──→

Day ends: push to share with team
                      β”‚
                      git push origin main
                      β–Ό
              [Remote (GitHub)]
Enter fullscreen mode Exit fullscreen mode

Branches Are Just Pointers

Before branching:

main ──●──●──●──●  (you're here)

After git branch feature-login:

main ──●──●──●──●
                  ╰──feature-login ──○  (you're here now)

After some work:

main ──●──●──●──●
                  ╰──feature-login ──○──●──●  (3 new commits)

After git merge (fast-forward):

main ──●──●──●──●──●──●──●  (feature-login's commits are now in main)
                  (feature-login pointer is deleted or kept)
Enter fullscreen mode Exit fullscreen mode

A branch = a named pointer to a commit. That's it.

Merge vs Rebase (With Pictures)

Merge: Creates a merge commit

main:    A ── B ── C
                    β•²
feature: D ── E ── F ╳── M (merge commit)

Result: A linear history with a merge bubble
Enter fullscreen mode Exit fullscreen mode
git checkout main
git merge feature-login
# Creates a merge commit combining both histories
Enter fullscreen mode Exit fullscreen mode

Rebase: Rewrites history to look linear

Before rebase:
main:    A ── B ── C
feature: D ── E ── F

After rebase:
main:    A ── B ── C
feature:                D' ── E' ── F'  (replayed on top of main)

After fast-forward merge:
main:    A ── B ── C ── D' ── E' ── F'
Enter fullscreen mode Exit fullscreen mode
git checkout feature-login
git rebase main         # Replay my commits on top of latest main
git checkout main
git merge feature-login # Fast-forward (no merge commit needed)
Enter fullscreen mode Exit fullscreen mode

Rule of thumb: Rebase local branches before merging. Merge shared branches.

Remote = Another Copy of the Repo

Your Computer:                    GitHub:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ .git/        β”‚                 β”‚ origin/main  β”‚
β”‚   main ──●───┼──push──────────▢│   main ──●───│
β”‚   feat ──●────                 β”‚              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

git push    β†’ Send your commits to remote
git pull    β†’ Get remote commits + merge them
git fetch   β†’ Get remote commits (don't merge yet)
Enter fullscreen mode Exit fullscreen mode

The Danger Zone: Force Push

Normal push (safe):
Remote: A ── B ── C
Local:  A ── B ── C ── D     git push β†’ Remote gets D βœ…

Force push (dangerous!):
Remote: A ── B ── C ── X ── Y  (someone else pushed!)
Local:  A ── B ── C ── D        git push --force β†’ 
Remote: A ── B ── C ── D       ❌ X and Y are GONE!

Only force push on YOUR own branch that nobody else uses!
Enter fullscreen mode Exit fullscreen mode

Common Scenarios (Cheat Sheet)

Scenario 1: "I messed up, undo!"

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

# Undo last commit (keep changes unstaged)
git reset HEAD~1

# Undo last commit (LOSE changes completely)
git reset --hard HEAD~1

# Undo working directory changes (before committing)
git checkout -- file.txt
git restore file.txt          # Newer command (same thing)
Enter fullscreen mode Exit fullscreen mode

Scenario 2: "I committed to wrong branch"

# Move last commit to different branch
git branch new-branch         # Create branch where commit currently sits
git reset --hard HEAD~1       # Remove commit from current branch
git checkout new-branch       # Switch to branch with the commit
Enter fullscreen mode Exit fullscreen mode

Scenario 3: "Someone pushed code I don't have"

git fetch origin              # Get their commits
git rebase origin/main        # Put your work on top of theirs
# Resolve conflicts if any
git push origin feature       # Push clean history
Enter fullscreen mode Exit fullscreen mode

Scenario 4: "I need to see what changed"

# What changed since last commit?
git diff                      # Unstaged changes
git diff --staged             # Staged changes

# What changed between two commits?
git diff abc123 def456        # Two specific commits
git diff main..feature        # Between two branches

# What did this commit change?
git show abc123               # One commit's details

# What files changed?
git diff --name-only          # Just filenames
Enter fullscreen mode Exit fullscreen mode

Scenario 5: "Find that thing I wrote"

# Find commit by message
git log --grep="login" --oneline

# Find commit that changed a specific line
git log -S "function login" --oneline

# Find who changed this line
git blame file.ts -L 10,20    # Lines 10-20 only

# See file history
git log --follow -- file.ts   # Including renames
Enter fullscreen mode Exit fullscreen mode

The .gitignore File

# Dependencies
node_modules/

# Build output
dist/
build/
*.js.map

# Environment
.env
.env.local

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db

# Logs
logs/
*.log

# But keep this file!
!.env.example
Enter fullscreen mode Exit fullscreen mode

Pro tip: Use gitignore.io to generate .gitignore for your stack.

My Git Config

# Essential config
git config --global user.name "Your Name"
git config --global user.email "your@email.com"

# Better defaults
git config --global init.defaultBranch main
git config --global core.editor "code --wait"
git config --global pull.rebase true        # Always rebase on pull

# Useful aliases
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all -15"

# Now you can type:
git co feature-login    # instead of git checkout
git lg                   # beautiful graph view
Enter fullscreen mode Exit fullscreen mode

The Git Graph (What It All Looks Like)

* abc1234 (HEAD -> main) Merge pull request #42
|\
| * def5678 Fix login validation
| * ghi9012 Add password strength checker
|/
* jkl3456 Feature: user authentication
* mno7890 Refactor API client
* pqr0123 Setup project structure
* stu4567 Initial commit
Enter fullscreen mode Exit fullscreen mode
# Generate this view anytime:
git log --oneline --graph --all -20
Enter fullscreen mode Exit fullscreen mode

Quick Reference Card

Command Does
git status Show state of files
git add <file> Stage changes
git commit -m "msg" Save snapshot
git push Send to remote
git pull Get + merge from remote
git branch <name> Create branch
git checkout <branch> Switch branch
git merge <branch> Combine branches
git rebase <branch> Replay on top of branch
git stash Temporarily save changes
git reset --hard HEAD~1 Undo last commit
git diff Show changes
git log --oneline Commit history
git blame <file> Who changed each line

What Git concept took you the longest to understand?

Follow @armorbreak for more developer guides.

Top comments (0)