Git 5 🧩 Git Best Practices 🧩 Troubleshooting
Good Git habits aren’t about following rules for their own sake — they’re about making history readable, reviewable, and reversible. A clean commit log tells a story. A messy one forces everyone to read diffs. This chapter covers the habits that separate a Git user from a Git practitioner.
Key point: The goal of good Git practice is clear history. If someone reads your log six months from now, they should understand what happened and why — without opening a single diff.
Writing good commit messages
A commit message is the single most-read piece of documentation in a repository. It’s also the one most often written carelessly.
The structure:
- Subject line — 50 characters max, imperative mood, no period
- Blank line separating subject from body
- Body — wrapped at 72 characters, explaining what and why
Fix login validation error
The previous implementation failed to validate email format properly,
causing authentication failures for valid users. This commit adds
proper regex validation for email addresses.
What makes a good subject line:
| Rule | Example |
|---|---|
| Imperative mood | Add login form (not “Added” or “Adds”) |
| Under 50 characters | Fix typo in API docs |
| No period | Fix typo in API docs (not Fix typo in API docs.) |
| Specific | Fix null pointer in user lookup |
Why imperative mood: Git itself uses imperative in auto-generated messages (“Merge branch…”, “Revert…”). Consistency matters.
Why 50/72: Traditional terminal width. Short subject lines fit in one line; wrapped bodies read cleanly.
What goes in the body:
- What changed — briefly
- Why it changed — the important part
- Context — links to issues, references to discussions
Add rate limiting to public API
Users were hitting the endpoints too frequently, causing
degraded performance for everyone. This adds a token bucket
limiter at 100 req/min per IP.
Closes #452
Why this matters: The subject answers “what happened?” The body answers “why?”. If only one is present, half the story is missing.
Atomic commits
An atomic commit contains exactly one logical change. Not two. Not a mix of features and fixes.
The rule: Each commit should be independently meaningful and reversible. If you reverted it, exactly one thing would change.
Good:
Fix typo in README
Add user login endpoint
Refactor auth service for testability
Update Node version to 20
Bad:
Fix typo, add login, update Node
Misc changes
WIP
stuff
The bad ones mix unrelated changes, making review and revert painful.
Why atomic commits matter:
- Reviewable — a reviewer sees one concern, not five
- Revertable — if the login change broke something, only that commit needs reverting
- Bisectable —
git bisectworks only when each commit is a single logical unit - Clean history — the log reads as a sequence of decisions, not a soup
How to keep commits atomic:
- Commit early and often — small steps, not one big dump
- Use
git add -pto stage only specific hunks - Split a large change into multiple commits before pushing
# Stage only some hunks of a file
git add -p file.js
# Choose which hunks to include
# y = yes, n = no, s = split, q = quit
Why this discipline pays off: A clean history is easier to bisect, review, and revert. A tangled one makes every future change harder.
Regular pushing and pulling
The more often you sync, the smaller the pain.
Push frequently. Local work is fragile — hardware fails, disks corrupt. Pushing to a remote is a backup. It’s also how your team sees what you’re doing.
Pull frequently. The longer you go without pulling, the more your branch diverges. When you finally pull, you get a large merge with many conflicts.
Sync at the start of every session:
git checkout main
git pull
git checkout feature/your-branch
git rebase main
Or on a shared branch:
git pull --rebase
Why --rebase on pull: It replays your local commits on top of the remote’s, keeping a linear history. Without it, git pull creates a merge commit every time.
Sync before pushing:
git fetch origin
git status # are you behind?
git pull --rebase # if so
git push
Why small, frequent syncs beat big, rare ones: A merge of 5 commits is easy to resolve. A merge of 500 commits is a week of work. Frequent pulling keeps divergence small.
Additional best practices
Use branches for everything. Never commit directly to main. Every feature, every fix, every experiment gets a branch.
Review your own changes before committing.
git diff --staged
See what you’re about to commit. Typos and stray debug lines get caught here.
Keep branches short-lived. A branch that lives for weeks will conflict with everything. Merge or rebase within days.
Write branch names that say what they do.
feature/user-login
fix/null-pointer-check
hotfix/security-patch
Never commit secrets. Use .gitignore, environment files, or a secrets manager. If you do commit one, rotate the credential immediately — Git history is forever.
Don’t commit generated files. Build output, node_modules/, compiled assets — they belong in .gitignore, not the repo.
Keep .gitignore current. When you add a tool, add its artifacts to ignore.
Use tags for releases. v1.0.0, v1.0.1 — not “released” or “final”.
Prefer git switch and git restore over git checkout. Clearer intent, fewer surprises.
Why these habits compound: Each one is small. Together they make a repository that’s easy to work in, easy to onboard to, and hard to break.
Complete Example Session
# ============================================
# PART 1: WRITE A GOOD COMMIT
# ============================================
git add auth.js
git commit
# (opens editor)
# Editor content:
# Fix null pointer in user lookup
#
# The user lookup assumed a session always exists. When
# the session expires mid-request, the code crashed.
# This adds a null check and returns a 401 instead.
# ============================================
# PART 2: ATOMIC COMMITS
# ============================================
# Instead of one big commit:
git add .
git commit -m "Add login, fix bug, update docs" # ❌
# Split into focused commits:
git add auth.js
git commit -m "Add login endpoint"
git add auth.test.js
git commit -m "Add login endpoint tests"
git add README.md
git commit -m "Document login endpoint"
# ============================================
# PART 3: STAGE HUNKS SELECTIVELY
# ============================================
git add -p file.js
# [ y/n/s/q prompts for each hunk ]
# ============================================
# PART 4: SYNC BEFORE WORKING
# ============================================
git checkout main
git pull
git checkout feature/login
git rebase main
# ============================================
# PART 5: REVIEW BEFORE COMMITTING
# ============================================
git diff --staged
# (see exactly what will be committed)
# ============================================
# PART 6: PUSH FREQUENTLY
# ============================================
git push origin feature/login
# [ Pushed to remote ]
# ============================================
# PART 7: PULL WITH REBASE
# ============================================
git pull --rebase origin main
# ============================================
# PART 8: SHORT-LIVED BRANCH
# ============================================
git checkout -b fix/typo
# (edit, commit, push, PR)
# (merge within hours, not weeks)
Quick Reference
Commit Message Structure
| Part | Length | Purpose |
|---|---|---|
| Subject | 50 | What changed |
| Blank line | 1 | Separator |
| Body | 72 per line | Why it changed |
Atomic Commits
| Good | Bad |
|---|---|
| One logical change | Mixed changes |
| Revertible | Intertwined |
| Bisectable | Ambiguous |
| Clear subject | Vague message |
Sync Commands
| Command | Purpose |
|---|---|
git pull | Fetch + merge |
git pull --rebase | Fetch + rebase |
git fetch | Fetch only |
git push | Push to remote |
git push -u origin NAME | Push + set upstream |
Before Committing
| Command | Purpose |
|---|---|
git status | See what changed |
git diff | See unstaged changes |
git diff --staged | See staged changes |
git add -p | Stage hunks |
Branch Naming
| Prefix | Purpose |
|---|---|
feature/ | New feature |
fix/ | Bug fix |
hotfix/ | Urgent fix |
experiment/ | Experimental |
release/ | Release prep |
Sync Frequency
| Situation | Cadence |
|---|---|
| Push | After every commit |
| Pull | Start of every session |
| Rebase | Before pushing |
| Merge branch | After review |
Never Commit
| Category | Examples |
|---|---|
| Secrets | .env, API keys |
| Generated | dist/, build/ |
| Dependencies | node_modules/ |
| Editor | .vscode/, .idea/ |
| OS | .DS_Store, Thumbs.db |
Best Practices
✅ Do This:
# Write imperative subject lines
git commit -m "Fix login validation" # ✅
# Use a body for non-obvious changes
git commit # ✅ (editor opens)
# Commit one logical change at a time
git add auth.js && git commit -m "Add auth" # ✅
# Stage hunks selectively
git add -p file.js # ✅
# Sync before starting work
git pull --rebase # ✅
# Review before committing
git diff --staged # ✅
# Keep branches short-lived
# Merge within days, not weeks # ✅
# Use descriptive branch names
git checkout -b fix/user-lookup-null # ✅
❌ Don’t Do This:
# Don't write vague commit messages
git commit -m "fix" # ❌
# Don't mix unrelated changes
git commit -m "Add login, fix typo, update deps" # ❌
# Don't commit directly to main
git checkout main && git commit # ❌
# Don't accumulate long-lived branches
# Weeks of divergence create huge conflicts # ❌
# Don't commit secrets
git add .env # ❌
# Don't commit generated files
git add dist/ # ❌
# Don't forget to push
# Local-only work isn't backed up # ⚠️
# Don't force-push shared branches
git push --force # ❌
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| Vague subject | History is unreadable | Be specific |
| No body | Context lost | Explain why |
| Mixed commits | Hard to review | Use add -p |
| Long-lived branch | Conflicts pile up | Merge quickly |
| No sync before work | Divergence | git pull first |
| Forgot to push | Work not backed up | Push often |
| Committed secret | Security leak | Rotate + untrack |
--force on shared | History rewritten | --force-with-lease |
Real-World Examples
1. Good commit message
Add password reset endpoint
Users couldn't recover accounts without contacting support.
This adds a token-based reset flow with email delivery.
2. Atomic commits
git commit -m "Add user model"
git commit -m "Add user repository"
git commit -m "Add user service"
3. Split a commit
git reset HEAD~1
git add file1.js
git commit -m "First change"
git add file2.js
git commit -m "Second change"
4. Stage hunks
git add -p
5. Sync session start
git checkout main
git pull
6. Pull with rebase
git pull --rebase origin main
7. Review before commit
git diff --staged
8. Push often
git push origin feature/login
9. Short-lived branch
git checkout -b fix/typo
# commit, push, PR, merge — all within hours
10. Delete merged branch
git branch -d fix/typo
11. Descriptive branch names
git checkout -b feature/user-authentication
12. Keep .gitignore current
echo "coverage/" >> .gitignore
13. Tag releases
git tag -a v1.0.0 -m "Release 1.0.0"
14. Switch over checkout
git switch main
15. Restore instead of reset
git restore file.js
Visual: The Commit Message
┌──────────────────────────────────────────────┐
│ Fix login validation error │ ← 50 chars max
│ ───────────────────────── │ ← blank line
│ The previous implementation failed to │ ← 72 chars
│ validate email format properly, causing │ per line
│ authentication failures for valid users. │
│ This commit adds proper regex validation. │
│ │
│ Closes #452 │
│ │
└──────────────────────────────────────────────┘
Visual: Atomic vs Mixed Commits
┌──────────────────────────────────────────────┐
│ Mixed (bad) │
│ │
│ ● Add login, fix typo, update deps │
│ │
│ Hard to review, hard to revert │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Atomic (good) │
│ │
│ ● Add login endpoint │
│ ● Fix typo in README │
│ ● Update Node to 20 │
│ │
│ Each commit is one change │
│ Each commit is revertible │
│ │
└──────────────────────────────────────────────┘
Visual: Sync Frequency
┌──────────────────────────────────────────────┐
│ Frequent syncs │
│ │
│ ○───○───○───○───○ │
│ small merges, small conflicts │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Rare syncs │
│ │
│ ○───────────○ │
│ big merge, big conflicts │
│ │
└──────────────────────────────────────────────┘
Visual: Before Every Push
┌──────────────────────────────────────────────┐
│ 1. git status │
│ 2. git diff --staged │
│ 3. git fetch origin │
│ 4. git pull --rebase (if behind) │
│ 5. git push │
│ │
└──────────────────────────────────────────────┘
Summary
| Practice | Why |
|---|---|
| Clear commit messages | Readable history |
| Atomic commits | Revertible, bisectable |
| Push often | Backup, visibility |
| Pull often | Small conflicts |
| Short-lived branches | Easy merges |
| Descriptive names | Self-documenting |
| Never commit secrets | Security |
| Use .gitignore | Clean repo |
| Tag releases | Traceable versions |
Key takeaways:
- Commit messages have a subject (50 chars), blank line, and body (72 chars)
- Write in imperative mood — “Fix” not “Fixed”
- Atomic commits contain one logical change each
- Use
git add -pto stage specific hunks - Push after every commit — local work isn’t backed up
- Pull at the start of every session — small syncs prevent big conflicts
- Use
git pull --rebasefor linear history - Review staged changes with
git diff --stagedbefore committing - Keep branches short-lived — merge within days
- Never commit secrets, generated files, or dependencies
- Use descriptive branch names with prefixes
- Never force-push shared branches
- Tag releases with semantic versions
Remember: Good Git habits are about making history readable, changes reviewable, and mistakes reversible. Write clear messages, commit atomically, sync often, and keep branches short. The habits feel small in the moment but compound over time — a repository with good history is easier to work in for years.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!