paucasanellas

git-workflow

Git workflow best practices including branching strategies, commit conventions, merge strategies, pull requests, and common operations. Use when working with Git version control, making commits, creating branches, or managing code collaboration.

paucasanellas 0 Updated 3mo ago
GitHub

Install

npx skillscat add paucasanellas/skills/git-workflow

Install via the SkillsCat registry.

SKILL.md

This skill covers Git workflow patterns, conventions, and best practices for effective version control and team collaboration.

Git is the foundation of modern software collaboration. Following consistent workflows, commit conventions, and branching strategies reduces friction, improves traceability, and keeps repositories clean. This skill provides practical guidance for day-to-day Git usage.

Commit Conventions

Conventional Commits

Follow the Conventional Commits specification for structured, machine-readable commit messages:

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Types:

Type When to use
feat New feature or functionality
fix Bug fix
docs Documentation only changes
style Formatting, whitespace, semicolons (no logic change)
refactor Code restructuring (no feature or fix)
perf Performance improvement
test Adding or correcting tests
build Build system or external dependency changes
ci CI/CD configuration changes
chore Maintenance tasks, tooling, configs
revert Reverting a previous commit

Examples:

feat(auth): add JWT refresh token rotation

fix(api): handle null response from payment gateway

refactor(users): extract validation logic into shared module

docs(readme): update setup instructions for Docker

feat(orders)!: change order status enum values

BREAKING CHANGE: order status values have been renamed.
"in-progress" is now "processing".

Commit Message Best Practices

Key Points:

  • Use imperative mood: "add feature" not "added feature" or "adds feature"
  • First line ≤ 72 characters
  • Separate subject from body with a blank line
  • Body explains what and why, not how
  • Reference issue/ticket numbers in footer: Closes #123

Good vs Bad:

# ❌ Bad
fixed stuff
update
WIP
changes

# ✅ Good
fix(cart): prevent duplicate items when adding same product twice
feat(search): add fuzzy matching for product name queries
refactor(db): migrate from raw SQL to query builder pattern

Atomic Commits

Each commit should represent a single logical change.

Key Points:

  • One commit = one purpose
  • Should be independently revertable
  • Don't mix refactoring with feature changes
  • Don't mix formatting with logic changes
  • Each commit should leave the codebase in a working state

Branching Strategies

GitHub Flow (Recommended for most teams)

Simple, lightweight strategy based on short-lived feature branches.

Workflow:

  1. main is always deployable
  2. Create feature branch from main
  3. Commit and push to feature branch
  4. Open Pull Request
  5. Review, discuss, iterate
  6. Merge to main
  7. Deploy from main

Key Points:

  • Simple and easy to adopt
  • Works well with CI/CD
  • Short-lived branches reduce merge conflicts
  • Best for continuous deployment

Trunk-Based Development

All developers commit to a single branch (main/trunk) with very short-lived branches (< 1 day).

Key Points:

  • Minimal branching, maximum integration
  • Requires strong CI/CD and test coverage
  • Feature flags to hide incomplete work
  • Reduces merge conflicts dramatically
  • Best for experienced teams with good test discipline

Branch Naming Conventions

Use consistent, descriptive branch names:

<type>/<ticket-id>-<short-description>

Examples:

feat/AUTH-123-jwt-refresh-tokens
fix/BUG-456-null-payment-response
refactor/TECH-789-extract-validation
hotfix/PROD-012-memory-leak-websocket
docs/DOC-345-api-endpoint-reference
chore/TECH-678-upgrade-node-20

Key Points:

  • Use lowercase and hyphens (kebab-case)
  • Include ticket/issue ID when available
  • Keep descriptions short but meaningful
  • Use the same type prefixes as commit conventions
  • Delete branches after merging

Merge Strategies

Merge Commit (--no-ff)

Creates a merge commit preserving the full branch history.

git merge --no-ff feature/my-feature

When to use:

  • You want to preserve the complete branch history
  • Feature branches have meaningful intermediate commits
  • You need to see when branches were integrated

Squash and Merge

Combines all branch commits into a single commit on the target branch.

git merge --squash feature/my-feature
git commit -m "feat(auth): add JWT refresh token rotation"

