Git Good: Version Control Strategies That Would Not Make Your Team Hate You


You’ve been there: staring at a Git conflict so complex it looks like someone encrypted your code. Or maybe you’re the teammate who just pushed a 10,000-line commit titled “fixes” directly to main. Perhaps you’ve uttered the dreaded phrase, “I’ll just delete the repo and clone it again.”

It’s time to stop being a Git disaster and start being a Git master. Let’s dive into version control strategies that will make your life easier and keep your teammates from plotting your demise.

Why Most Git Workflows Are a Dumpster Fire

The average developer’s Git strategy:

  1. Make 73 unrelated changes
  2. Realize they haven’t committed in 8 hours
  3. Panic-commit with message “WIP”
  4. Force-push when things get weird
  5. Wonder why nobody wants to code review their PRs

Sound familiar? Let’s fix that.

Commit Like You Give a Damn

The Art of the Atomic Commit

Atomic commits focus on a single change, making your history browsable and your changes reviewable:

# BAD: Giant commit of doom
git add .
git commit -m "Fixed stuff"

# GOOD: Logical, separate commits
git add src/components/Button.js
git commit -m "Fix Button component focus state for accessibility"

git add src/utils/validation.js
git commit -m "Add email format validation for registration form"

Commit Messages That Actually Help

Your future self and teammates need more context than “updates”:

# Structure your commit messages like this:

Fix user authentication timeout on slow connections

- Increases token expiration window from 10s to 30s
- Adds automatic retry with exponential backoff
- Fixes issue #1337 reported by premium customers

The first line is your commit title (keep it under 50 characters). Then add a blank line followed by details.

Branching Strategies That Won’t Break Everything

Git Flow for Larger Projects

Git Flow creates a structured process with these branches:

  • main - Production code only
  • develop - Integration branch for features
  • feature/xyz - New functionality
  • hotfix/xyz - Emergency production fixes
  • release/xyz - Preparing for a release
# Starting a new feature
git checkout develop
git checkout -b feature/amazing-thing

# Working on hotfixes
git checkout main
git checkout -b hotfix/critical-payment-bug

GitHub Flow for Simpler Projects

For smaller teams, GitHub Flow simplifies things:

  1. Branch from main
  2. Develop your feature
  3. Open a PR
  4. Review and discuss
  5. Deploy/test
  6. Merge to main
git checkout -b descriptive-feature-name
# Make changes
git push -u origin descriptive-feature-name
# Open PR through GitHub interface

Pull Requests That People Actually Want to Review

A good PR is a joy to review. A bad one is digital torture.

The Anatomy of a Review-Friendly PR

Keep PRs focused and reasonably sized:

  • 200-400 lines changed is ideal
  • One logical feature or fix
  • Clear description of what and why
  • Screenshots for UI changes
  • Links to related issues
## What this PR does
Adds password strength validation to the signup form

## Why it's needed
Users were creating weak passwords, leading to account security issues

## How it works
- Uses zxcvbn library to calculate password entropy
- Shows real-time feedback as user types
- Requires minimum score of 3/5 to enable submit button

## Screenshots
[Weak password state]
[Strong password state]

## Testing done
- Unit tests for validation logic
- E2E tests for form submission
- Manual testing across Chrome/Firefox/Safari

Handling Feedback Like a Pro

When receiving comments:

  • Thank reviewers (they’re helping you!)
  • Address every comment (even if just to explain why you’re not changing something)
  • Make requested changes in new commits for easier re-review
  • Use GitHub’s suggestion feature for simple changes

Dealing With the Inevitable Git Disasters

You Committed to the Wrong Branch

# Don't panic! This is easy to fix
git cherry-pick <commit-hash>  # Apply the commit to your current branch
git checkout previous-branch
git reset --hard HEAD~1  # Remove the commit from old branch

Your Main Branch Is Broken

# Find the last working commit
git log

# Create a new branch from that commit
git checkout -b recovery-branch <last-working-commit-hash>

# Cherry-pick good changes
git cherry-pick <good-commit-hash>

# Once fixed, force-update main (BE CAREFUL)
git checkout main
git reset --hard recovery-branch
git push --force-with-lease origin main

Only use --force-with-lease after communicating with your team!

The “Oh Crap I Didn’t Mean to Commit That” Moment

# Accidentally committed secrets or large files?

# Option 1: Fix the last commit
git reset --soft HEAD~1
# Remove the problematic files
git add .
git commit -m "Your original message without the bad stuff"

# Option 2: For sensitive data already pushed
# Use BFG Repo Cleaner or git-filter-branch
# Then force push (after warning team)

Advanced Git Techniques That Make You Look Like a Wizard

Interactive Rebasing for Clean History

Squash related commits and clean up before merging:

git rebase -i HEAD~5  # Interactive rebase of last 5 commits

# You'll see an editor with options:
# pick, squash, fixup, reword, etc.

Git Hooks for Consistency

Set up pre-commit hooks to enforce standards:

# .git/hooks/pre-commit
#!/bin/sh
npm run lint && npm test

Git Aliases for Efficiency

git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'

Now git st works instead of git status!

Automating With GitHub Actions

# .github/workflows/pr-checks.yml
name: PR Checks

on:
  pull_request:
    branches: [ main, develop ]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Check commit message format
        run: |
          bash .github/scripts/validate-commits.sh

Git Etiquette: Don’t Be That Person

The Unwritten Rules of Git

  1. Never force push to shared branches
  2. Don’t rewrite history after sharing
  3. Don’t leave hanging pull requests
  4. Credit co-authors when pair programming
  5. Keep your local repo updated
# Before starting work each day
git checkout main
git pull
git checkout your-branch
git rebase main

Resolving Conflicts Like an Adult

# When you get a merge conflict
git status  # See which files are conflicted
code path/to/conflicted/file.js  # Open in editor

# Look for conflict markers <<<<<<< HEAD
# Fix the conflicts manually

git add path/to/conflicted/file.js
git rebase --continue  # If rebasing
# or
git merge --continue  # If merging

The Ultimate Git Cheat Sheet

Daily Workflow

# Start the day
git pull

# Create a new feature branch
git checkout -b feature/amazing-stuff

# Make small, focused commits
git add file1.js file2.js
git commit -m "Implement feature X"

# Push to remote
git push -u origin feature/amazing-stuff

# Create PR via GitHub interface

# Update with changes from main
git checkout main
git pull
git checkout feature/amazing-stuff
git rebase main
git push --force-with-lease

Helpful Commands

# See what you're about to commit
git diff --staged

# See changes in a file
git log -p filename

# Discard unstaged changes
git checkout -- filename

# See commit history with graph
git log --oneline --graph --decorate

# Stash changes temporarily
git stash
git stash pop

# Blame (who changed what)
git blame filename

Conclusion

Git is a powerful tool that can either make your development process smooth or turn it into a nightmare—and the difference often comes down to discipline and good habits.

Remember:

  • Small, atomic commits
  • Descriptive messages
  • Focused branches
  • Respect shared history
  • Clear pull requests

Master these practices, and you’ll not only avoid Git disasters but also become the teammate everyone wants to work with. Your future self will thank you when you’re trying to figure out why something was changed six months from now.

Now go forth and Git Good! (But please, no more commits titled “stuff”.)