This document defines our Git branching strategy, commit standards, and daily workflow.

Branch Naming Conventions

Use these prefixes for all branches:

Feature Branches

For new features or enhancements.

Format: feature/short-description

Examples:

  • feature/webhook-subscriptions
  • feature/user-dashboard
  • feature/order-export

Bugfix Branches

For fixing bugs in the current release.

Format: bugfix/short-description or bugfix/issue-number-short-description

Examples:

  • bugfix/order-total-calculation
  • bugfix/123-login-redirect
  • bugfix/webhook-retry-logic

Hotfix Branches

For urgent fixes to production.

Format: hotfix/short-description

Examples:

  • hotfix/payment-gateway-timeout
  • hotfix/database-connection-leak
  • hotfix/security-patch

Note: Hotfixes branch from main and merge back to both main and develop (if using git-flow).

Other Branch Types

  • docs/description - Documentation-only changes
  • refactor/description - Code refactoring without behaviour changes
  • test/description - Adding or updating tests
  • chore/description - Maintenance tasks (dependencies, configs)

Commit Message Format

We follow the Conventional Commits format.

Structure

  <type>(<scope>): <subject>

<body>

<footer>
  

Type

Required. Must be one of:

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation changes
  • style - Code style changes (formatting, semicolons, etc.)
  • refactor - Code refactoring (neither fixes a bug nor adds a feature)
  • test - Adding or updating tests
  • chore - Maintenance tasks (dependencies, configs, build)
  • perf - Performance improvements

Scope

Optional. Component or module affected.

Examples: auth, orders, webhooks, database, api, ui

Subject

Required. Brief description (≤50 characters).

Rules:

  • Use imperative mood (“add” not “added” or “adds”)
  • Don’t capitalise first letter
  • No period at the end
  • Be specific and concise

Body

Optional. Detailed explanation of what and why (not how).

When to include:

  • Change is complex
  • Need to explain context or reasoning
  • Breaking changes
  • Related to multiple components

Optional. Reference issues, breaking changes.

Examples:

  • Closes #123
  • Fixes #456
  • Related to #789
  • BREAKING CHANGE: API endpoint removed

Commit Message Examples

Good Examples

Simple feature:

  feat(webhooks): add subscription creation endpoint

Implements POST /api/v1/webhooks/subscriptions to allow users
to register webhook URLs for order events.

Closes #234
  

Bug fix:

  fix(orders): correct total calculation with tax

Total was not including tax for multi-currency orders.
Added currency conversion before tax calculation.

Fixes #567
  

Refactoring:

  refactor(auth): extract token generation to service

Moved JWT token generation logic from endpoint to AuthService
for better reusability and testing.
  

Simple change:

  docs: update API endpoint documentation
  

Test addition:

  test(webhooks): add integration tests for delivery retries
  

Dependency update:

  chore: upgrade fastapi to 0.109.0
  

Bad Examples

Too vague:

  fix: bug fix
  

Wrong tense:

  feat: added webhook feature
  

Too detailed (belongs in body):

  feat: add subscription creation endpoint that allows users to register webhook URLs and receive notifications when order statuses change with retry logic and signature verification
  

Multiple changes (should be separate commits):

  feat: add webhooks and refactor auth and update docs
  

Commit Frequency Guidelines

How Often to Commit

Commit every logical change.

Good commit frequency:

  • After completing a model or schema
  • After completing a service method
  • After completing an endpoint
  • After tests pass for a feature
  • Before switching tasks or taking a break
  • At least once per hour when actively coding

Bad commit frequency:

  • One massive commit at end of day
  • Committing broken code
  • Committing without testing

Typical Commits Per Day

For active development: 3-10 commits per day

Example daily commits:

  09:30 - feat(webhooks): add webhook subscription model
10:15 - feat(webhooks): add subscription creation schema
11:00 - feat(webhooks): add subscription service methods
12:00 - feat(webhooks): add subscription CRUD endpoints
14:00 - test(webhooks): add subscription creation tests
15:30 - feat(webhooks): add delivery worker and retry logic
16:30 - test(webhooks): add delivery retry tests
17:00 - docs(webhooks): update API documentation
  

Don’t Commit Broken Code

Rule: Every commit should leave the codebase in a working state.

Good:

  • All tests pass
  • Code runs without errors
  • Linting passes

Bad:

  • Syntax errors
  • Failing tests
  • Missing imports

Exception: Work-in-progress branches (mark as WIP: in commit message)

  WIP: feat(webhooks): partial implementation of delivery logic

Pushing to backup work. Tests not yet written.
  

Commit Before Switching Tasks

Before switching to a different task:

  1. Commit current changes (even if incomplete)
  2. Mark as WIP if not complete
  3. Push to remote as backup
  git add .
git commit -m "WIP: feat(orders): partial export functionality"
git push origin feature/order-export
  

When to Push

Push to remote regularly to:

  • Back up your work
  • Allow others to see progress
  • Enable collaboration
  • Trigger CI/CD checks

Push Frequency

