Mastering Git for Professional Development Teams
Every developer knows
git addgit commitgit pushThis guide covers professional Git workflows used by real engineering teams.
"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." — John Woods. The same applies to your commit messages.
1. Commit Messages: The Changelog Nobody Reads Until They Need It
A commit message is a letter to your future self and your teammates. Write it like one.
The Conventional Commits Standard
text# Format: <type>(<scope>): <description> # # Types: # feat — new feature # fix — bug fix # docs — documentation only # style — formatting, no logic change # refactor — code change that neither fixes a bug nor adds a feature # perf — performance improvement # test — adding or fixing tests # chore — build process, dependency updates # ci — CI/CD changes # BAD commits (useless history) git commit -m "fix" git commit -m "changes" git commit -m "wip" git commit -m "asdfgh" # GOOD commits (searchable, meaningful history) git commit -m "feat(auth): add OAuth2 login with Google provider" git commit -m "fix(api): handle null user in /api/profile endpoint" git commit -m "perf(db): add composite index on orders(user_id, status)" git commit -m "refactor(components): extract Button into reusable ui primitive"
Multi-line Commits for Complex Changes
textgit commit -m "fix(auth): prevent session fixation on login Before this fix, the session ID was preserved across login/logout cycles, allowing an attacker who obtained a pre-auth session ID to hijack the authenticated session. Fix: regenerate session ID on successful authentication. Closes #247 Security: CVE-2026-XXXX"
git log --onelinesemantic-releasechangesets2. Branching Strategies
Git Flow: For Versioned Releases
textmain ──────────────────────────────────────── (production) │ └── develop ──────────────────────────────── (integration) │ ├── feature/user-auth ──── merge → develop ├── feature/payment-api ── merge → develop │ └── release/v2.1.0 ──────── merge → main + develop │ └── hotfix/critical-bug ── merge → main + develop
Good for: products with scheduled releases, multiple versions in production, large teams.
GitHub Flow: For Continuous Deployment
textmain ──────────────────────────────────────── (always deployable) │ ├── feature/add-dark-mode ── PR → main → deploy ├── fix/navbar-mobile ────── PR → main → deploy └── chore/update-deps ────── PR → main → deploy
Good for: web apps with continuous deployment, small-to-medium teams, SaaS products.
Trunk-Based Development: For High-Velocity Teams
textmain ──────────────────────────────────────── (commit directly or very short-lived branches) │ ├── short-lived/feature-flag-experiment (< 1 day) └── short-lived/db-migration (< 1 day)
Good for: teams with strong CI/CD, feature flags, and high deployment frequency. Used by Google, Facebook, and Netflix.
3. Rebase vs Merge: The Eternal Debate
text# Merge: preserves history exactly as it happened # Creates a merge commit — shows the branch structure git checkout main git merge feature/user-auth # Result: non-linear history with merge commits # Rebase: rewrites history to be linear # Replays your commits on top of the target branch git checkout feature/user-auth git rebase main git checkout main git merge feature/user-auth # Fast-forward — no merge commit # Result: clean linear history
When to Use Each
text# Use MERGE for: # - Merging feature branches into main (preserves context) # - Public branches that others have checked out # - When you want to see exactly when branches diverged # Use REBASE for: # - Updating your feature branch with latest main changes # - Cleaning up local commits before a PR # - Keeping a linear, readable history # Interactive rebase: clean up messy local commits before PR git rebase -i HEAD~5 # Rewrite last 5 commits # In the editor: # pick a1b2c3 feat: add user model # squash d4e5f6 fix typo ← squash into previous # squash g7h8i9 fix another typo ← squash into previous # reword j0k1l2 add tests ← reword the message # pick m3n4o5 feat: add auth endpoints
4. The Perfect Pull Request
A PR is a communication tool, not just a code delivery mechanism.
text## What does this PR do? Adds Google OAuth2 login as an alternative to email/password authentication. Users can now click "Continue with Google" on the login page. ## Why? ~40% of users abandon the registration form. OAuth reduces friction and eliminates password management for users who prefer it. ## How was it tested? - [ ] Unit tests for the OAuth callback handler (see `auth.service.test.ts`) - [ ] Manual testing with a real Google account in staging - [ ] Tested error cases: denied permission, expired token, invalid state ## Screenshots [Before] [After] ## Breaking changes None — existing email/password login is unchanged. ## Checklist - [x] Tests pass locally - [x] No console.log statements left in - [x] Environment variables documented in .env.example - [x] Database migration included and tested Closes #183
PR Size: The Most Ignored Best Practice
text# Check how large your PR is before opening it git diff main --stat # If it's more than ~400 lines changed, consider splitting it. # Large PRs: # - Take longer to review (reviewers lose focus) # - Are harder to understand in context # - Are riskier to merge (more surface area for bugs) # - Block other work longer # Split by: # - Refactoring PR (no behavior change) + Feature PR # - Data model PR + API PR + UI PR # - Infrastructure PR + Application PR
5. Git Hooks: Automate Quality Gates
Git hooks run scripts at specific points in the Git workflow. Use them to enforce standards before code ever reaches CI.
text# Install husky for cross-platform hooks npm install --save-dev husky lint-staged npx husky init
text// package.json { "lint-staged": { "*.{ts,tsx}": [ "eslint --fix", "prettier --write" ], "*.{json,md,yml}": [ "prettier --write" ] } }
text# .husky/pre-commit — runs before every commit #!/bin/sh npx lint-staged
text# .husky/commit-msg — validates commit message format #!/bin/sh npx --no -- commitlint --edit $1
text// commitlint.config.js — enforce Conventional Commits module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [2, 'always', [ 'feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'revert' ]], 'subject-max-length': [2, 'always', 100], 'subject-case': [2, 'always', 'lower-case'], }, };
text# .husky/pre-push — run tests before pushing #!/bin/sh npm run test -- --run npm run type-check
--no-verify6. Advanced Git Commands You Should Know
text# git stash: temporarily shelve changes git stash push -m "WIP: half-finished auth refactor" git stash list git stash pop # Apply most recent stash git stash apply stash@{2} # Apply specific stash # git cherry-pick: apply a specific commit to another branch git cherry-pick a1b2c3d4 # Apply one commit git cherry-pick a1b2..d4e5 # Apply a range of commits # git bisect: binary search for the commit that introduced a bug git bisect start git bisect bad # Current commit is broken git bisect good v2.0.0 # This tag was working # Git checks out the midpoint — you test and mark good/bad # Repeat until Git identifies the exact breaking commit git bisect reset # git reflog: recover "lost" commits # Even after reset --hard, commits are recoverable for ~90 days git reflog git checkout HEAD@{5} # Go back to where HEAD was 5 moves ago # git worktree: work on multiple branches simultaneously # No need to stash — each worktree is a separate directory git worktree add ../portfolio-hotfix hotfix/critical-bug # Now you have two working directories, one per branch
7. Resolving Conflicts Like a Pro
text# When a merge conflict occurs: git merge feature/payment-api # CONFLICT (content): Merge conflict in src/services/order.service.ts # Open the file — you'll see conflict markers: # <<<<<<< HEAD (your changes) # const total = items.reduce((sum, item) => sum + item.price, 0); # ======= # const total = items.reduce((sum, item) => sum + item.price * item.qty, 0); # >>>>>>> feature/payment-api (incoming changes) # Use a visual merge tool git mergetool # Opens configured tool (VS Code, IntelliJ, etc.) # Or configure VS Code as your merge tool git config --global merge.tool vscode git config --global mergetool.vscode.cmd 'code --wait $MERGED' # After resolving: git add src/services/order.service.ts git merge --continue # If it's a disaster, abort and start over git merge --abort
Preventing Conflicts
text# Update your branch frequently — small conflicts are easier than large ones git fetch origin git rebase origin/main # Rebase instead of merge to keep history clean # Use git rerere (reuse recorded resolution) # Git remembers how you resolved a conflict and applies it automatically next time git config --global rerere.enabled true
8. Git Aliases: Work Faster
text# Add to ~/.gitconfig [alias] # Short status s = status -sb # Pretty log lg = log --oneline --graph --decorate --all # Undo last commit (keep changes staged) undo = reset --soft HEAD~1 # Amend last commit without changing message amend = commit --amend --no-edit # Delete all merged branches cleanup = "!git branch --merged | grep -v '\\*\\|main\\|develop' | xargs -n 1 git branch -d" # Show what changed in last commit last = log -1 HEAD --stat # Find commits by message find = "!f() { git log --all --oneline --grep=\"$1\"; }; f" # Stash with a message quickly save = stash push -m
text# Usage git lg # Beautiful graph log git s # Quick status git undo # Oops, undo that commit git cleanup # Remove stale branches git find "auth" # Find all commits mentioning "auth"
9. Watch: Git for Professional Developers

10. The Git Habits That Matter
git push --force--force-with-leaseThe Bottom Line
Git is the one tool every developer uses every single day, and most developers use only 10% of it. The investment in learning it properly pays back every day for the rest of your career.
Clean commits, focused PRs, and a consistent branching strategy are not bureaucracy — they are the difference between a codebase that is a joy to work in and one that everyone dreads touching.

Vighnesh Salunkhe
"Passionate about building scalable web applications and exploring the intersection of AI and human creativity."
Join the Conversation
Share your thoughts or ask a question