Git Workflows: From Solo to Team (2026)
Git isn't just commit and push. A good workflow prevents disasters before they happen.
The Essentials Everyone Needs
# Before any workflow: configure your identity!
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main # Not master anymore
# Useful defaults
git config --global core.autocrlf input # Normalize line endings on checkout
git config --global core.whitespace trailing-space,space-before-tab
git config --global push.autoSetupRemote true # git push sets upstream automatically
git config --global rebase.autoStash true # Auto-stash before rebase/pull
Solo Workflow: Simple but Effective
main ──────────────────────────────→ (deployed to production)
↑
│ Your daily work:
│ 1. git pull (sync with remote)
│ 2. Create feature branch
│ 3. Commit changes
│ 4. Push branch
│ 5. Merge to main (or PR if you want review discipline)
│ 6. Delete branch
# Daily solo workflow in practice:
# Start new work
git checkout -b feature/user-auth
# or: git switch -c feature/user-auth # Newer syntax
# Make commits (small, focused, descriptive)
git add src/auth.js
git commit -m "Add JWT token generation and validation"
# More work...
git add src/middleware.js
git commit -m "Add auth middleware for protected routes"
# Sync with main before merging
git fetch origin
git rebase origin/main # Clean linear history (vs merge = merge bubble)
# Push and merge
git push -u origin feature/user-auth
git checkout main
git merge feature/user-auth
git push
git branch -d feature/user-auth # Clean up local branch
Team Workflow: Feature Branches + PRs
main (protected) ──── only via PR merges ────→ production
↗ ↑ ↖
feature/A feature/B feature/C
(PR) (PR) (PR)
Rules:
1. Never commit directly to main
2. All changes go through Pull Requests
3. PRs require at least 1 approval
4. CI must pass before merge
5. Keep branches short-lived (< 2 days ideally)
Branch Naming Convention
# Consistent naming makes branches self-documenting
feature/user-auth-login # New feature
feature/#123-password-reset # Feature linked to issue
bugfix/crash-on-null-input # Bug fix
hotfix/security-patch-v1.2.1 # Urgent production fix
refactor/remove-deprecated-api # Code cleanup
docs/update-readme # Documentation
chore/update-dependencies # Maintenance
release/v2.0.0 # Version release
experiment/new-ui-layout # Experimental (may be discarded)
Commit Discipline: The Foundation of Everything
# ❌ Bad commits
git commit -m "fix" # Useless message
git commit -m "updated stuff" # Vague
git commit -m "fix bug, add feature, refactor" # Multiple concerns
git commit -m "WIP" # Don't commit incomplete work
git commit -m "asdfghjkl" # Seriously?
# ✅ Good commits (Conventional Commits format)
git commit -m "feat: add user authentication with JWT"
git commit -m "fix: resolve crash when email is null"
git commit -m "docs: update API endpoint examples"
git commit -m "refactor: extract validation into shared module"
git commit -m "test: add coverage for payment processing"
git commit -m "chore: upgrade express to v4.21"
git commit -m "style: format code with prettier"
git commit -m "perf: cache database queries in user service"
git commit -m "revert: undo broken auth middleware change"
# Conventional Commits structure:
# <type>(<scope>): <subject>
#
# Types: feat | fix | docs | style | refactor | perf | test | build | ci | chore | revert
# Scope: optional, describes which module/area (auth, api, db, ui...)
# Subject: imperative mood, lowercase, no period at end
# Body (optional, add blank line after subject):
git commit -m "feat(auth): add OAuth2 Google login support
- Add /auth/google endpoint for initiating OAuth flow
- Add /auth/google/callback for handling callback
- Store Google user info in users table
- Generate JWT token on successful authentication
Closes #142"
Handling Conflicts Like a Pro
# Scenario: You and teammate both edited the same file
# Step 1: Get latest main
git checkout main
git pull origin main
# Step 2: Rebase your feature branch onto latest main
git checkout feature/my-feature
git rebase main
# CONFLICT! Git marks conflicts like this:
# <<<<<<< HEAD (current: main's version)
# const authenticate = (user) => {
# validateUser(user);
# return generateToken(user);
# };
# =======
# const authenticate = (user) => {
# if (!validateUser(user)) throw new AuthError();
# return signToken(user.id);
# };
# >>>>>>> feature/my-feature (incoming: your version)
# Step 3: Resolve conflicts (pick best of both, or rewrite)
const authenticate = (user) => {
if (!validateUser(user)) throw new AuthError(); // Keep your validation
return generateToken(user); // Keep their function name
};
# Step 4: Mark as resolved and continue
git add src/auth.js # Stage the resolved file
git rebase --continue # Continue rebasing
# If it gets too messy, abort and try again
# git rebase --abort # Go back to how things were
# Pro tip: use a merge tool for visual conflict resolution
git config --global merge.tool vscode # or meld, kdiff3, etc.
git mergetool # Opens visual merge tool
Rewriting History (When It's Safe)
# ⚠️ Only rewrite LOCAL history that hasn't been pushed/shared!
# Amend last commit (fix typo, add forgotten file)
git commit --amend -m "Better commit message"
# or: git add forgotten-file.js && git commit --amend --no-edit
# Squash last N commits into one (clean up WIP commits)
git rebase -i HEAD~3
# Editor opens, pick/squash/reword/drop/edit each commit:
# pick abc1234 Initial work
# squash def5678 Add more features → squashed into previous
# drop ghi9012 Experiment that didn't work → removed entirely
# Interactive rebase commands:
# p, pick = use commit as-is
# r, reword = use commit but edit message
# e, edit = use commit but stop for amending
# s, squash = combine with previous commit
# f, fixup = like squash but discard message
# d, drop = remove commit completely
# Reorder commits (move lines in interactive rebase editor)
# Split a commit (edit → reset HEAD~1 → make multiple commits)
# Fix author info (if you committed with wrong email)
git commit --amend --author="Correct Name <correct@email.com>"
# Or for multiple commits:
git rebase -i root~1 # Then mark each with 'edit' and:
# git commit --amend --author="..." && git rebase --continue
Advanced Patterns
# Stash: Save work temporarily without committing
git stash # Save all working directory changes
git stash push -m "WIP: auth feature" # With descriptive message
git stash list # List all stashes
git stash pop # Apply and remove most recent stash
git stash apply # Apply but keep in stash list
git stash show -p # Show diff of stashed changes
git stash drop # Remove specific stash
git stash clear # Remove ALL stashes
git stash push -p # Interactively choose what to stash
# Cherry-pick: Apply specific commits from another branch
git cherry-pick abc1234 # Apply one commit
git cherry-pick abc1234 def5678 # Apply multiple commits
git cherry-pick --no-commit abc1234 # Apply to staging area only
# Bisect: Find which commit introduced a bug
git bisect start
git bisect bad # Current version has the bug
git bisect good v2.0.0 # This version was fine
# Git will checkout commits; you test each one:
git bisect good # This commit is fine
git bisect bad # This commit has the bug
# Eventually: abc1234 is the first bad commit
git bisect reset # Done, return to original branch
# Worktree: Work on multiple branches simultaneously
git worktree add ../feature-a feature/branch-a
git worktree add ../hotfix hotfix/urgent-fix
# Now you have three checkouts without stashing/switching!
git worktree list # Show all worktrees
git worktree remove ../feature-a # Clean up
# Hooks: Automate quality checks
# .git/hooks/pre-commit (make executable):
#!/bin/bash
npm run lint || exit 1 # Block commit if lint fails
npm run typecheck || exit 1 # Block commit if types wrong
# .git/hooks/commit-msg (enforce conventional commits):
#!/bin/bash
# Check if message follows conventional commit format
if ! grep -qE "^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .+" "$1"; then
echo "Invalid commit message! Use conventional commits format."
exit 1
fi
Quick Reference
Daily commands I actually use:
git status # What's changed?
git diff # What exactly changed?
git log --oneline -10 # Recent history
git add -p # Stage chunks interactively
git commit -m "type: message" # Commit with good message
git push # Send to remote
git pull --rebase # Sync + rebase (clean history)
git checkout -b new-feature # Start new work
git stash # Temporarily save work
git clean -fd # Remove untracked files (careful!)
Emergency commands:
git reset --hard HEAD~1 # Undo last commit (destructive!)
git reflog # Find lost commits (safety net!)
git checkout -b recovery abc1234 # Recover from reflog
git restore file.txt # Undo local changes to one file
What's your must-have Git alias or workflow trick?
Follow @armorbreak for more practical developer guides.
Top comments (0)