Git Workflow
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-subscriptionsfeature/user-dashboardfeature/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-calculationbugfix/123-login-redirectbugfix/webhook-retry-logic
Hotfix Branches
For urgent fixes to production.
Format: hotfix/short-description
Examples:
hotfix/payment-gateway-timeouthotfix/database-connection-leakhotfix/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 changesrefactor/description- Code refactoring without behaviour changestest/description- Adding or updating testschore/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 featurefix- Bug fixdocs- Documentation changesstyle- Code style changes (formatting, semicolons, etc.)refactor- Code refactoring (neither fixes a bug nor adds a feature)test- Adding or updating testschore- 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
Footer
Optional. Reference issues, breaking changes.
Examples:
Closes #123Fixes #456Related to #789BREAKING 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:
- Commit current changes (even if incomplete)
- Mark as WIP if not complete
- 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:
- Run tests:
pytestandnpm test - Run linters: Pre-commit hooks should do this
- 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-isreword- Change commit messageedit- Edit commit contentssquash- Combine with previous commitfixup- Like squash, but discard commit messagedrop- 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
Recommended Config
# 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
- Review
pr-process.mdfor pull request standards - Check
feature-development.mdfor development workflow - See
../06-tooling/pre-commit-hooks.mdfor hook setup