Contact

The Git Workflow That Actually Works for Solo Developers

November 25, 2025
Nick Paolini
6 min read
GitProductivityBest PracticesWorkflow
The Git Workflow That Actually Works for Solo Developers

Let's be honest - most Git tutorials are written for teams of 50+ developers working on enterprise applications. But what if you're a solo developer working on side projects? You don't need GitFlow with its release branches and hotfix procedures.

Here's the simple, effective Git workflow I've settled on after years of trial and error.

The Core Principles

  1. Main branch is always deployable
  2. Feature branches for everything
  3. Meaningful commit messages
  4. Regular small commits over huge dumps
  5. Rebase to keep history clean

That's it. No develop branch, no release branches, no complicated merge strategies.

The Day-to-Day Workflow

Starting a New Feature

# Make sure main is up to date
git checkout main
git pull origin main
 
# Create a feature branch
git checkout -b feature/add-dark-mode
 
# Work on your feature
# Make commits as you go
git add .
git commit -m "feat: add dark mode toggle component"

I prefix my commits with conventional commit types (feat:, fix:, docs:, etc.) because it makes the history scannable at a glance.

Making Commits

I used to make giant commits with messages like "finished feature" or "fixes". Now I commit small, focused changes:

# ❌ Bad
git commit -m "updated stuff"
 
# ✅ Good
git commit -m "feat: add dark mode context provider"
git commit -m "feat: create theme toggle button component"
git commit -m "style: update color tokens for dark mode"
git commit -m "docs: add dark mode usage to README"

Each commit does one thing and has a clear message. If I need to revert something later, I can target the exact change.

Keeping Your Branch Updated

If I'm working on a feature for a few days, I'll rebase on main regularly to avoid merge conflicts:

# Update main
git checkout main
git pull origin main
 
# Rebase your feature branch
git checkout feature/add-dark-mode
git rebase main
 
# If there are conflicts, fix them
# Then continue
git rebase --continue

Cleaning Up Before Merging

Before I merge a feature, I'll often squash or reword commits to clean up the history:

# Interactive rebase for the last 5 commits
git rebase -i HEAD~5
 
# This opens an editor where you can:
# - squash commits together
# - reword commit messages
# - reorder commits
# - drop commits entirely

For example, I might squash "fix typo" and "fix another typo" commits into the main feature commit.

Merging Back to Main

I prefer merge commits over fast-forward merges because they preserve the feature branch context:

git checkout main
git merge --no-ff feature/add-dark-mode
git push origin main
 
# Delete the feature branch
git branch -d feature/add-dark-mode

The --no-ff flag creates a merge commit even if a fast-forward is possible, which makes it clear in the history that this was a feature.

Handling Experiments

For quick experiments or spike solutions, I use a different approach:

# Create a throwaway branch
git checkout -b spike/test-new-library
 
# Experiment freely
# Make messy commits, try things, break stuff
 
# If it works and you want to keep it:
git checkout -b feature/implement-new-library
git reset --soft main
git commit -m "feat: integrate new library"
 
# The experiment branch can be deleted
git branch -D spike/test-new-library

This lets me experiment messily, then create a clean, focused commit if I want to keep the changes.

My Commit Message Format

I use conventional commits with a twist:

<type>: <description>

[optional body]

[optional footer]

Types I use:

  • feat: - New feature
  • fix: - Bug fix
  • refactor: - Code change that neither fixes a bug nor adds a feature
  • docs: - Documentation changes
  • style: - Code style changes (formatting, semicolons, etc.)
  • test: - Adding or updating tests
  • chore: - Maintenance tasks (updating dependencies, etc.)

Examples:

git commit -m "feat: add user authentication with JWT"
 
git commit -m "fix: prevent memory leak in event listeners"
 
git commit -m "refactor: extract validation logic to separate module
 
Moved validation functions from component into utils/validation.ts
for better reusability and testability."
 
git commit -m "docs: update API documentation for auth endpoints"

Useful Git Aliases

I've added these to my ~/.gitconfig:

[alias]
  # Short status
  s = status -sb
 
  # Pretty log
  lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
 
  # Undo last commit but keep changes
  undo = reset HEAD~1 --soft
 
  # Amend last commit
  amend = commit --amend --no-edit
 
  # List branches sorted by last modified
  branches = branch --sort=-committerdate
 
  # Quick commit all changes
  ca = commit -am
 
  # Create and checkout branch
  cb = checkout -b

Now I can do:

git s              # Quick status
git lg             # Beautiful log
git undo           # Undo last commit
git amend          # Fix last commit
git cb feat/new    # Create and switch to branch

Dealing with Mistakes

Committed to the wrong branch?

# Undo the commit but keep the changes
git reset HEAD~1 --soft
 
# Switch to the correct branch
git checkout correct-branch
 
# Commit there instead
git commit -m "feat: the feature on the right branch"

Need to undo a public commit?

# Create a new commit that reverses the changes
git revert <commit-hash>

Never use git reset on commits you've already pushed!

Accidentally committed a secret?

# Remove the file from git but keep it locally
git rm --cached .env
echo ".env" >> .gitignore
git commit -m "chore: remove .env from version control"
 
# Then change the secret!

When to Push

I push to remote:

  • After completing a logical chunk of work
  • At the end of the day (even if feature isn't done)
  • Before switching computers
  • Before trying something risky

Regular pushes are like save points in a video game. Don't wait until everything is perfect.

What I Don't Do

  • No force push unless I'm 100% sure nobody else has pulled the branch
  • No commit directly to main for anything significant
  • No vague commit messages like "fix", "update", "changes"
  • No huge commits with 50 files changed

Tools That Help

  • Git GUI: I use Fork for visualizing branches and staging partial commits
  • Git hooks: Husky for pre-commit linting
  • Conventional commits: Commitizen for structured commit messages

The Bottom Line

This workflow is simple enough that I actually follow it, but structured enough to keep my projects organized. It scales from tiny side projects to larger applications without feeling like overkill.

The key is finding what works for you and sticking to it consistently. Good Git hygiene becomes muscle memory after a few weeks.

What's your Git workflow? Any tips or aliases you swear by?

Resources