Git Workflow Reference
Everything you need to keep your history clean, your team happy, and your production server breathing.
Git is the version-control backbone of every professional project. This reference covers the commands and workflows you will reach for every single day — from a quick status check to untangling a bad commit that slipped into production. Bookmark it; come back as often as you need.
The Everyday Loop
git status # See what's changed, staged, or untracked
git diff # Unstaged changes (line-by-line)
git diff --staged # What's already staged and ready to commit
git add -p # Stage changes interactively, hunk by hunk (best habit)
git add <file> # Stage a specific file
git commit -m "feat: add login form validation"
git push origin main # Push to remote
git pull --rebase origin main # Pull + replay your local commits on top (avoids noise merges)
Why
add -p? It forces you to review every line you're committing, catching debug logs and secrets before they're immortalized.
Branching & the Feature-Branch Workflow
git branch # List local branches
git branch -a # List all branches (local + remote)
git checkout -b feature/my-thing # Create and switch to a new branch
# shorthand for: git branch feature/my-thing && git checkout feature/my-thing
git push -u origin feature/my-thing # Push branch and set upstream tracking
git checkout main # Switch back to main
git branch -d feature/my-thing # Delete branch after merging
git push origin --delete feature/my-thing # Delete the remote branch too
Workflow:
- Always branch off
main(ordevelopif your team uses it). - One feature = one branch = one pull request.
- Keep branches short-lived — days, not weeks.
Merge vs Rebase
Merge — preserves history, creates a merge commit
git checkout main
git merge feature/my-thing # Fast-forward if possible, else merge commit
git merge --no-ff feature/my-thing # Force a merge commit even if FF is possible
Rebase — rewrites history, linear timeline
git checkout feature/my-thing
git rebase main # Replay your branch commits on top of main
git push --force-with-lease # Only safe after rebasing a private branch
The Golden Rule
Never rebase a branch that has already been pushed and shared with others. Rebasing rewrites commit hashes. If teammates have pulled your branch, you will cause chaos.
Rule of thumb: rebase your own private feature branches before opening a PR; merge when integrating into shared branches.
Resolving Merge Conflicts
When Git can't auto-merge, it pauses and marks the file:
<<<<<<< HEAD
const port = 3000;
=======
const port = 8080;
>>>>>>> feature/change-port
<<<<<<< HEAD— your current branch's version=======— divider between the two versions>>>>>>> feature/change-port— the incoming branch's version
Steps to resolve:
# 1. Open the conflicted file, decide which version (or blend both) is correct
# 2. Delete ALL three marker lines (<<<, ===, >>>)
# 3. Save the file, then:
git add <conflicted-file>
git commit # Git pre-fills the merge commit message
# If you want to abort and start over:
git merge --abort
git rebase --abort # During a rebase conflict
Undo Toolbox
| Command | What it undoes | Safe? |
|---|---|---|
git restore <file> | Discard unstaged changes in a file | Safe (working tree only) |
git restore --staged <file> | Unstage a file (keep changes) | Safe |
git revert <hash> | Create a new commit that undoes a commit | Safe — history preserved |
git reset --soft HEAD~1 | Undo last commit, keep changes staged | Safe locally |
git reset --mixed HEAD~1 | Undo last commit, keep changes unstaged | Safe locally |
git reset --hard HEAD~1 | Undo last commit, discard all changes | Destructive |
git restore src/index.js # Discard unstaged edits
git restore --staged src/index.js # Move back out of staging area
git revert abc1234 # Safe undo (adds a reverting commit)
git reset --soft HEAD~1 # "Oops, wrong message" — recommit cleanly
git reset --mixed HEAD~1 # Undo commit and unstage, but keep edits
git reset --hard HEAD~1 # Nuclear: commit gone, edits gone — no recovery
Rule: use
reverton anything already pushed. Useresetonly on commits that never left your machine.
Finding a Bad Commit with git bisect
Binary-search through history to find the commit that introduced a bug:
git bisect start
git bisect bad # Current commit is broken
git bisect good v1.4.0 # Last known good commit (tag or hash)
# Git checks out a middle commit — test it, then tell Git:
git bisect good # This one is fine
git bisect bad # This one is broken
# Repeat until Git prints: "abc1234 is the first bad commit"
git bisect reset # Return to HEAD when done
Stash & Cherry-Pick
# Stash — temporarily shelve work-in-progress
git stash # Save dirty working tree
git stash pop # Restore most recent stash
git stash list # See all stashes
git stash drop stash@{0} # Delete a specific stash
# Cherry-pick — apply a single commit from another branch
git cherry-pick abc1234 # Apply that commit to the current branch
git cherry-pick abc1234 --no-commit # Apply changes without committing yet
Good Commit Messages
Use Conventional Commits style — machines and humans both love it:
<type>(<scope>): <short description>
[optional body explaining WHY, not WHAT]
[optional footer: BREAKING CHANGE, Closes #42]
Types: feat · fix · docs · style · refactor · test · chore · ci
# Good examples
git commit -m "feat(auth): add OAuth2 Google login"
git commit -m "fix(cart): prevent negative quantities on decrement"
git commit -m "refactor(api): extract pagination helper into shared util"
git commit -m "docs: update README with local setup steps"
# Bad examples (don't do this)
git commit -m "fix stuff"
git commit -m "WIP"
git commit -m "asdfgh"
Atomic commits: one logical change per commit. If you can't summarize it in one line, it's probably two commits.
.gitignore Essentials
# Dependencies
node_modules/
vendor/
# Build output
dist/
build/
.next/
out/
# Environment & secrets
.env
.env.local
.env.*.local
*.pem
*.key
# OS clutter
.DS_Store
Thumbs.db
# Editor files
.vscode/settings.json
.idea/
*.swp
git check-ignore -v <file> # Debug: why is this file being ignored?
git rm --cached <file> # Stop tracking a file already committed (keeps local copy)
If You Accidentally Committed a Secret
# Immediately rotate the secret (change the password/token NOW — assume it's compromised)
# Remove it from history with git-filter-repo (preferred over filter-branch):
pip install git-filter-repo
git filter-repo --path .env --invert-paths
# Force-push all branches (coordinate with your team first):
git push origin --force --all
# Then add the file to .gitignore so it never happens again
echo ".env" >> .gitignore
git commit -am "chore: add .env to gitignore"
Never commit
node_modules, build artifacts,.envfiles, API keys, passwords, or certificates. Ever.
Build It Right, Or Don't Build It At All. 🏛️