When to use:

  • Feature branch has messy or WIP commits
  • You want a clean, linear history on main
  • Each PR = one logical commit on main
  • Recommended default for most teams

Rebase and Merge

Replays branch commits on top of the target branch, then fast-forwards.

git checkout feature/my-feature
git rebase main
git checkout main
git merge --ff-only feature/my-feature

When to use:

  • You want linear history AND individual commits preserved
  • Commits are already clean and atomic
  • Be careful: rewrites history — never rebase shared/public branches

Choosing a Strategy

Strategy History Traceability Cleanliness
Merge commit Full branch history High (merge commits) Can be noisy
Squash and merge One commit per PR Medium Very clean
Rebase and merge Linear, all commits Low (no merge points) Clean

Recommendation: Squash and merge as default. Use merge commits for long-running branches where intermediate history matters.

Pull Requests (PRs)

Creating Good PRs

Key Points:

  • Keep PRs small and focused (< 400 lines ideal)
  • One PR = one logical change
  • Write a clear title following commit conventions
  • Include description with context, motivation, and approach
  • Link related issues or tickets
  • Add screenshots for UI changes
  • Self-review before requesting reviews

PR Description Template:

## What
Brief description of the change.

## Why
Motivation and context. Link to issue/ticket.

## How
Technical approach and key decisions.

## Testing
How this was tested. Steps to reproduce.

## Screenshots (if applicable)
Before/after for UI changes.

Code Review Best Practices

As a reviewer:

  • Review promptly (within a few hours, not days)
  • Focus on logic, architecture, and correctness — not style (use linters)
  • Ask questions rather than making demands
  • Approve when "good enough" — don't block on perfection
  • Use conventional comments: nit:, suggestion:, question:, issue:

As an author:

  • Don't take feedback personally
  • Respond to all comments
  • Push fixes as new commits during review (squash on merge)
  • Request re-review after significant changes

Interactive Rebase

Clean up commit history before merging:

# Rebase last N commits interactively
git rebase -i HEAD~5

# Rebase onto main
git rebase -i main

Common operations in interactive rebase:

Command What it does
pick Keep the commit as-is
reword Change the commit message
edit Pause to amend the commit
squash Merge into previous commit, combine messages
fixup Merge into previous commit, discard this message
drop Remove the commit entirely
reorder Move lines to change commit order

Key Points:

  • Only rebase commits that haven't been pushed/shared
  • Use fixup to clean up "fix typo" type commits
  • Reorder commits to group related changes
  • Force push with --force-with-lease (safer than --force)

Common Git Operations

Undoing Changes

# Discard all uncommitted changes in working directory
git checkout -- .
# or modern equivalent:
git restore .

# Unstage files (keep changes in working directory)
git reset HEAD <file>
# or modern equivalent:
git restore --staged <file>

# Undo last commit but keep changes staged
git reset --soft HEAD~1

# Undo last commit and unstage changes
git reset --mixed HEAD~1

# Undo last commit and discard changes (DESTRUCTIVE)
git reset --hard HEAD~1

# Create a new commit that reverses a previous commit (safe for shared branches)
git revert <commit-sha>

Key Points:

  • Use revert on shared/public branches — it's safe and traceable
  • Use reset only on local/unpushed commits
  • --hard is destructive — use with caution
  • --force-with-lease is safer than --force when pushing rebased commits

Stashing

Temporarily save uncommitted changes:

# Stash current changes
git stash

# Stash with a description
git stash push -m "WIP: refactoring auth module"

# List stashes
git stash list

# Apply most recent stash (keep in stash list)
git stash apply

# Apply and remove most recent stash
git stash pop

# Apply a specific stash
git stash apply stash@{2}

# Drop a specific stash
git stash drop stash@{0}

Cherry-Pick

Apply a specific commit from another branch:

git cherry-pick <commit-sha>

# Cherry-pick without committing (stage changes only)
git cherry-pick --no-commit <commit-sha>

# Cherry-pick a range of commits
git cherry-pick <start-sha>..<end-sha>

When to use:

  • Applying a hotfix from main to a release branch
  • Pulling a specific commit from a feature branch
  • Avoid using as primary workflow — prefer merging

