The Git Workflow That Saved My Sanity as a Solo Developer
I used to dread git conflicts and messy commit histories. This workflow fixed everything.
The Problem
My old git history:
a1b2c3d fix
e4f5g6h fix again
i7j8k9l wip
m1n2o3p wip fix
q4r5s6t merge branch 'main' of ...
u7v8w9x conflict resolution
y0z1a2b another fix
❌ 50+ commits for a 2-day feature
❌ "fix" and "wip" everywhere
❌ No idea which commit does what
❌ Scary to push because of mess
The Solution: My Current Workflow
Branch Naming Convention
# Format: type/ticket-description
# Types: feat/ fix/ refactor/ docs/ chore/ hotfix/
git checkout -b feat/auth-login-page
git checkout -b fix/api-timeout-error
git checkout -b refactor/user-service-cleanup
git checkout -b docs/readme-installation
git checkout -b hotfix/security-patch-v1.0.1
Commit Message Convention (Conventional Commits)
# Format: type(scope): subject
# body (optional)
# footer (optional)
git commit -m "feat(auth): add OAuth2 Google login"
git commit -m "fix(api): handle timeout errors gracefully"
git commit -m "refactor(db): replace raw queries with ORM"
git commit -m "docs(readme): add installation instructions"
Types I use:
| Type | When |
|---|---|
feat |
New feature |
fix |
Bug fix |
refactor |
Code restructuring (no behavior change) |
docs |
Documentation only |
style |
Formatting, semicolons, etc. (no code change) |
perf |
Performance improvement |
test |
Adding or updating tests |
chore |
Build process, dependencies, config |
ci |
CI/CD changes |
The Daily Development Loop
Start of day:
git pull origin main # Get latest
git checkout -b feat/my-feature # New branch
During work:
# Make changes
git add .
git commit -m "feat(feature): description of progress"
# Need to share with teammate?
git push origin feat/my-feature
Ready for review:
# Rebase on latest main first!
git fetch origin
git rebase origin/main # Clean history
# Push (force needed after rebase)
git push --force-with-lease origin feat/my-feature
# Create PR on GitHub/GitLab
After review + merge:
git checkout main
git pull origin main
git branch -d feat/my-feature # Clean up local branch
The Magic of Interactive Rebase
# Squash 15 "wip" commits into 3 meaningful ones
git rebase -i HEAD~15
# Opens editor with:
pick a1b2c3d feat: initial setup
pick e4f5g6h wip
pick i7j8k9l wip
pick m1n2o3p wip
pick q4r5s6t wip
pick u7v8w9x fix
pick y0z1a2b wip
# Change to:
pick a1b2c3d feat: initial setup
squash e4f5g6h
squash i7j8k9l
squash m1n2o3p
squash q4r5s6t
fixup u7v8w9x
squash y0z1a2b
# Result: 1 clean commit instead of 7 messy ones!
# Or use the shortcut:
git reset --soft HEAD~7 # Keep all changes staged
git commit -m "feat: complete feature implementation with fixes" # One clean commit
Handling Common Scenarios
Scenario 1: "I committed to the wrong branch"
# Undo last commit (keep changes)
git reset HEAD~1
# Stash the changes
git stash
# Switch to correct branch
git checkout correct-branch
# Apply changes
git stash pop
git commit -m "feat: ..."
Scenario 2: "I need to pull in changes from main mid-feature"
# Option A: Merge (creates merge commit)
git merge main
# Option B: Rebase (cleaner linear history) ← PREFERRED
git rebase main
# If there are conflicts:
# 1. Git will pause at each conflict
# 2. Open the files, look for <<<<<<< markers
# 3. Fix the conflict
# 4. git add <file>
# 5. git rebase --continue
# Repeat until done
Scenario 3: "I want to undo this commit but keep the changes"
# Soft reset: keeps changes staged
git reset --soft HEAD~1
# Mixed reset: keeps changes unstaged (default)
git reset HEAD~1
# Hard reset: LOSES changes completely
git reset --hard HEAD~1 # ⚠️ Destructive!
Scenario 4: "I pushed something I shouldn't have"
# If it's YOUR branch and nobody else uses it:
git push --force-with-lease origin my-branch
# If it's SHARED (main, develop, team branches):
# DO NOT force push! Instead:
git revert <commit-hash> # Creates a new commit that undoes the change
git push origin main # Safe push
My .gitconfig
[user]
name = Alex Chen
email = alex@example.com
[init]
defaultBranch = main
[pull]
rebase = true # Always rebase instead of merge on pull
[core]
editor = code --wait # VS Code as default editor
autocrlf = input # Handle line endings properly
[alias]
co = checkout
br = branch
ci = commit
st = status -sb # Short status format
lg = log --oneline --graph --all -20 # Pretty graph view
unstage = reset HEAD --
amend = commit --amend # Edit last commit message
save = stash push -m # Stash with message
pop = stash pop
recent = branch --sort=-committerdate -10 # Recent branches
[rerere]
enabled = true # Remember conflict resolutions!
rerere is a game-changer — if you resolve the same conflict twice, git remembers your resolution and auto-applies it the third time.
The Pre-Push Checklist
Before every push, I run:
#!/bin/bash
# pre-push-check.sh
echo "=== Pre-push checks ==="
# 1. Branch name follows convention
BRANCH=$(git branch --show-current)
if ! echo "$BRANCH" | grep -qE '^(feat|fix|refactor|docs|chore|hotfix)/'; then
echo "⚠️ Branch name doesn't follow convention: $BRANCH"
fi
# 2. No "wip" or "fix" commits left
if git log --oneline origin/main..HEAD | grep -qiE '\b(wip|todo|asdf|fix)\b'; then
echo "⚠️ Found WIP/Todo commits. Consider squashing."
fi
# 3. Tests pass
if [ -f "package.json" ] && grep -q '"test"' package.json; then
npm test
if [ $? -ne 0 ]; then
echo "❌ Tests failed! Aborting push."
exit 1
fi
fi
# 4. No large files accidentally included
if git diff --cached --stat | grep -E '^\s+\d+\.\d+\s+[MG]B'; then
echo "⚠️ Large file detected!"
fi
# 5. No secrets
if git diff --cached --name-only -z | xargs -0 grep -l -E '(password|secret|key|token)\s*[:=]' 2>/dev/null; then
echo "❌ Possible secret detected! Aborting push."
exit 1
fi
echo "✅ All checks passed!"
Hook it up: .git/hooks/pre-push → make executable.
What Changed For Me
Before this workflow:
- Dreaded git operations
- Messy history that scared me to push
- Spent hours resolving confusing conflicts
- Couldn't find anything in history
After this workflow:
- Git is a tool, not an obstacle
- Clean history tells a story
- Conflicts are rare and easy when they happen
- Can find any change instantly
- Confidence in every push
What's your git workflow? Any tips I'm missing?
Follow @armorbreak for more developer content.
Top comments (0)