DEV Community

Alex Chen
Alex Chen

Posted on

The Git Workflow That Saved My Sanity

The Git Workflow That Saved My Sanity

After years of trial and error, this is the workflow I use for every project.

The Problem

❌ Bad workflow:
- Everything on main branch
- Commit messages like "fix", "update", "stuff"
- No code review, just push to production
- Merge conflicts every day
- "Which version has the bug fix?" — nobody knows

✅ Good workflow:
- Feature branches with clear naming
- Descriptive commit messages
- PRs with code review before merge
- Clean git history that tells a story
Enter fullscreen mode Exit fullscreen mode

Branch Naming Convention

# Format: type/ticket-id-or-description
feature/user-authentication          # New feature
fix/login-redirect-bug              # Bug fix
hotfix/payment-expiry               # Production hotfix
refactor/user-service                # Code refactor (no behavior change)
docs/api-endpoints                  # Documentation only
test/payment-flow                   # Tests only
chore/update-dependencies           # Maintenance tasks
release/v1.2.0                      # Version release
Enter fullscreen mode Exit fullscreen mode

Commit Message Convention (Conventional Commits)

type(scope): subject

body (optional)

footer (optional)
Enter fullscreen mode Exit fullscreen mode

Types

Type When to Use
feat New feature
fix Bug fix
docs Documentation changes
style Formatting, semicolons, etc. (no logic change)
refactor Code restructuring (no behavior change)
perf Performance improvement
test Adding or updating tests
chore Build process, dependencies, tooling
ci CI/CD configuration
revert Revert previous commit

Examples

git commit -m "feat(auth): add OAuth2 Google login"
git commit -m "fix(cart): resolve race condition in add-to-cart"
git commit -m "perf(api): cache user profile queries (3x faster)"
git commit -m "docs(readme): update installation instructions for Node 22"
git commit -m "refactor(user): extract validation into separate module"
git commit -m "test(payment): add unit tests for Stripe webhook handler"
git commit -m "chore(deps): upgrade express from 4.18 to 4.21"
git commit -m "ci(github): add Node.js 22 to test matrix"

# Breaking change (!)
git commit -m "feat(api)!: remove deprecated /v1 endpoints"

# Scope can be omitted if not applicable
git commit -m "chore: update .gitignore"
Enter fullscreen mode Exit fullscreen mode

My Daily Workflow

# 1. Start new work
git checkout main
git pull origin main
git checkout -b feature/search-filters

# ... write code ...

# 2. Stage and commit (review your changes first!)
git diff                    # See what changed
git status                 # See what's staged/unstaged
git add -p                  # Interactive staging (review each hunk!)
git commit -m "feat(search): add category and price filters"

# ... write more code ...

# 3. Keep commits atomic (one logical change per commit)
git add src/components/SearchFilters.tsx
git commit -m "feat(search): add SearchFilters component"

git add src/api/search.ts
git commit -m "feat(search): implement filter API endpoint"

# 4. Sync with main before pushing
git fetch origin
git rebase origin/main       # Not merge! Keeps history clean

# 5. Push and create PR
git push -u origin feature/search-filters
gh pr create --title "Add search filters" --body "## Summary\n..."

# 6. After review + merge, clean up
git checkout main
git pull origin main
git branch -d feature/search-filters  # Delete local branch
git push origin --delete feature/search-filters  # Delete remote branch
Enter fullscreen mode Exit fullscreen mode

The Golden Rules

Rule 1: Atomic Commits

# ❌ One giant commit with everything
git add .
git commit -m "did some stuff"

# ✅ Small, focused commits
git add auth/login.ts
git commit -m "feat(auth): implement login form validation"

git add auth/session.ts
git commit -m "feat(auth): add session management with JWT"

git add tests/auth.test.ts
git commit -m "test(auth): add login and session tests"
Enter fullscreen mode Exit fullscreen mode

Why? Easy to revert one change without undoing others. Easy to bisect bugs.

Rule 2: Write Good Messages

# ❌ Useless messages
"updates"
"fix bug"
"wip"
"asdf"
"minor changes"

# ✅ Informative messages
"fix(auth): handle expired token gracefully by redirecting to login"
"feat(dashboard): show revenue chart for last 30 days"
"refactor(api): extract pagination logic into reusable utility"
Enter fullscreen mode Exit fullscreen mode

Why? Your future self (and teammates) will thank you when debugging.

Rule 3: Don't Break Main

# ❌ Push directly to main
git checkout main
git commit -m "hotfix" 
git push origin main        # DANGEROUS!

# ✅ Always use branches + PRs
git checkout -b fix/critical-bug
# ... fix ...
git push origin fix/critical-bug
# Create PR → get review → merge to main
Enter fullscreen mode Exit fullscreen mode

Rule 4: Clean Up Branches

# List merged branches (safe to delete)
git branch --merged main

# Delete merged local branches
git branch --merged main | grep -v '^\*\|main' | xargs git branch -d

# Force delete unmerged branch (use carefully)
git branch -D abandoned-feature
Enter fullscreen mode Exit fullscreen mode

Handling Common Situations

"I committed to the wrong branch"

# Undo last commit (keep changes staged)
git reset --soft HEAD~1
git stash
git checkout correct-branch
git stash pop
Enter fullscreen mode Exit fullscreen mode

"I need to split a commit"

# Interactive rebase of last 3 commits
git rebase -i HEAD~3
# Change 'pick' to 'edit' on the commit you want to split
# Then:
git reset HEAD~1            # Unstage but keep changes
git add file1.ts             # Stage part 1
git commit -m "part 1"
git add file2.ts             # Stage part 2
git commit -m "part 2"
git rebase --continue
Enter fullscreen mode Exit fullscreen mode

"Merge conflict hell"

# Don't panic! Follow these steps:
# 1. Understand what changed
git diff --name-only         # Which files have conflicts?
git diff file.ts             # See the actual conflict markers

# 2. Resolve each conflict
# Open file → find <<<<<<< ======= >>>>>>> markers
# Choose ours, theirs, or manual edit

# 3. Mark as resolved
git add file.ts              # Stage resolved files
git rebase --continue        # Continue rebasing

# 4. If stuck, abort and try again
git rebase --abort           # Go back to original state
Enter fullscreen mode Exit fullscreen mode

"I need to update my PR after review"

# Make changes
git add .
git commit -m "fix(review): address feedback on input validation"

# Update the PR (amend last commit or squash new commits)
git rebase -i origin/main     # Squash fix commits into original
git push --force-with-lease   # Update PR (safe force-push!)
Enter fullscreen mode Exit fullscreen mode

Git Aliases I Can't Live Without

[alias]
    co = checkout
    br = branch
    ci = commit
    st = status -sb
    lg = log --oneline --graph --all --decorate
    unstage = reset HEAD --
    amend = commit --amend --no-edit
    recent = log --oneline -10
    save = stash push -u -m
    pop = stash pop
    prune = !git branch --merged main | grep -v '\\*\\|main' | xargs git branch -d
    wip = !git add -A && git commit -m \"WIP: $(date +%s)\"
    done = !git checkout main && git pull && git prune
Enter fullscreen mode Exit fullscreen mode

What's your git workflow? Any tips I'm missing?

Follow @armorbreak for more developer content.

Top comments (0)