Tagging

Mark specific points in history for releases:

# Create annotated tag (recommended)
git tag -a v1.2.0 -m "Release version 1.2.0"

# Create lightweight tag
git tag v1.2.0

# Push tags to remote
git push origin v1.2.0
git push origin --tags

# List tags
git tag -l "v1.*"

# Delete a tag
git tag -d v1.2.0
git push origin --delete v1.2.0

Key Points:

  • Use annotated tags for releases (includes metadata)
  • Follow semantic versioning: vMAJOR.MINOR.PATCH
  • Tag after merging to main, not on feature branches

Conflict Resolution

Preventing Conflicts

Key Points:

  • Keep branches short-lived
  • Pull/rebase from main frequently
  • Communicate with team about overlapping work
  • Small, focused PRs reduce conflict surface area

Resolving Conflicts

# During merge or rebase, conflicts are marked:
<<<<<<< HEAD
const timeout = 5000
=======
const timeout = 10000
>>>>>>> feature/update-timeout

# After resolving all conflicts:
git add <resolved-files>
git rebase --continue   # if rebasing
# or
git merge --continue    # if merging

# Abort if needed:
git rebase --abort
git merge --abort

Key Points:

  • Understand both sides before resolving
  • Don't just accept "theirs" or "ours" blindly
  • Test after resolving conflicts
  • Use visual merge tools when conflicts are complex

Git Hooks

Automate checks and enforce standards with Git hooks.

Common Hooks

Hook When it runs Common use
pre-commit Before commit is created Lint, format, type-check
commit-msg After commit message is written Validate commit message format
pre-push Before push to remote Run tests, check branch name
prepare-commit-msg Before editor opens for message Auto-populate templates

Tools for Git Hooks

Husky (Node.js projects):

# Install
npx husky init

# Add pre-commit hook
echo "npx lint-staged" > .husky/pre-commit

lint-staged (run linters on staged files only):

{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{json,md}": ["prettier --write"]
  }
}

commitlint (enforce conventional commits):

# .husky/commit-msg
npx --no -- commitlint --edit "$1"

.gitignore Best Practices

Structure

# Dependencies
node_modules/
vendor/

# Build output
dist/
build/
.next/
.nuxt/

# Environment files
.env
.env.local
.env.*.local

# IDE
.vscode/settings.json
.idea/

# OS
.DS_Store
Thumbs.db

# Logs
*.log
logs/

# Test coverage
coverage/

# Temporary files
tmp/
.cache/

Key Points:

  • Never commit secrets, API keys, or credentials
  • Use .env.example with placeholder values for documentation
  • Add .gitignore early — before the first commit
  • Use global gitignore for OS/IDE files: git config --global core.excludesfile ~/.gitignore_global
  • Use git rm --cached <file> to remove already-tracked files that should be ignored

Semantic Versioning

Tag releases following semver (MAJOR.MINOR.PATCH):

Increment When
MAJOR (1.0.0 → 2.0.0) Breaking changes to public API
MINOR (1.0.0 → 1.1.0) New features, backward compatible
PATCH (1.0.0 → 1.0.1) Bug fixes, backward compatible

Pre-release: 1.0.0-alpha.1, 1.0.0-beta.2, 1.0.0-rc.1

Key Points:

  • Start at 0.1.0 for initial development
  • 0.x.x signals unstable API — breaking changes can happen in minor versions
  • 1.0.0 signals first stable public API
  • Document breaking changes in changelogs

Git Best Practices Summary

Commits:

  • Write meaningful commit messages following conventional commits
  • Make atomic commits — one logical change per commit
  • Don't commit generated files, secrets, or build artifacts

Branches:

  • Keep branches short-lived (days, not weeks)
  • Delete branches after merging
  • Pull/rebase from main regularly
  • Use consistent naming conventions

Collaboration:

  • Keep PRs small and focused
  • Review PRs promptly
  • Use squash and merge for clean history
  • Communicate about overlapping work

Safety:

  • Never force-push to shared branches (main, develop)
  • Use --force-with-lease instead of --force
  • Use revert on public branches, reset on local branches
  • Tag releases with annotated tags