BytePane

Git Cheat Sheet: Most Useful Commands Every Developer Needs

Git18 min read

According to the 2025 Stack Overflow Developer Survey — which collected responses from 49,000+ developers across 177 countries — Git is used by 93.87% of professional developers, up from 87.1% in 2016. It is the single most universally adopted tool in the industry. Yet most developers operate with a vocabulary of 8–12 commands and panic when they need anything beyond that.

This reference covers the commands you will actually reach for in daily work, organized by the workflow phase where they matter most. No padding, no obvious filler — just the commands, flags, and real-world context that make the difference.

Key Takeaways

  • 93.87% of developers use Git per the 2025 Stack Overflow Developer Survey — the highest adoption of any developer tool.
  • git reset vs git revert: reset rewrites history (local only); revert is safe on shared branches.
  • git stash push -m with a message beats unnamed stashes the moment you accumulate more than one.
  • Interactive rebase (git rebase -i) is the most powerful cleanup tool most developers never use.
  • git bisect can pinpoint the exact commit that introduced a bug in O(log n) steps without any manual binary search.

Initial Setup & Global Configuration

These commands run once per machine. Git stores configuration in three layers: --system (all users), --global (current user, stored in ~/.gitconfig), and --local (per-repo, stored in .git/config). Local overrides global, which overrides system.

# Identity — appears in every commit you create
git config --global user.name "Your Name"
git config --global user.email "[email protected]"

# Default branch name (Git 2.28+)
git config --global init.defaultBranch main

# Set VS Code as the default editor
git config --global core.editor "code --wait"

# Use rebase instead of merge on git pull
git config --global pull.rebase true

# Automatically set up remote tracking on push
git config --global push.autoSetupRemote true   # Git 2.37+

# View all active config values and where they come from
git config --list --show-origin

# View a specific value
git config user.email

push.autoSetupRemote true (introduced in Git 2.37, July 2022) eliminates the need to type --set-upstream origin branch-name on your first push from a new branch. It is one of the most underused config improvements in recent Git versions.

Starting and Cloning Repositories

# Initialize a new repository in the current directory
git init

# Initialize with a specific branch name
git init -b main

# Clone a remote repository
git clone https://github.com/org/repo.git

# Clone into a specific folder name
git clone https://github.com/org/repo.git my-folder

# Shallow clone — only the latest commit (faster for CI/CD)
git clone --depth 1 https://github.com/org/repo.git

# Clone a single branch
git clone --branch develop --single-branch https://github.com/org/repo.git

# Clone using SSH (requires SSH key setup)
git clone [email protected]:org/repo.git

Shallow clones (--depth 1) are standard practice in CI/CD pipelines where you only care about the latest code. According to GitHub's internal data, shallow clones can reduce clone time by 80–95% on large repositories. The trade-off is that history-dependent commands like git bisect and git log --follow will not work correctly.

Staging, Inspecting, and Committing Changes

Git's three-stage model — working directory, staging area (index), repository — is the concept most developers gloss over until it bites them. Understanding what git add actually does (writing to the index, not to any commit) is the key to using git reset and git checkout without surprises.

# Show working directory and staging area status
git status
git status -s        # Short format: M=modified, A=added, ?=untracked

# Stage a specific file
git add src/app.js

# Stage all changes in the current directory
git add .

# Stage only parts of a file (interactive hunk selection)
git add -p           # or --patch

# Unstage a file (keeps changes in working directory)
git restore --staged src/app.js   # Git 2.23+
# Older equivalent:
git reset HEAD src/app.js

# Show what is staged (what will go into the next commit)
git diff --staged    # or --cached

# Show unstaged changes (working dir vs index)
git diff

# Commit staged changes
git commit -m "feat: add user authentication"

# Stage all tracked files and commit in one step
git commit -am "fix: correct typo in error message"

# Amend the most recent commit (message or content)
# WARNING: only use before pushing — rewrites history
git commit --amend -m "fix: correct typo in error message"
git commit --amend --no-edit   # Keep same message, add staged changes

Use git add -p (patch mode) to stage only specific hunks within a file. This is how you make atomic commits — each commit does one thing — without having to artificially split your work into separate files. See our guide on Git workflow best practices for commit message conventions and PR review discipline.

Viewing History and Searching Commits

# Show commit history
git log
git log --oneline                        # Compact, one commit per line
git log --oneline --graph --all          # Visual branch graph
git log --oneline -10                    # Last 10 commits

# Show commits by a specific author
git log --author="Alice"

# Show commits that changed a specific file
git log --follow -- src/auth.js          # --follow tracks renames

