Git Commands Cheat Sheet: From Basics to Advanced
Complete git cheat sheet with 50+ commands, real examples, and ASCII branch diagrams. Used by 100M+ developers worldwide. Bookmark this reference.
Git is the version control system behind virtually every software project on Earth. Over 100 million developers use it daily (GitHub Octoverse, 2024). Yet most developers only know a handful of commands. This cheat sheet covers everything from first-time setup to advanced techniques like bisect and reflog, with real examples you can copy and run. Bookmark it. You’ll be back.
Key Takeaways
- Git's daily workflow (add, commit, push, pull) covers about 80% of what most developers do.
- Rebase rewrites history into a clean line. Merge preserves it. Pick based on your team's convention.
- Destructive commands (reset --hard, force push) can't be undone easily. Use reflog as your safety net.
- Over 100 million developers use Git daily (GitHub Octoverse, 2024).
How Do You Set Up Git for the First Time?
Git requires a username and email before your first commit. According to Stack Overflow’s 2024 Developer Survey, 93.9% of developers use Git for version control (Stack Overflow, 2024). Getting the config right from the start saves headaches later.
Identity
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
Editor and defaults
# Set VS Code as default editor
git config --global core.editor "code --wait"
# Set default branch name to main
git config --global init.defaultBranch main
# Enable colored output
git config --global color.ui auto
Check your config
# List all settings
git config --list
# Check a specific value
git config user.name
Tip
Use --global for settings that apply to all repos. Drop it to set per-repo overrides. Your global config lives at ~/.gitconfig.
SSH key setup
# Generate an ED25519 key (recommended over RSA)
ssh-keygen -t ed25519 -C "you@example.com"
# Start the SSH agent and add your key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# Copy public key to clipboard (macOS)
pbcopy < ~/.ssh/id_ed25519.pub
Then paste the public key into your GitHub/GitLab SSH settings. Test with ssh -T git@github.com.
What Are the Essential Daily Git Commands?
The daily workflow boils down to five commands: status, add, commit, push, and pull. A 2024 JetBrains survey found that 77% of developers commit at least once per day (JetBrains Developer Ecosystem, 2024). Master these first.
| Command | What It Does | Example |
|---|---|---|
| git init | Create a new repository | git init my-project |
| git clone <url> | Copy a remote repo locally | git clone git@github.com:user/repo.git |
| git status | Show working tree state | git status (run this constantly) |
| git add <file> | Stage changes for commit | git add src/app.ts |
| git add -p | Stage changes interactively, hunk by hunk | git add -p (then y/n per change) |
| git commit -m | Save staged changes with a message | git commit -m 'fix: resolve login bug' |
| git push | Upload commits to remote | git push origin main |
| git pull | Fetch + merge from remote | git pull origin main |
The staging area explained
Git has three states for your files: modified, staged, and committed. The staging area (index) lets you craft commits precisely. You don’t have to commit everything at once.
# Stage specific files
git add src/utils.ts src/helpers.ts
# Stage all changes in current directory
git add .
# Stage only tracked files (skip new untracked files)
git add -u
# Unstage a file (keep the changes, just remove from staging)
git restore --staged src/utils.ts
Tip
git add -p (patch mode) is the most underused Git feature. It lets you stage individual hunks within a file. Perfect for splitting a messy working directory into clean, logical commits.
Writing good commit messages
# Short format (under 72 chars)
git commit -m "fix: prevent null pointer in user lookup"
# Long format with body (opens editor)
git commit
# In your editor:
# fix: prevent null pointer in user lookup
#
# The getUser() function returned undefined when the cache
# expired. Added a null check before accessing .name property.
# Fixes #142
Follow the Conventional Commits spec: type: description. Common types: feat, fix, docs, refactor, test, chore.
How Do You View History and Diffs?
Understanding what changed and when is half of version control. Git’s log and diff commands are powerful but verbose by default. These aliases and flags cut through the noise.
| Command | What It Does | Example |
|---|---|---|
| git log --oneline | Compact commit history | git log --oneline -20 |
| git log --graph | Visual branch history | git log --oneline --graph --all |
| git diff | Show unstaged changes | git diff src/app.ts |
| git diff --staged | Show staged changes | git diff --staged |
| git diff main..feature | Compare two branches | git diff main..feature -- src/ |
| git show <commit> | Show a specific commit's changes | git show abc1234 |
| git blame <file> | Show who changed each line | git blame src/app.ts |
| git log -S 'text' | Search commits for added/removed text | git log -S 'TODO' --oneline |
Useful log formats
# Pretty one-line graph (add as alias)
git log --oneline --graph --decorate --all
# Commits by a specific author
git log --author="Your Name" --oneline
# Commits in a date range
git log --after="2026-01-01" --before="2026-03-01" --oneline
# Files changed in each commit
git log --stat --oneline -10
How Does Git Branching Work?
Branches are Git’s killer feature. They’re lightweight pointers to commits, not copies of your codebase. Creating a branch takes microseconds. The 2024 GitHub Octoverse report shows the average active repo has 8 branches (GitHub Octoverse, 2024). Use them aggressively.
| Command | What It Does | Example |
|---|---|---|
| git branch | List local branches | git branch |
| git branch -a | List all branches (local + remote) | git branch -a |
| git branch <name> | Create a new branch | git branch feature/login |
| git checkout <branch> | Switch to a branch | git checkout feature/login |
| git checkout -b <name> | Create and switch in one step | git checkout -b fix/header-bug |
| git switch <branch> | Switch branches (modern syntax) | git switch main |
| git switch -c <name> | Create and switch (modern syntax) | git switch -c feature/search |
| git branch -d <name> | Delete a merged branch | git branch -d feature/login |
| git branch -D <name> | Force-delete an unmerged branch | git branch -D experiment |
Branch naming conventions
Most teams follow a pattern: type/short-description. Common prefixes:
feature/for new functionalityfix/orbugfix/for bug repairshotfix/for urgent production fixesrelease/for release preparationchore/for maintenance tasks
# Good branch names
git checkout -b feature/user-authentication
git checkout -b fix/cart-total-rounding
git checkout -b hotfix/security-patch-2026-03
# Bad branch names (too vague)
git checkout -b update
git checkout -b fix
git checkout -b johns-branch
What’s the Difference Between Git Merge and Rebase?
This is the most debated question in Git workflows. Merge preserves history as it happened. Rebase rewrites it into a clean line. According to a 2023 GitKraken survey, 75% of development teams use a merge-based strategy, while 25% prefer rebase (GitKraken Git Report, 2023). Neither is wrong. Pick one and be consistent.
Merge: preserve history
git checkout main
git merge feature/login
# Before merge:
A---B---C feature/login
/
D---E---F---G main
# After merge:
A---B---C
/ \
D---E---F---G---H main (H = merge commit)
Merge creates a new commit (H) that ties the branches together. The full history of both branches stays intact. This is the safe, conservative choice.
Rebase: rewrite onto a clean line
git checkout feature/login
git rebase main
# Before rebase:
A---B---C feature/login
/
D---E---F---G main
# After rebase:
A'--B'--C' feature/login
/
D---E---F---G main
Rebase replays your commits (A, B, C) on top of the latest main. The result is a perfectly linear history. The tradeoff: it rewrites commit hashes (A becomes A’).
Warning
Never rebase commits that other people have pulled. Rewriting shared history forces everyone to re-sync, which creates duplicate commits and confusion. Only rebase your own local, unpushed work.
When to use which?
- Merge when you want a clear record that a feature branch existed.
- Rebase when you want a clean, linear history before merging.
- Squash merge (
git merge --squash) when the feature branch has messy interim commits.
A common workflow: rebase your feature branch onto main locally, then merge with --no-ff to create a merge commit on main. You get clean history and a clear merge point.
# Clean workflow
git checkout feature/login
git rebase main # Clean up your branch
git checkout main
git merge --no-ff feature/login # Merge with a merge commit
git branch -d feature/login
How Do You Undo Mistakes in Git?
Everyone messes up. Git’s safety net is deep if you know where to look. The key distinction: some commands are safe (they don’t destroy work), and some are destructive (they permanently discard changes).
| Command | What It Does | Destructive? |
|---|---|---|
| git restore <file> | Discard unstaged changes to a file | Yes, changes are lost |
| git restore --staged <file> | Unstage a file (keep changes) | No |
| git commit --amend | Edit the last commit message or add files | Rewrites last commit |
| git revert <commit> | Create a new commit that undoes a past commit | No (safe) |
| git reset --soft HEAD~1 | Undo last commit, keep changes staged | No |
| git reset --mixed HEAD~1 | Undo last commit, keep changes unstaged | No |
| git reset --hard HEAD~1 | Undo last commit, discard all changes | Yes, data loss |
Amending the last commit
# Fix the message
git commit --amend -m "fix: correct the login redirect URL"
# Add a forgotten file to the last commit
git add forgotten-file.ts
git commit --amend --no-edit
Reverting a pushed commit
# Safely undo a specific commit (creates a new "undo" commit)
git revert abc1234
# Revert a merge commit (specify which parent to keep)
git revert -m 1 abc1234
Revert is the safe option for shared branches. It doesn’t rewrite history.
Warning
git reset --hard permanently destroys uncommitted changes. There’s no undo. If you’ve already committed the work, reflog can save you. But unstaged changes lost to --hard are gone forever.
The three flavors of reset
git reset --soft HEAD~1
# Commit is undone. Changes are staged. Ready to re-commit.
git reset --mixed HEAD~1 (default)
# Commit is undone. Changes are in working directory but unstaged.
git reset --hard HEAD~1
# Commit is undone. Changes are gone. Working directory is clean.
How Do You Work with Remote Repositories?
Remote operations connect your local repo to GitHub, GitLab, Bitbucket, or any hosted Git server. As of 2024, GitHub hosts over 420 million repositories (GitHub About Page, 2024). Understanding remotes is essential for collaboration.
| Command | What It Does | Example |
|---|---|---|
| git remote -v | List remotes with URLs | git remote -v |
| git remote add <name> <url> | Add a new remote | git remote add upstream git@github.com:org/repo.git |
| git fetch | Download remote changes (don't merge) | git fetch origin |
| git pull | Fetch + merge | git pull origin main |
| git pull --rebase | Fetch + rebase (cleaner) | git pull --rebase origin main |
| git push | Upload local commits | git push origin main |
| git push -u origin <branch> | Push and set upstream tracking | git push -u origin feature/login |
| git push --force-with-lease | Force push safely (checks remote first) | git push --force-with-lease origin feature/login |
Fetch vs pull
git fetch downloads new commits from the remote but doesn’t touch your working directory. It’s always safe. git pull is git fetch + git merge (or git rebase if you pass --rebase). When in doubt, fetch first and inspect.
# Safe workflow
git fetch origin
git log HEAD..origin/main --oneline # See what's new
git merge origin/main # Merge when ready
Warning
git push --force overwrites the remote branch with your local version. If a teammate pushed changes you haven’t fetched, their work vanishes. Always use --force-with-lease instead. It fails if the remote has commits you haven’t seen.
Tracking branches
# See which local branches track which remotes
git branch -vv
# Set an existing branch to track a remote
git branch --set-upstream-to=origin/main main
How Do You Use Git Stash?
Stash temporarily shelves your changes so you can switch context. It’s a developer’s quick-save button. We’ve found it’s one of the most practical yet underused features, especially when you need to pull changes or switch branches mid-task.
# Stash everything (staged + unstaged)
git stash
# Stash with a descriptive message
git stash push -m "WIP: half-finished search bar"
# Stash including untracked files
git stash -u
# List all stashes
git stash list
# Apply the most recent stash (keep it in the stash list)
git stash apply
# Apply and remove from stash list
git stash pop
# Apply a specific stash
git stash apply stash@{2}
# Drop a specific stash
git stash drop stash@{0}
# Clear all stashes
git stash clear
Tip
Always use git stash push -m "description" instead of plain git stash. Future you will have no idea what stash@{3} contains without a message.
How Does Git Cherry-Pick Work?
Cherry-pick copies a single commit from one branch to another. It doesn’t move or merge anything. It creates a new commit with the same changes. This is useful when a bugfix lands on the wrong branch, or you need one specific change without merging an entire feature.
# Copy a single commit to your current branch
git cherry-pick abc1234
# Cherry-pick without committing (stage the changes instead)
git cherry-pick --no-commit abc1234
# Cherry-pick a range of commits
git cherry-pick abc1234..def5678
# Before cherry-pick (you're on main):
A---B---C feature
/
D---E---F main
# After cherry-picking commit B:
A---B---C feature
/
D---E---F---B' main (B' has the same changes as B)
When to cherry-pick
- Backporting a bugfix from
mainto a release branch. - Pulling one commit from a feature branch that isn’t ready to merge.
- Recovering a commit from a deleted branch (find it with
git reflog).
But don’t overuse it. If you’re cherry-picking multiple commits, a merge or rebase is usually cleaner.
How Do You Debug with Git Bisect?
Git bisect uses binary search to find the exact commit that introduced a bug. Instead of checking commits one by one, it cuts the search space in half each step. On a branch with 1,000 commits, bisect finds the culprit in roughly 10 steps instead of 500.
# Start bisecting
git bisect start
# Mark the current commit as bad (has the bug)
git bisect bad
# Mark a known good commit (before the bug existed)
git bisect good v2.0.0
# Git checks out a middle commit. Test it, then:
git bisect good # if this commit works fine
git bisect bad # if this commit has the bug
# Repeat until Git identifies the first bad commit
# When done:
git bisect reset # Return to your original branch
Automated bisect
If you have a test script that exits 0 for good and non-zero for bad, you can automate the entire process.
git bisect start
git bisect bad HEAD
git bisect good v2.0.0
git bisect run npm test
Git runs the test at each step automatically. In our experience, automated bisect has saved hours of debugging on large codebases where manual testing would be painful.
Tip
Write a small shell script that reproduces the bug and returns exit code 0 (good) or 1 (bad). Then git bisect run ./test-bug.sh does all the work. This is dramatically faster than manual bisecting.
What Is Git Reflog and How Does It Save You?
Reflog is Git’s hidden undo history. It records every time HEAD moves, even operations like reset --hard and rebase. If you’ve committed something and then “lost” it, reflog almost certainly still has it. Entries persist for 90 days by default.
# View reflog
git reflog
# Example output:
# abc1234 HEAD@{0}: reset: moving to HEAD~3
# def5678 HEAD@{1}: commit: add search feature
# 789abcd HEAD@{2}: commit: update homepage
# ...
# Recover a "lost" commit after a bad reset
git reset --hard HEAD@{1}
# Or create a branch from the lost commit
git branch recovery-branch HEAD@{1}
Common rescue scenarios
Accidentally ran git reset --hard:
git reflog # Find the commit before the reset
git reset --hard HEAD@{1} # Jump back to it
Deleted a branch with unmerged work:
git reflog # Find the last commit on that branch
git checkout -b recovered HEAD@{5} # Recreate it
Messed up a rebase:
git reflog # Find the commit before the rebase
git reset --hard HEAD@{4} # Restore pre-rebase state
Tip
Think of reflog as your local time machine. It only exists on your machine (it’s not pushed to remotes), and entries expire after 90 days. But within that window, almost nothing is truly lost.
What Are the Most Useful Git Aliases?
Aliases turn verbose commands into quick shortcuts. They go in your ~/.gitconfig file. In our experience, a good set of aliases can cut your daily typing by half without hiding what Git is actually doing.
# Add these aliases
git config --global alias.s "status --short"
git config --global alias.co "checkout"
git config --global alias.br "branch"
git config --global alias.cm "commit -m"
git config --global alias.lg "log --oneline --graph --decorate --all -20"
git config --global alias.undo "reset --soft HEAD~1"
git config --global alias.amend "commit --amend --no-edit"
git config --global alias.wip "stash push -m"
git config --global alias.last "log -1 HEAD --stat"
git config --global alias.unstage "restore --staged"
After setup, your workflow becomes:
git s # Quick status
git lg # Visual log
git cm "fix: typo" # Fast commit
git undo # Undo last commit (keep changes)
git amend # Add to last commit silently
Quick Reference: Git Commands by Category
Here’s every command from this guide in one scannable table, organized by workflow phase.
| Category | Command | One-Liner |
|---|---|---|
| Setup | git config --global user.name | Set your identity |
| Setup | git init | New repository |
| Setup | git clone <url> | Copy remote repo |
| Daily | git add -p | Stage interactively |
| Daily | git commit -m 'msg' | Save with message |
| Daily | git push / pull | Sync with remote |
| Branch | git checkout -b <name> | Create + switch |
| Branch | git switch -c <name> | Create + switch (modern) |
| Merge | git merge <branch> | Combine branches |
| Merge | git rebase <branch> | Rewrite onto branch |
| Undo | git revert <commit> | Safe public undo |
| Undo | git reset --soft HEAD~1 | Undo commit, keep staged |
| Remote | git fetch | Download without merge |
| Remote | git push --force-with-lease | Safe force push |
| Advanced | git stash push -m 'msg' | Shelve work temporarily |
| Advanced | git cherry-pick <commit> | Copy one commit |
| Advanced | git bisect start | Binary search for bugs |
| Advanced | git reflog | Local undo history |
Frequently Asked Questions
What’s the difference between git pull and git fetch?
git fetch downloads new commits from the remote without touching your working directory. It’s always safe. git pull runs git fetch followed by git merge (or git rebase with --rebase). Use fetch when you want to inspect changes before integrating them. Use pull when you trust the incoming changes.
How do I undo the last git commit?
Use git reset --soft HEAD~1 to undo the commit while keeping all changes staged. Use git reset --mixed HEAD~1 (the default) to undo and unstage. Use git commit --amend to edit the last commit message or add files. If the commit is already pushed, use git revert instead to avoid rewriting shared history.
Should I use merge or rebase?
It depends on your team’s convention. Merge preserves the full branch history and is safer for shared branches. Rebase creates a cleaner, linear history but rewrites commit hashes. A popular middle ground: rebase locally to clean up your feature branch, then merge into main with --no-ff. According to GitKraken’s 2023 report, 75% of teams use merge-based workflows (GitKraken, 2023).
How do I recover a deleted branch?
Use git reflog to find the last commit on the deleted branch, then git checkout -b branch-name HEAD@{n} to recreate it. Reflog entries persist for 90 days by default, so you have a generous window. This works even after git branch -D.
What does git reset —hard actually do?
It moves your branch pointer to the specified commit, updates the staging area to match, and overwrites your working directory. Any uncommitted changes are permanently destroyed. Committed work can still be recovered via git reflog, but unstaged modifications are gone for good. Always run git stash before reset --hard if you’re unsure.
Git is deep. You won’t memorize all of this, and that’s fine. The daily commands become muscle memory within a week. The advanced ones are here for when you need them. Bookmark this page and come back when things get weird.