The short version
Git is a version control system — it tracks changes to your code over time so you can undo mistakes, collaborate with others, and maintain a full history of your project. These are the commands you'll use constantly once you start.
Most Git tutorials begin with a diagram explaining branching strategies and merge conflict resolution. That's fine eventually, but it's not what you need on day one. What you need is a handful of commands that cover the real situations that come up every single day — and an honest explanation of what each one actually does.
Here they are. No theory preamble, no git history lesson.
The commands you'll use every single day
git status — always start here
Before you do anything else in a project, run this. It tells you exactly what's happening: which files you've changed, which ones are staged and ready to commit, and which ones Git doesn't know about yet.
git status
Get into the habit of running it constantly. It's completely harmless — it never changes anything — and it saves you from a lot of confusion about what state your project is in.
git add — stage your changes
Staging means telling Git "I want to include these changes in my next commit." You can add specific files, a folder, or everything at once.
# Stage one specific file
git add index.js
# Stage everything that changed
git add .
# Stage all files in a specific folder
git add src/components/
# Stage parts of a file interactively (advanced)
git add -p
The reason staging exists (rather than just committing everything at once) is so you can bundle related changes into one commit even if you've been editing multiple unrelated things.
git commit — save a snapshot
A commit is a permanent snapshot of your staged changes, saved to your project's history. The message should describe what changed and — more importantly — why.
git commit -m "fix: redirect to login page when session expires"
# Stage everything and commit in one step (skips staging for tracked files)
git commit -am "update header styles for mobile"
Good commit messages make your history readable. Instead of "fixed stuff" or "changes", write something your future self will understand at a glance three months later.
git push — send your commits to GitHub
Commits live locally on your machine until you push them. Push sends them to the remote repository (GitHub, GitLab, etc.) so others can see them and so they're backed up.
# Push to the main branch
git push origin main
# Push a feature branch
git push origin feature/user-auth
# Push and set the upstream (first push of a new branch)
git push -u origin feature/user-auth
git pull — get the latest changes
Pull downloads and merges changes from the remote into your local branch. Always pull before you start working, especially when collaborating with others.
git pull
# Pull a specific branch
git pull origin main
The ones you need when things get more interesting
git log — see your project's history
Shows the list of commits, most recent first. The default output is verbose, so --oneline is your friend.
git log
# Compact, one line per commit
git log --oneline
# Show a visual graph of branches
git log --oneline --graph --all
git branch — work in isolation
Branches let you work on a feature without touching the main codebase. When you're done, you merge it back in. Always branch off main before starting anything new.
# See all your branches
git branch
# Create and immediately switch to a new branch
git checkout -b feature/add-dark-mode
# Switch between branches
git checkout main
# Modern alternative to checkout (Git 2.23+)
git switch main
git switch -c feature/add-dark-mode
git stash — shelve your unfinished work
Stash is for when you're mid-task but need to quickly switch to something else without committing half-done work. It saves your current changes to a temporary shelf, cleans your working directory, and lets you retrieve them later.
# Stash your current changes
git stash
# See what's in your stash
git stash list
# Get your stashed changes back (and remove them from the stash)
git stash pop
# Apply stash without removing it from the list
git stash apply
git diff — see exactly what changed
Shows the line-by-line differences between your current files and the last commit. Great for reviewing your own work before staging or committing.
# See unstaged changes
git diff
# See changes that are staged (ready to commit)
git diff --staged
# Compare two branches
git diff main feature/add-dark-mode
The ones you search for in a panic
git reset — undo a commit (locally)
There are two useful variants. Use --soft when you committed too early and want to un-commit but keep your changes staged. Leave off the flag to unstage everything but keep the files as-is.
# Undo the last commit, keep changes staged
git reset --soft HEAD~1
# Undo the last commit, unstage everything (but keep the file changes)
git reset HEAD~1
# Nuclear option: undo commit AND throw away all changes
# (cannot be undone — be very sure)
git reset --hard HEAD~1
git revert — undo a commit safely
Unlike reset, revert doesn't rewrite history. It creates a new commit that reverses a previous one. This is the right tool when you've already pushed the commit to a shared branch and can't just reset.
# Get the commit hash from git log --oneline, then:
git revert abc1234
# Revert without immediately creating a commit
git revert --no-commit abc1234
git restore — discard changes to a file
Throws away all unsaved changes in a file and restores it to what it looked like at the last commit. Cannot be undone.
# Discard changes to one file
git restore index.js
# Discard all unstaged changes in the whole project
git restore .
A workflow that actually works day to day
Here's the sequence I use every time I start on something new:
# 1. Make sure I'm on main and up to date
git checkout main
git pull
# 2. Branch off for this task
git checkout -b fix/broken-search-results
# 3. Do the work, check what changed
git status
git diff
# 4. Stage and commit with a clear message
git add .
git commit -m "fix: search results now return correct page count"
# 5. Push the branch
git push -u origin fix/broken-search-results
# 6. Open a pull request on GitHub
That loop handles the vast majority of real day-to-day development work. The other commands above are there for when things go sideways — and they will, occasionally. Now you're ready for that too.
Common Git questions
What is the difference between Git and GitHub?
Git is the version control software that runs on your computer and tracks changes to your code. GitHub is a website that hosts Git repositories online, making it easy to share code, collaborate with others, and back up your work. You use Git locally and push your changes to GitHub. GitLab and Bitbucket are alternatives to GitHub that work the same way.
What is a merge conflict and how do you fix one?
A merge conflict happens when two people change the same part of the same file in different branches, and Git can't figure out which version to keep. Git marks the conflicting sections in the file with special markers. You open the file, manually decide which version is correct (or combine both), remove the markers, stage the file, and complete the merge with git commit. Tools like VS Code show conflicts visually, which makes this much less painful.
How do I undo a git add before committing?
Use git restore --staged filename to unstage a specific file, or git restore --staged . to unstage everything. This removes the file from the staging area without affecting the actual changes you made to it — the file still has your edits, it just won't be included in the next commit.
What does HEAD mean in Git?
HEAD is just a pointer to whatever commit you're currently on — usually the latest commit on your current branch. When you see HEAD~1 in commands, it means "one commit before the current one." HEAD~2 means two commits back. It's shorthand so you don't have to look up and type out the full commit hash every time.
What is git fetch vs git pull?
git fetch downloads changes from the remote but doesn't apply them to your local branch. It just updates your local record of what the remote looks like. git pull does a fetch and then immediately merges those changes into your current branch. Most of the time you want git pull. Use git fetch when you want to inspect incoming changes before merging them.