# Show what changed in a specific commit
git show abc1234
git show HEAD                            # Most recent commit

# Search commit messages for a string
git log --grep="authentication"

# Search for when a line of code was introduced/removed
git log -S "function getUserById"        # Pickaxe search

# Show commits between two dates
git log --after="2026-01-01" --before="2026-03-01"

# Show who last modified each line of a file
git blame src/auth.js
git blame -L 10,30 src/auth.js          # Lines 10–30 only

The -S flag (pickaxe) is indispensable for debugging: it finds every commit where a specific string was added or removed from the codebase, regardless of which file. Combine it with --oneline -p to see both the commit and the diff in one pass.

Branching and Switching

# List all local branches (* = current branch)
git branch

# List all branches including remote-tracking
git branch -a

# Create a new branch
git branch feature/user-auth

# Create and switch to a new branch (preferred modern syntax)
git switch -c feature/user-auth     # Git 2.23+
# Older equivalent:
git checkout -b feature/user-auth

# Switch to an existing branch
git switch main
git checkout main    # Older equivalent

# Rename the current branch
git branch -m new-name

# Delete a merged local branch
git branch -d feature/user-auth

# Force-delete an unmerged branch (data loss risk!)
git branch -D feature/user-auth

# List branches already merged into current branch
git branch --merged

# List branches NOT yet merged
git branch --no-merged

The git switch command (Git 2.23+) was introduced specifically to separate branch operations from file restore operations, which git checkout awkwardly combined. Prefer git switch in scripts and documentation going forward. For team branching strategies, see our comparison of GitFlow, GitHub Flow, and trunk-based development.

Merging and Rebasing

# Merge a branch into the current branch
git merge feature/user-auth

# Merge without fast-forward (always creates a merge commit)
git merge --no-ff feature/user-auth

# Squash all branch commits into one staged changeset
git merge --squash feature/user-auth
git commit -m "feat: add user authentication"

# Abort an in-progress merge
git merge --abort

# Rebase current branch onto main
git rebase main

# Interactive rebase — edit last 4 commits
# pick = keep, reword = change message, squash = combine, drop = delete
git rebase -i HEAD~4

# Rebase onto a specific commit
git rebase --onto main feature-base feature-branch

# Abort a rebase in progress
git rebase --abort

# Continue after resolving a conflict
git rebase --continue

Interactive rebase (git rebase -i) is the most powerful history cleanup tool in Git and the one most developers avoid because it looks intimidating. Use it to squash WIP commits before opening a PR, split a large commit into focused pieces, or reorder commits so the history tells a coherent story. The full decision framework for when to use each strategy is in our rebase vs merge guide.

Working with Remotes

# List remotes (with URLs)
git remote -v

# Add a remote
git remote add origin https://github.com/org/repo.git

# Change a remote URL (e.g., switch from HTTPS to SSH)
git remote set-url origin [email protected]:org/repo.git

# Remove a remote
git remote remove upstream

# Fetch all remote changes without merging
git fetch origin
git fetch --all        # Fetch from all remotes
git fetch --prune      # Fetch and delete stale remote-tracking branches

# Push a branch to remote
git push origin feature/user-auth

# Push and set upstream tracking in one step
git push -u origin feature/user-auth

# Force push — DANGEROUS on shared branches
git push --force-with-lease   # Safer: fails if remote has new commits
git push --force              # Overwrites remote unconditionally

# Delete a remote branch
git push origin --delete feature/user-auth

# Pull (fetch + merge)
git pull origin main

# Pull with rebase instead of merge
git pull --rebase origin main

Always use --force-with-lease instead of --force when you must force-push. It checks whether the remote has been updated by someone else since your last fetch — if it has, the push is rejected, preventing you from silently overwriting a teammate's work. This single habit has prevented countless lost commits on engineering teams.

Undoing Changes: Reset, Revert, Restore, and Clean

This is where most Git horror stories originate. The key rule: never rewrite history that has already been pushed to a shared branch.

# Discard changes in working directory (dangerous — irreversible)
git restore src/app.js         # Git 2.23+
git checkout -- src/app.js     # Older equivalent

# Unstage changes (keeps changes in working directory)
git restore --staged src/app.js

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

# Undo last commit — keep changes unstaged (DEFAULT behavior)
git reset --mixed HEAD~1
git reset HEAD~1              # Same as --mixed

# Undo last commit — DISCARD all changes (irreversible!)
git reset --hard HEAD~1

