Git Commands Every Developer Needs to Know (With Examples)

The git commands you'll actually use — not a 200-command reference. Covers daily workflow, branching, merge conflicts, and recovering from mistakes.

Why Git Feels Hard (And Why It's Simpler Than It Looks)

Git has a reputation for being confusing. This reputation is partly deserved — the mental model is counterintuitive, the error messages are cryptic, and the commands sometimes do surprising things. But 90% of daily git usage involves about 15 commands. The confusion usually comes from not understanding the three states that files can be in: working directory (changed but not staged), staging area (staged but not committed), and committed. Once that mental model clicks, most git operations make sense.

The Daily Workflow

Here's what a normal development day looks like in git:

# Start of day: get latest changes
git pull origin main

# Create a branch for your feature
git checkout -b feature/user-authentication

# Work on code... then:
git status                        # see what you changed
git diff                          # see the actual changes
git add src/auth/login.js         # stage specific files
git add .                         # stage everything
git commit -m 'add login endpoint with JWT auth'

# Push to remote and create PR
git push origin feature/user-authentication

# After PR is merged, clean up
git checkout main
git pull origin main
git branch -d feature/user-authentication

Branching Strategy That Actually Works

The most practical branching approach for small-to-medium projects: main is always deployable, never commit directly to it. Create a branch for every feature or bug fix, named descriptively (feature/auth, fix/login-redirect, chore/upgrade-deps). When your branch is ready, open a pull request to main. After it's reviewed and merged, delete the branch. This creates a clean, reviewable history and prevents the chaos of everyone committing directly to main.

# Naming conventions that read clearly in git log:
git checkout -b feature/user-profile       # new feature
git checkout -b fix/password-reset-email   # bug fix
git checkout -b chore/upgrade-to-react-19  # dependency updates
git checkout -b docs/update-readme         # documentation

# See all branches:
git branch -a

# Delete merged local branch:
git branch -d feature/user-profile

# Force delete unmerged branch (be careful):
git branch -D feature/abandoned-experiment

Handling Merge Conflicts Without Panicking

Merge conflicts happen when two branches change the same lines. They feel scary but they're just git asking you to decide which version to keep. The markers tell you exactly what happened:

# Conflict markers in your file:
<<<<<<< HEAD
// Your version of the code
const apiUrl = process.env.API_URL || 'http://localhost:3000'
=======
// The incoming branch's version
const apiUrl = process.env.API_URL ?? 'https://api.production.com'
>>>>>>> feature/production-config

# How to resolve:
# 1. Decide which version (or combine them)
# 2. Delete the conflict markers
# 3. Stage the resolved file
git add src/config.js
git commit -m 'resolve merge conflict in config'

# Or abort if you want to start over:
git merge --abort

Recovery Commands for When You Mess Up

These are the commands you'll look up at 2am in a panic. Know them before you need them.

# Undo the last commit (but keep the changes):
git reset --soft HEAD~1

# Undo the last commit (and discard the changes — careful!):
git reset --hard HEAD~1

# Unstage a file you accidentally added:
git restore --staged src/config.js

# Discard changes to a file (revert to last commit):
git restore src/config.js

# See what you just undid:
git reflog  # git keeps a log of everything — you can usually recover

# Stash work-in-progress before switching branches:
git stash
git stash pop  # bring it back