At minimum:

  • End of each day
  • When feature is testable
  • Before requesting code review
  • After completing a significant milestone

Recommended:

  • After every 2-3 commits
  • Before taking a break or lunch
  • After completing a logical unit of work

Before Pushing

Always:

  1. Run tests: pytest and npm test
  2. Run linters: Pre-commit hooks should do this
  3. Review your changes: git diff main

How to Write Good Commit Messages

Focus on WHY, Not WHAT

The code shows what changed. Commit message explains why.

Good (explains why):

  fix(webhooks): add retry delay to prevent rate limiting

Webhooks were retrying immediately and hitting rate limits.
Added exponential backoff: 1s, 2s, 4s between retries.
  

Bad (describes what):

  fix(webhooks): change retry timing

Changed the retry timing from immediate to delayed.
  

Be Specific

Generic messages aren’t helpful when looking at history.

Specific:

  fix(orders): prevent negative inventory after concurrent orders
  

Vague:

  fix: bug fix
  

Use Imperative Mood

Write as if giving a command.

Imperative:

  • “add feature”
  • “fix bug”
  • “update documentation”

Wrong tense:

  • “added feature”
  • “fixing bug”
  • “updates documentation”

Tip: Complete this sentence: “If applied, this commit will ___”

✅ “If applied, this commit will add webhook subscription endpoint” ❌ “If applied, this commit will added webhook subscription endpoint

Keep Subject Short

Limit: 50 characters for subject line

If you need more, use the body.

Concise:

  feat(webhooks): add subscription endpoint

Implements POST /api/v1/webhooks/subscriptions endpoint
to allow users to register webhook URLs.
  

Too long:

  feat(webhooks): add subscription endpoint that allows users to register webhook URLs for receiving order notifications
  

Using Git Hooks

Pre-commit Hooks

Pre-commit hooks run automatically before each commit.

What they check:

  • Code formatting (Black, Prettier)
  • Import sorting (isort)
  • Linting (flake8, eslint)
  • Type checking (mypy)
  • Trailing whitespace
  • Large files

Installation:

  pip install pre-commit
pre-commit install
  

Manual run:

  pre-commit run --all-files
  

Bypass (emergency only):

  git commit --no-verify
  

See ../06-tooling/pre-commit-hooks.md for setup.

Commit Message Hook

Validates commit message format.

Install:

  # .git/hooks/commit-msg
#!/bin/sh
commit_msg=$(cat "$1")
pattern="^(feat|fix|docs|style|refactor|test|chore|perf)(\(.+\))?: .{1,50}"

if ! echo "$commit_msg" | grep -Eq "$pattern"; then
    echo "❌ Commit message doesn't follow format: type(scope): subject"
    echo ""
    echo "Examples:"
    echo "  feat(webhooks): add subscription endpoint"
    echo "  fix(orders): correct tax calculation"
    echo ""
    exit 1
fi
  

Interactive Rebase for Clean History

Use interactive rebase to clean up commit history before creating a PR.

When to Rebase

Before creating a PR, if you have:

  • Multiple WIP commits
  • Typo fix commits
  • Commits that should be combined

How to Rebase

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

# Or rebase since branching from main
git rebase -i main
  

Rebase Commands

In the interactive editor:

  • pick - Keep commit as-is
  • reword - Change commit message
  • edit - Edit commit contents
  • squash - Combine with previous commit
  • fixup - Like squash, but discard commit message
  • drop - Remove commit

Example Rebase Session

Before:

  a1b2c3d feat(webhooks): add model
b2c3d4e fix typo
c3d4e5f WIP: add service
d4e5f6g feat(webhooks): complete service
e5f6g7h fix linting
  

Rebase commands:

  pick a1b2c3d feat(webhooks): add model
fixup b2c3d4e fix typo
pick c3d4e5f WIP: add service
squash d4e5f6g feat(webhooks): complete service
fixup e5f6g7h fix linting
  

After:

  a1b2c3d feat(webhooks): add webhook subscription model
c3d4e5f feat(webhooks): add webhook subscription service
  

Push after rebase:

  # Use --force-with-lease (safer than --force)
git push --force-with-lease
  

⚠️ Warning: Only rebase commits that haven’t been pushed, or coordinate with team if rebasing pushed commits.


Resolving Merge Conflicts

When Conflicts Occur

Conflicts happen when:

  • Two branches modify the same lines
  • One branch deletes what another modifies
  • Both branches create files with same name

Resolution Steps

  # 1. Pull latest main
git checkout main
git pull

# 2. Checkout your feature branch
git checkout feature/your-feature

# 3. Merge main (or rebase)
git merge main
# or
git rebase main

# 4. Git shows conflicts
# CONFLICT (content): Merge conflict in app/api/v1/endpoints/users.py

# 5. Open conflicted files and look for markers
# <<<<<<< HEAD
# your changes
# =======
# main branch changes
# >>>>>>> main

# 6. Edit file to resolve conflict (remove markers, keep desired code)

