Git & GitHub
Learn version control from scratch - how to track changes, collaborate with others, and never lose your work again.
The Time Machine Problem#
Imagine writing a novel. You make changes, delete paragraphs, rewrite chapters. Three days later, you realize the version from Tuesday was better. But you can't remember exactly what it said.
Now imagine a magical undo button that lets you go back to any moment in time - not just the last change, but any version you ever saved. "Show me the story from Tuesday at 3pm." Done.
That's Git. It's a time machine for your code.
And GitHub? That's like storing your time machine in the cloud so your teammates can access it too.
Why You Need Version Control#
Without version control:
- You can't undo mistakes from three days ago
- "What did I change?" becomes an unanswerable question
- Working with others means emailing files back and forth
- "final_v2_ACTUALLY_final.js" becomes your life
With version control:
- Every change is saved forever
- You can see exactly what changed and when
- Multiple people can work on the same code without chaos
- You can experiment without fear of breaking things
Not Optional
Every professional development team uses Git. It's not a nice-to-have - it's as fundamental as knowing how to use a text editor.
Getting Started with Git#
First, install Git (it's probably already on your computer). Then tell Git who you are:
git config --global user.name "Your Name"
git config --global user.email "your@email.com"
git config --global init.defaultBranch main
Creating Your First Repository#
A "repository" (or "repo") is a folder that Git is tracking:
# Create a new project
mkdir my-project
cd my-project
git init
That git init command creates a hidden .git folder. That folder IS your time machine - it stores every version of every file.
The Three Stages#
Git has three "places" where your files can be:
- Working Directory - Your actual files, as you see them
- Staging Area - Files you've marked for the next save
- Repository - The permanent record (your time machine)
Think of it like packing for a trip:
- Working Directory = everything in your room
- Staging Area = items you've put in your suitcase
- Repository = the suitcase after you've zipped it shut
# See what's changed (working directory)
git status
# Move files to staging area
git add filename.js # Stage one file
git add . # Stage all changes
# Save staged files to repository (commit)
git commit -m "Add user login feature"
Your First Commit#
# Create a file
echo "# My Project" > README.md
# Check status
git status
# Shows: Untracked files: README.md
# Stage it
git add README.md
# Check status again
git status
# Shows: Changes to be committed: README.md
# Commit it
git commit -m "Add README"
# Now it's saved in your time machine forever
The Daily Workflow#
Here's what you'll do dozens of times a day:
# 1. Make changes to files
# 2. See what changed
git status
# 3. Stage the changes you want to commit
git add .
# 4. Commit with a message
git commit -m "Fix login validation bug"
# 5. Push to GitHub (so teammates can see)
git push
That's it. Make changes, stage, commit, push. Repeat.
Branches: Parallel Universes#
Here's where Git gets powerful. Imagine you want to try a risky experiment - what if you could create a parallel universe, try the experiment there, and only bring the changes back if they work?
That's a branch.
# Create and switch to a new branch
git checkout -b feature/user-profiles
# Now you're in a parallel universe. Any changes
# you make here don't affect 'main'.
You can have multiple branches - multiple parallel universes - at once:
main ─────────●─────────●─────────●────────────────●
\ /
●─────●─────●─────────────────────
feature/user-profiles
Working with Branches#
# Create a new branch and switch to it
git checkout -b feature/shopping-cart
# Make your changes, then commit
git add .
git commit -m "Add shopping cart component"
# Switch back to main
git checkout main
# Your changes aren't here! They're safely in your branch.
# Go back to your branch
git checkout feature/shopping-cart
# Your changes are back!
Branch Naming#
Good branch names tell others what's happening:
# Good names
feature/user-authentication # New feature
fix/login-timeout-error # Bug fix
docs/api-documentation # Documentation
refactor/database-queries # Code cleanup
# Bad names
my-branch
stuff
johns-changes
asdf
GitHub: Git in the Cloud#
Git stores your repo on your computer. GitHub stores a copy in the cloud so you can:
- Access your code from anywhere
- Collaborate with others
- Back up your work
- Use pull requests for code review
Connecting to GitHub#
# Clone an existing repo from GitHub
git clone https://github.com/username/repo-name.git
# Or connect your local repo to GitHub
git remote add origin https://github.com/username/repo-name.git
# Push your code to GitHub
git push -u origin main
The -u flag remembers the connection, so next time you just type git push.
The GitHub Flow#
This is the workflow used by most teams:
1. Create branch from main
└─> 2. Make changes and commit
└─> 3. Push branch to GitHub
└─> 4. Open Pull Request
└─> 5. Team reviews code
└─> 6. Merge to main
Let's walk through it:
# 1. Start from an updated main branch
git checkout main
git pull origin main
# 2. Create your feature branch
git checkout -b feature/user-auth
# 3. Make changes, commit them
git add .
git commit -m "Add JWT authentication"
# 4. Push your branch to GitHub
git push -u origin feature/user-auth
# 5. Go to GitHub and open a Pull Request
# (A PR asks others to review and merge your code)
# 6. After PR is merged, clean up
git checkout main
git pull origin main
git branch -d feature/user-auth
Pull Requests: Code Review#
A Pull Request (PR) says "I made these changes, can someone review them before they go into main?"
Creating a Good PR#
When you open a PR on GitHub, write a clear description:
## What does this PR do?
Adds JWT-based authentication with login and register endpoints.
## Changes
- Added /auth/login endpoint
- Added /auth/register endpoint
- Added JWT middleware for protected routes
- Added password hashing with bcrypt
## How to test
1. Run `npm test` - all tests should pass
2. Try logging in with valid credentials
3. Try accessing /api/profile without a token (should 401)
4. Try accessing /api/profile with a valid token (should work)
## Checklist
- [ ] Tests pass
- [ ] No console.logs left in code
- [ ] ENV variables documented
Reviewing PRs#
When reviewing someone else's PR:
- Read the description first
- Look at the code changes
- Check for bugs, security issues, unclear code
- Leave constructive comments
- Approve or request changes
Be Kind
Remember there's a human on the other end. "This approach might have issues with X" is better than "This is wrong."
Writing Good Commit Messages#
Your commit messages are documentation. Someone (including future you) will read them to understand what changed.
The Format#
<type>: <what you did>
# Examples
feat: Add user registration endpoint
fix: Resolve database timeout on large queries
docs: Update API documentation for auth endpoints
refactor: Extract validation into separate middleware
test: Add integration tests for payment flow
chore: Update npm dependencies
What Makes a Good Message#
# GOOD - Explains what and hints at why
"Fix login timeout by increasing database pool size"
"Add email validation to registration form"
"Refactor authentication to use JWT instead of sessions"
# BAD - Meaningless
"Fix stuff"
"WIP"
"Update"
"asdfgh"
"Final changes"
Handling Merge Conflicts#
Conflicts happen when two people change the same lines. Git doesn't know which version to keep.
# You'll see something like this in the file:
<<<<<<< HEAD
const timeout = 5000;
=======
const timeout = 10000;
>>>>>>> feature/other-branch
# HEAD = your version
# Below ======= = their version
To resolve:
- Open the file
- Decide what the final code should be
- Delete the
<<<<<<<,=======, and>>>>>>>markers - Stage and commit
# After editing the file to resolve conflicts
git add resolved-file.js
git commit -m "Resolve merge conflict in timeout config"
Avoiding Conflicts#
Keep your branch up to date with main:
git checkout feature/my-feature
git fetch origin
git merge origin/main
# Or: git rebase origin/main
Do this regularly, especially before opening a PR.
GitHub Actions: Automation#
GitHub Actions runs code automatically when things happen - like when you push or open a PR.
A Simple CI Pipeline#
Create .github/workflows/ci.yml:
name: CI
# When to run
on:
push:
branches: [main]
pull_request:
branches: [main]
# What to do
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
Now every push and PR automatically runs your tests. If they fail, you'll know before merging.
With Databases#
If your tests need MongoDB or Redis:
jobs:
test:
runs-on: ubuntu-latest
services:
mongodb:
image: mongo:7
ports:
- 27017:27017
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
env:
MONGODB_URI: mongodb://localhost:27017/test
REDIS_URL: redis://localhost:6379
What NOT to Commit#
Create a .gitignore file to exclude files that shouldn't be in your repo:
# Dependencies - too big, can be reinstalled
node_modules/
# Environment variables - contain secrets!
.env
.env.local
# Build output - generated, not source
dist/
build/
# Logs
*.log
logs/
# Editor settings - personal preference
.vscode/
.idea/
# OS files
.DS_Store
Thumbs.db
# Test coverage
coverage/
Never Commit Secrets
If you accidentally commit a password or API key, it's in Git's history forever. Changing the file doesn't help - the secret is in the old commits. You'll need to rotate the secret.
Essential Commands Cheat Sheet#
# Starting out
git init # Create a repo
git clone <url> # Copy a repo from GitHub
# Daily workflow
git status # See what changed
git add . # Stage all changes
git commit -m "message" # Save changes
git push # Send to GitHub
# Branches
git checkout -b name # Create and switch to branch
git checkout main # Switch to main
git branch -d name # Delete branch
# Getting updates
git fetch # Download changes (don't apply)
git pull # Download and apply changes
# History
git log --oneline # See recent commits
git diff # See unstaged changes
# Oops
git checkout -- file # Discard changes to file
git reset HEAD file # Unstage file
git stash # Temporarily hide changes
git stash pop # Bring back stashed changes
Key Takeaways#
Git and GitHub are essential tools you'll use every day:
- Commit often - Small, frequent commits are easier to understand and undo
- Use branches - Never work directly on main
- Write clear messages - Your future self will thank you
- Open PRs - Get feedback before merging
- Set up CI - Catch bugs before they reach main
- Never commit secrets - Use
.gitignoreand environment variables
The Habit
Make committing second nature. Finished a small task? Commit. About to try something risky? Commit first. Going to lunch? Commit. The more checkpoints you have, the safer you are.
Ready to level up your skills?
Explore more guides and tutorials to deepen your understanding and become a better developer.