# Safely undo a commit by creating a new reverting commit
# Safe on shared branches because history is preserved
git revert HEAD               # Revert the last commit
git revert abc1234            # Revert a specific commit
git revert HEAD~3..HEAD       # Revert a range of commits

# Remove untracked files from working directory
git clean -n                  # Dry run — show what would be deleted
git clean -fd                 # Actually delete untracked files and dirs
CommandRewrites History?Safe on Shared Branch?Keeps Changes?
git reset --softYesNoYes (staged)
git reset --mixedYesNoYes (unstaged)
git reset --hardYesNoNo (lost!)
git revertNo (adds commit)YesN/A (creates inverse)
git restoreNoYesNo (working dir only)

Stashing Work in Progress

# Save current working directory and staging area
git stash

# Stash with a descriptive name (highly recommended)
git stash push -m "WIP: refactor auth middleware"

# Include untracked files in the stash
git stash push -u -m "WIP: add new config files"

# List all stashes
git stash list
# Output:
# stash@{0}: On feature/auth: WIP: refactor auth middleware
# stash@{1}: On main: WIP: add new config files

# Apply the most recent stash (keeps it in stash list)
git stash apply

# Apply a specific stash
git stash apply stash@{1}

# Apply and remove the most recent stash from the list
git stash pop

# Show the diff of a stash without applying it
git stash show -p stash@{0}

# Delete a specific stash
git stash drop stash@{1}

# Delete all stashes
git stash clear

# Create a branch from a stash
git stash branch feature/new-branch stash@{0}

Always name your stashes with -m. The auto-generated name (WIP on feature/auth: abc1234 fix typo) becomes useless after you have accumulated 3+ stashes and cannot remember which is which.

Tagging Releases

# List all tags
git tag

# Create an annotated tag (recommended — includes tagger, date, message)
git tag -a v1.2.0 -m "Release v1.2.0: add user authentication"

# Create a lightweight tag (just a pointer, no metadata)
git tag v1.2.0

# Tag a specific commit
git tag -a v1.1.0 abc1234 -m "Backfill release tag"

# Show tag details and the tagged commit
git show v1.2.0

# Push a specific tag to remote
git push origin v1.2.0

# Push all local tags to remote
git push origin --tags

# Delete a local tag
git tag -d v1.2.0

# Delete a remote tag
git push origin --delete v1.2.0

Advanced Commands Worth Knowing

Cherry-pick: Apply Specific Commits

# Apply a single commit from another branch
git cherry-pick abc1234

# Cherry-pick a range of commits (exclusive..inclusive)
git cherry-pick abc1234..def5678

# Cherry-pick without auto-committing (stage only)
git cherry-pick -n abc1234

# Continue after resolving a conflict
git cherry-pick --continue

Bisect: Binary Search for a Bug

git bisect is criminally underused. Given a range of 1,000 commits, it finds the exact commit that introduced a regression in just 10 steps (log₂ 1000 ≈ 10). You can automate it entirely with a test script.

# Start a bisect session
git bisect start

# Mark the current (broken) commit as bad
git bisect bad

# Mark a known-good commit
git bisect good v1.0.0

# Git checks out the midpoint commit. Test it, then:
git bisect good    # If this commit is fine
git bisect bad     # If this commit is broken

# Git narrows the range and repeats until it finds the culprit
# To automate with a test script (exit 0 = good, exit 1 = bad):
git bisect run npm test -- --testPathPattern="auth.test.js"

# End the session and return to original HEAD
git bisect reset

Worktrees: Multiple Branches, One Clone

# Check out a branch into a separate directory
# without cloning the repo again
git worktree add ../hotfix-branch hotfix/critical-fix

# List all worktrees
git worktree list

# Remove a worktree
git worktree remove ../hotfix-branch

Worktrees are the clean solution to the classic "I'm in the middle of a feature and need to quickly patch prod" problem. Instead of stashing, switching branches, fixing, pushing, and un-stashing — you check out the hotfix branch into a separate folder and work there independently. Both branches share the same .git directory, so no disk duplication.

Reflog: Your Safety Net

# Show the reflog for HEAD (every position HEAD has pointed to)
git reflog

# Show reflog for a specific branch
git reflog show main

# Recover a commit after a bad reset --hard
# Find the commit SHA in reflog, then:
git reset --hard abc1234   # Return to that state
# Or create a new branch at the lost commit:
git branch recovery-branch abc1234

The reflog is local-only and expires after 90 days by default. If you accidentally git reset --hard past important commits, git reflog is usually your path back. It records every movement of HEAD, including resets, rebases, and merges.

.gitignore Patterns and Troubleshooting