# 7. Stage resolved files
git add app/api/v1/endpoints/users.py

# 8. Complete merge/rebase
git commit  # for merge
# or
git rebase --continue  # for rebase

# 9. Push
git push
  

Tips for Avoiding Conflicts

  • Pull main frequently
  • Keep PRs small and merge quickly
  • Communicate with team about what you’re working on
  • Don’t work on same files simultaneously if possible

Branch Management

Creating a Branch

  # From main
git checkout main
git pull
git checkout -b feature/your-feature
  

Keeping Branch Updated

  # Option 1: Merge main into your branch
git checkout feature/your-feature
git merge main

# Option 2: Rebase your branch on main (cleaner history)
git checkout feature/your-feature
git rebase main
  

When to update:

  • Before creating a PR
  • If your branch is >3 days old
  • If main has important changes you need

Deleting Merged Branches

  # Delete local branch
git branch -d feature/your-feature

# Delete remote branch
git push origin --delete feature/your-feature
  

GitHub can auto-delete branches after PR merge - enable this in repository settings.


Daily Git Workflow

Start of Day

  # 1. Pull latest changes
git checkout main
git pull

# 2. Create or switch to feature branch
git checkout -b feature/new-feature
# or
git checkout feature/existing-feature
git pull

# 3. Start coding!
  

During Development

  # Make changes...

# Check what changed
git status
git diff

# Stage changes
git add app/models/webhook.py
# or stage all
git add .

# Commit with good message
git commit -m "feat(webhooks): add webhook subscription model"

# Repeat for each logical change
  

End of Day

  # Commit any remaining changes
git add .
git commit -m "WIP: feat(webhooks): partial service implementation"

# Push to backup work
git push origin feature/your-feature

# Review what you accomplished
git log --oneline --since="1 day ago"
  

Creating a PR

  # 1. Make sure all changes committed
git status

# 2. Update from main
git checkout main
git pull
git checkout feature/your-feature
git rebase main

# 3. Run tests
pytest
npm test

# 4. Push
git push origin feature/your-feature

# 5. Create PR on GitHub
# See pr-process.md for PR description template
  

Useful Git Commands

Viewing History

  # View commit history
git log

# Compact view
git log --oneline

# View last 5 commits
git log -5

# View commits by author
git log --author="John Doe"

# View commits since date
git log --since="2025-01-01"

# View file history
git log --follow app/models/user.py

# View changes in commit
git show abc123
  

Undoing Changes

  # Discard uncommitted changes to file
git checkout -- file.py

# Discard all uncommitted changes
git reset --hard

# Undo last commit (keep changes)
git reset --soft HEAD~1

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

# Revert a merged commit (safe for shared branches)
git revert -m 1 abc123
  

Stashing Changes

  # Save work-in-progress
git stash

# List stashes
git stash list

# Apply most recent stash
git stash apply

# Apply specific stash
git stash apply stash@{1}

# Apply and remove stash
git stash pop

# Stash with message
git stash save "WIP: webhook delivery logic"
  

Checking Out Specific Files

  # Get file from another branch
git checkout feature/other-branch -- app/models/user.py

# Get file from specific commit
git checkout abc123 -- app/models/user.py

# Get file from main
git checkout main -- app/config.py
  

Git Configuration

  # Set up your identity
git config --global user.name "Your Name"
git config --global user.email "your.email@company.com"

# Set default branch name
git config --global init.defaultBranch main

# Use VS Code as editor
git config --global core.editor "code --wait"

# Colourful output
git config --global color.ui auto

# Show original in merge conflicts
git config --global merge.conflictstyle diff3

# Rebase by default when pulling
git config --global pull.rebase true

# Prune deleted remote branches
git config --global fetch.prune true
  

Useful Aliases

  # Add to ~/.gitconfig

[alias]
    st = status
    co = checkout
    br = branch
    ci = commit
    unstage = reset HEAD --
    last = log -1 HEAD
    visual = log --graph --oneline --all
    undo = reset --soft HEAD~1
  

Usage:

  git st          # instead of git status
git co main     # instead of git checkout main
git visual      # pretty commit graph
  

Common Scenarios

Scenario: Accidentally committed to main

  # 1. Create a branch with your changes
git branch feature/my-changes

# 2. Reset main to remote
git reset --hard origin/main

# 3. Switch to new branch
git checkout feature/my-changes
  

Scenario: Need to pull changes but have uncommitted work

  # Option 1: Stash, pull, apply
git stash
git pull
git stash pop

# Option 2: Commit as WIP
git add .
git commit -m "WIP: save work before pull"
git pull
  

Scenario: Pushed bad commit

  # If no one else has pulled yet
git revert HEAD
git push

# If it's a public branch, create revert commit
git revert abc123
git push
  

Scenario: Need to change last commit message

  # If not pushed yet
git commit --amend -m "feat(webhooks): add subscription endpoint"

# If already pushed (coordinate with team)
git commit --amend -m "feat(webhooks): add subscription endpoint"
git push --force-with-lease
  

Next Steps