# Common .gitignore patterns
node_modules/          # Ignore entire directory
*.log                  # Ignore all log files
.env                   # Ignore environment files
.DS_Store              # macOS metadata
dist/                  # Build output
*.local                # Local override files

# Negate a pattern (track this file even if its parent is ignored)
!dist/index.html

# Check why a file is being ignored
git check-ignore -v src/secrets.env

# Add a file that is already being tracked (already in git history)
# .gitignore only affects untracked files. To stop tracking:
git rm --cached src/secrets.env
echo "src/secrets.env" >> .gitignore
git commit -m "chore: stop tracking secrets file"

# Check what .gitignore rules apply globally (user-level)
git config --global core.excludesFile    # Typically ~/.gitignore_global

Quick Reference: Commands by Frequency of Use

FrequencyCommandsPurpose
Dailystatus, add, commit, push, pull, switchCore edit-commit-sync loop
Weeklyfetch, merge, rebase, log, diff, stashBranch integration and history
Monthlyrebase -i, cherry-pick, tag, remoteCleanup and release management
Emergencyreflog, bisect, reset --hard, worktreeRecovery and debugging

Git Aliases Worth Configuring

Git aliases save keystrokes on the commands you run dozens of times a day. Add these to your ~/.gitconfig under [alias]:

[alias]
  # Compact status
  st = status -s

  # Pretty log graph
  lg = log --oneline --graph --all --decorate

  # Last 10 commits with relative dates
  last = log -10 --pretty=format:"%C(yellow)%h%Creset %ad %C(bold)%s%Creset %C(red)%d%Creset" --date=relative

  # Undo last commit, keep changes staged
  undo = reset --soft HEAD~1

  # Amend without editing the message
  amend = commit --amend --no-edit

  # Delete all merged local branches (except main/master/develop)
  cleanup = !git branch --merged | grep -v '\*\|main\|master\|develop' | xargs git branch -d

  # Show files changed in last N commits
  changed = diff --name-only HEAD~1 HEAD

  # Stash including untracked files
  save = stash push -u

  # Find which branch contains a commit
  find-branch = branch -a --contains

The cleanup alias is particularly useful at the end of a sprint. It deletes every local branch that has already been merged, leaving only the active branches. Run it after merging a batch of PRs to keep your branch list clean. Review CI/CD integration with Git in our CI/CD pipeline guide.

Frequently Asked Questions

What is the difference between git fetch and git pull?

git fetch downloads remote changes into your local remote-tracking branches (origin/main) without touching your working branch. git pull is effectively git fetch followed by git merge (or rebase with --rebase). Use fetch when you want to inspect remote changes before integrating them. Use pull when you trust the remote and want to update immediately.

How do I undo the last git commit without losing changes?

Run git reset --soft HEAD~1. This moves the HEAD pointer back one commit but leaves all changes staged in your index. If you want to also unstage the changes (but keep them in your working directory), use git reset --mixed HEAD~1. Never use git reset --hard unless you are sure you want to permanently discard the changes.

What does git stash do and when should I use it?

git stash temporarily shelves your uncommitted changes so you can switch branches or pull updates without committing half-finished work. Run git stash push -m "description" to save with a name, git stash pop to restore. Stashes are local-only — they are not pushed to remotes.

How do I delete a remote branch in Git?

Run git push origin --delete branch-name. To also remove the local tracking reference, run git fetch --prune afterward. On GitHub and GitLab, branches are automatically offered for deletion after a pull request is merged.

What is the difference between git reset and git revert?

git reset rewrites history by moving the HEAD pointer backward — safe only on local commits that have not been pushed. git revert creates a new commit that undoes a previous commit, preserving the full history. Always use git revert on shared branches to avoid disrupting collaborators who have already pulled the commits you want to undo.

How do I see which branches have been merged?

Run git branch --merged to list local branches already merged into the current branch. Use git branch -r --merged to check remote branches. To list branches not yet merged, run git branch --no-merged. Useful for identifying stale branches that are safe to delete after a sprint or release cycle.

How do I configure Git to use a specific SSH key for a repository?

Edit ~/.ssh/config and add a Host block that maps an alias to your key. Then set the remote URL to use the alias: git remote set-url origin git@github-work:org/repo.git. This lets you use different SSH keys for different GitHub accounts. See our SSH keys guide for the full setup walkthrough.

Compare File Differences in Your Browser

Need to inspect what changed between two versions of a file without pulling up a terminal? BytePane's Diff Checker gives you a side-by-side diff with syntax highlighting, inline change markers, and copy-paste convenience — no install required.

Open Diff Checker

Related Articles