Git Workflow¶
A well-defined Git workflow is essential for team collaboration and code quality. This guide covers branch strategies, commit conventions, pull request processes, and merge strategies for Python and Django development teams.
Why Git Workflow Matters¶
Benefits of Structured Workflow
- Clear collaboration - Team members know how to contribute
- Code quality - Review and testing before merge
- Release management - Track what goes into production
- Rollback capability - Easy to revert problematic changes
- Feature isolation - Work independently without conflicts
Branch Strategy¶
Main Branches¶
Production Django applications typically use a simple, effective branch structure:
gitGraph
commit id: "Initial commit"
branch develop
checkout develop
commit id: "Setup Django project"
branch feature/user-auth
checkout feature/user-auth
commit id: "Add User model"
commit id: "Add login view"
checkout develop
merge feature/user-auth
checkout main
merge develop tag: "v1.0.0"
Main Branch (main or master):
- Production-ready code only
- Protected branch with required reviews
- Tagged releases (v1.0.0, v1.1.0, etc.)
- CI/CD deploys from this branch
- Direct commits forbidden
Development Branch (develop) - Optional:
- Integration branch for features
- Used in larger teams (3+ developers)
- Continuous integration testing
- May deploy to staging environment
- Merges to main for releases
Feature Branches:
- Created from main (or develop if used)
- Named descriptively: feature/, fix/, refactor/
- Deleted after merge
- Short-lived (days, not weeks)
- One feature or fix per branch
Branch Naming Conventions¶
Adopt consistent, descriptive branch names:
# Feature branches
feature/user-authentication
feature/django-rest-api
feature/celery-task-queue
# Bug fixes
fix/login-redirect-loop
fix/password-reset-email
fix/database-migration-error
# Refactoring
refactor/user-model-cleanup
refactor/api-serializers
refactor/test-fixtures
# Hotfixes (urgent production fixes)
hotfix/security-patch-cve-2024
hotfix/payment-processing-bug
# Documentation
docs/api-documentation
docs/deployment-guide
# Performance improvements
perf/database-query-optimization
perf/api-response-caching
# Chores (dependencies, config, etc.)
chore/update-django-5-2
chore/upgrade-python-3-13
Creating and Managing Branches¶
# Create feature branch from main
git checkout main
git pull origin main
git checkout -b feature/user-dashboard
# Work on feature
git add .
git commit -m "Add user dashboard view"
git push -u origin feature/user-dashboard
# Keep branch updated with main
git checkout main
git pull origin main
git checkout feature/user-dashboard
git rebase main # or git merge main
# Clean up after merge
git checkout main
git branch -d feature/user-dashboard
git push origin --delete feature/user-dashboard
Branch Protection Rules¶
Configure branch protection in GitHub/GitLab:
Main Branch Protection: - Require pull request reviews (minimum 1) - Require status checks to pass - Require branches to be up to date - No force pushes - No deletions - Include administrators in restrictions
Develop Branch Protection (if used): - Require pull request reviews (minimum 1) - Require CI to pass - Allow force push for maintainers (with caution)
Commit Message Conventions¶
Conventional Commits¶
Adopt Conventional Commits specification for structured, meaningful commit history:
Format:
Types:
- feat: New feature for the user
- fix: Bug fix for the user
- docs: Documentation changes
- style: Formatting, missing semicolons, etc.
- refactor: Code change that neither fixes a bug nor adds a feature
- perf: Performance improvement
- test: Adding or updating tests
- chore: Updating build tasks, package manager configs, etc.
Examples:
# Feature commits
feat(auth): add password reset functionality
Implemented password reset flow using Django's built-in
PasswordResetView. Added email templates and URL routing.
Closes #42
# Bug fix commits
fix(api): correct pagination in user list endpoint
The user list API was returning all users instead of paginated
results. Fixed by adding PageNumberPagination to UserViewSet.
Fixes #157
# Documentation commits
docs(deployment): add AWS ECS deployment guide
Created comprehensive guide for deploying Django to AWS ECS
with RDS database and ALB load balancer.
# Refactoring commits
refactor(models): simplify User model relationships
Removed redundant reverse relationships and optimized
select_related queries for better performance.
# Performance commits
perf(queries): add database indexes for common lookups
Added indexes on User.email and Article.created_at to improve
query performance for frequently accessed data.
# Test commits
test(auth): add integration tests for login flow
Added comprehensive tests covering successful login,
invalid credentials, and account lockout scenarios.
# Chore commits
chore(deps): update Django to 5.2.1
Updated Django from 5.2.0 to 5.2.1 to include security
patches and bug fixes.
Commit Message Best Practices¶
Subject Line: - Use imperative mood ("add" not "added" or "adds") - Limit to 50 characters - Don't end with a period - Capitalize first letter - Be specific and descriptive
# Good
feat(auth): add two-factor authentication
fix(api): validate email format in registration
docs(readme): update installation instructions
# Bad
Added stuff
Fixed bug
Updates
WIP
Body: - Wrap at 72 characters - Explain what and why, not how - Use bullet points for multiple changes - Reference issue numbers
feat(celery): add async email sending
Implemented Celery task queue for asynchronous email delivery:
- Created send_email_task for processing emails
- Added Redis as message broker
- Configured retry logic with exponential backoff
- Updated email views to queue tasks instead of blocking
This improves user experience by eliminating email send delays
during registration and password reset flows.
Closes #234
Footer: - Reference issues and pull requests - Note breaking changes - List co-authors if pair programming
fix(models): correct timezone handling in Article model
BREAKING CHANGE: Article.published_at now uses timezone-aware
datetime objects. Existing code must be updated to use
timezone.now() instead of datetime.now().
Refs #456
Co-authored-by: Jane Smith <jane@example.com>
Commit Granularity¶
Each commit should be a logical, atomic unit of work:
Good Commit Granularity:
# Commit 1: Model changes
feat(models): add Comment model with User relationship
# Commit 2: View changes
feat(views): create CommentViewSet for API
# Commit 3: Template changes
feat(templates): add comment display to article detail
# Commit 4: Tests
test(comments): add tests for comment creation and display
Bad Commit Granularity:
# Too broad
feat: add commenting system
# (Includes models, views, templates, tests - hard to review)
# Too granular
fix: add import
fix: fix typo
fix: update spacing
# (Should be one commit)
Amending and Rewriting History¶
Modify unpushed commits to maintain clean history:
# Amend last commit
git add forgotten_file.py
git commit --amend --no-edit
# Amend commit message
git commit --amend -m "Better commit message"
# Interactive rebase to clean up commits
git rebase -i HEAD~3 # Last 3 commits
# In rebase editor:
# pick abc1234 feat: add feature
# squash def5678 fix: typo in feature # Squash into previous
# reword ghi9012 test: add tests # Edit message
Never Rewrite Public History
Never force push to shared branches or rewrite commits that others may have pulled. This causes conflicts and confusion. Only amend or rebase commits that exist only in your local repository.
Pull Request Process¶
Creating Pull Requests¶
Before Creating PR:
-
Ensure branch is up to date:
-
Run all checks locally:
-
Review your changes:
PR Title Format:
Follow same conventions as commit messages:
feat(auth): Add OAuth2 authentication support
fix(api): Correct pagination in article list endpoint
docs(deployment): Update Docker deployment guide
PR Description Template:
Create .github/pull_request_template.md:
## Description
Brief description of what this PR accomplishes.
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring
## Related Issues
Closes #(issue number)
Relates to #(issue number)
## Changes Made
- Detailed list of changes
- Additional context about the changes
- Why these changes were necessary
## Testing
Describe the tests you ran to verify your changes:
- [ ] Unit tests pass (`pytest`)
- [ ] Integration tests pass
- [ ] Manual testing performed
- [ ] No new warnings or errors
## Database Migrations
- [ ] No migrations required
- [ ] Migrations included and tested
- [ ] Migrations are reversible
## Documentation
- [ ] Code includes docstrings
- [ ] README updated (if applicable)
- [ ] Documentation updated (if applicable)
## Screenshots (if applicable)
Add screenshots to help reviewers understand UI changes.
## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Comments added for complex logic
- [ ] No commented-out code
- [ ] No debug statements or print calls
- [ ] All tests pass
- [ ] Coverage maintained or improved
## Additional Notes
Any additional information reviewers should know.
PR Size Guidelines¶
Keep pull requests focused and reviewable:
Ideal PR Size: - Small: 1-50 lines (quick review, easy to understand) - Medium: 50-200 lines (thorough review, manageable) - Large: 200-400 lines (detailed review, split if possible) - Too Large: 400+ lines (very hard to review, should be split)
Breaking Down Large PRs:
# Instead of one large PR
feature/complete-user-dashboard (800 lines)
# Split into multiple PRs
feature/user-dashboard-backend (200 lines)
feature/user-dashboard-api (150 lines)
feature/user-dashboard-frontend (250 lines)
feature/user-dashboard-tests (200 lines)
Draft Pull Requests¶
Use draft PRs for work-in-progress:
# Draft PR Title
🚧 WIP: Add user dashboard feature
## Status
This is a work in progress. Not ready for review yet.
## TODO
- [x] Backend models
- [x] API endpoints
- [ ] Frontend components
- [ ] Tests
- [ ] Documentation
## Early Feedback Welcome
Looking for feedback on the overall approach before completing.
Code Review Guidelines¶
For Authors¶
Preparing for Review:
- Self-review first:
- Read through your own diff
- Check for debug code, commented code
- Verify tests are included
-
Ensure documentation is updated
-
Provide context:
- Explain complex decisions
- Link to related issues
-
Add inline comments for tricky code
-
Request specific feedback:
-
Respond to feedback:
- Address all comments (even if just to acknowledge)
- Ask questions if feedback is unclear
- Mark conversations as resolved when addressed
- Push updates as new commits (don't force push during review)
During Review:
# Don't force push while review is ongoing
# Instead, add fixup commits
git commit -m "fixup: address review comments"
# After approval, clean up if desired
git rebase -i main
# Squash fixup commits
# Then push
git push --force-with-lease origin feature/my-feature
For Reviewers¶
Review Checklist:
Functionality: - [ ] Code does what the PR says it does - [ ] Edge cases are handled - [ ] Error handling is appropriate - [ ] No obvious bugs
Code Quality: - [ ] Code is readable and maintainable - [ ] Naming is clear and consistent - [ ] No unnecessary complexity - [ ] DRY principle followed - [ ] SOLID principles followed (where appropriate)
Testing: - [ ] Tests cover new functionality - [ ] Tests cover edge cases - [ ] Tests are clear and maintainable - [ ] No test-specific hacks in production code
Security: - [ ] No sensitive data in code - [ ] User input is validated - [ ] SQL injection prevented (use Django ORM properly) - [ ] XSS vulnerabilities addressed - [ ] Authentication/authorization correct
Performance: - [ ] No obvious performance issues - [ ] Database queries are efficient - [ ] N+1 query problems avoided - [ ] Appropriate use of caching
Django-Specific: - [ ] Migrations are included and tested - [ ] Settings changes are documented - [ ] Template changes follow conventions - [ ] URL patterns are correct - [ ] Forms include proper validation - [ ] Admin interface updated (if needed)
Review Feedback Format:
# Blocking Issues (must fix before merge)
🔴 **Security**: Line 45 - User input is not validated
# Non-Blocking Suggestions (nice to have)
🟡 **Suggestion**: Consider extracting this to a separate function
# Questions (seeking clarification)
❓ **Question**: Why did we choose approach X over Y?
# Praise (positive feedback)
✅ **Nice**: Great test coverage on this edge case!
Types of Review Comments:
- Blocking - Must be addressed before merge
- Non-blocking - Suggestions for improvement
- Nitpick - Minor style/preference issues
- Question - Seeking understanding
- FYI - Information, no action required
- Praise - Positive feedback (important!)
Review Response Times¶
Set expectations for review turnaround:
Small PRs (< 50 lines): - Initial review: 2-4 hours - Follow-up review: 1-2 hours
Medium PRs (50-200 lines): - Initial review: 4-8 hours (within same day) - Follow-up review: 2-4 hours
Large PRs (200+ lines): - Initial review: 1-2 business days - Follow-up review: 4-8 hours
Urgent PRs (hotfixes): - Initial review: 1 hour - Follow-up review: 30 minutes
Merge Strategies¶
Merge Commit¶
Creates a merge commit that preserves all individual commits:
Resulting History:
* Merge branch 'feature/user-auth' into main
|\
| * Add login view
| * Add User model
|/
* Previous commit on main
Advantages: - Preserves complete history - Shows when features were integrated - Easy to see what commits belong to a feature - Can revert entire feature with one commit
Disadvantages: - Creates merge commits (noisy history) - Can make history harder to follow - Not linear
Use When: - Working on long-running features - Want to preserve feature development history - Need ability to revert entire features easily - Team values complete historical context
Squash and Merge¶
Combines all commits into a single commit:
git checkout main
git merge --squash feature/user-auth
git commit -m "feat(auth): add user authentication"
Resulting History:
Advantages: - Clean, linear history - One commit per feature - Easy to understand history - Simplifies bisecting and reverting
Disadvantages: - Loses individual commit history - Can't see feature development progression - Large commits can be hard to review in history
Use When: - PRs have many small fixup commits - Want clean, linear history - Individual commits don't add value - Recommended for most Django projects
Rebase and Merge¶
Replays commits on top of main, then fast-forwards:
git checkout feature/user-auth
git rebase main
git checkout main
git merge --ff-only feature/user-auth
Resulting History:
Advantages: - Linear history (no merge commits) - Preserves individual commits - Clean, easy to follow - Makes bisecting easier
Disadvantages: - Rewrites commit history - Can cause conflicts during rebase - Requires more Git knowledge
Use When: - Commits are well-structured and meaningful - Team is comfortable with rebasing - Want linear history with detailed commits - Good for smaller teams with Git experience
Choosing a Strategy¶
Recommended Strategy by Project Size:
Solo Developer: - Use: Rebase and merge - Why: Clean history, full control
2-3 Person Team: - Use: Squash and merge - Why: Simple, clean history, easy to understand - Recommended for most Django teams
4-10 Person Team: - Use: Squash and merge (default) or Rebase and merge (for significant features) - Why: Balance between cleanliness and context
10+ Person Team: - Use: Merge commits - Why: Preserves feature context, supports parallel development
Our Recommendation for Django Projects:
# GitHub repository settings
merge_strategy: squash
require_linear_history: true
delete_branch_on_merge: true
Configure in GitHub: 1. Repository Settings → General 2. Pull Requests section 3. Enable: "Allow squash merging" 4. Disable: "Allow merge commits" and "Allow rebase merging" (or keep as options) 5. Enable: "Automatically delete head branches" 6. Enable: "Require linear history" (in branch protection)
Handling Conflicts¶
Preventing Conflicts¶
Best Practices: - Keep branches short-lived (days, not weeks) - Sync with main regularly (daily if possible) - Communicate with team about overlapping work - Use feature flags for large changes - Break large features into smaller PRs
Resolving Merge Conflicts¶
Step-by-step Resolution:
-
Update your branch:
-
Identify conflicts:
-
Resolve conflicts manually:
# myapp/models.py shows conflict markers class User(models.Model): username = models.CharField(max_length=150) <<<<<<< HEAD email = models.EmailField() # Your change ======= email_address = models.EmailField() # Their change >>>>>>> main # Resolve to: class User(models.Model): username = models.CharField(max_length=150) email = models.EmailField() # Choose one or merge both -
Mark as resolved:
-
Continue rebase/merge:
-
Test after resolution:
-
Force push (if rebased):
Conflict Resolution Tools¶
Git Mergetool:
# Configure merge tool (one time)
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# Use during conflict
git mergetool
IDE/Editor Support: - VS Code: Built-in 3-way merge editor - PyCharm: Integrated merge tool - Vim: vim-fugitive plugin - Emacs: ediff mode
Workflow Examples¶
Feature Development Workflow¶
Complete workflow for adding a new feature:
# 1. Create feature branch
git checkout main
git pull origin main
git checkout -b feature/user-dashboard
# 2. Implement feature
# ... make changes ...
git add .
git commit -m "feat(dashboard): add user dashboard view"
# 3. Add tests
# ... write tests ...
git add .
git commit -m "test(dashboard): add dashboard view tests"
# 4. Update documentation
# ... update docs ...
git add .
git commit -m "docs(dashboard): document dashboard API"
# 5. Sync with main
git fetch origin
git rebase origin/main
# 6. Run all checks
ruff format .
ruff check .
pytest
python manage.py check
# 7. Push to remote
git push -u origin feature/user-dashboard
# 8. Create pull request
# Use GitHub/GitLab UI
# 9. Address review feedback
# ... make changes ...
git add .
git commit -m "fix: address PR review comments"
git push origin feature/user-dashboard
# 10. After approval and merge
git checkout main
git pull origin main
git branch -d feature/user-dashboard
Hotfix Workflow¶
Urgent production fix workflow:
# 1. Create hotfix from main
git checkout main
git pull origin main
git checkout -b hotfix/security-patch
# 2. Implement fix
# ... make minimal changes ...
git add .
git commit -m "fix: patch security vulnerability CVE-2024-1234"
# 3. Test thoroughly
pytest
# Manual testing in production-like environment
# 4. Create PR with urgent label
# Push and create PR immediately
# 5. Fast-track review
# Get approval from senior developer
# 6. Merge and deploy
# Merge to main
# Deploy to production immediately
# 7. Backport to develop if needed
git checkout develop
git cherry-pick <hotfix-commit-sha>
git push origin develop
Release Workflow¶
Preparing and tagging a release:
# 1. Ensure main is ready
git checkout main
git pull origin main
# 2. Verify all tests pass
pytest
ruff check .
python manage.py check
# 3. Update version
# Update __version__ in myapp/__init__.py or setup.py
# 4. Update CHANGELOG
# Document changes in CHANGELOG.md
# 5. Commit version bump
git add .
git commit -m "chore: bump version to 1.2.0"
git push origin main
# 6. Create and push tag
git tag -a v1.2.0 -m "Release version 1.2.0"
git push origin v1.2.0
# 7. Create GitHub release
# Use GitHub UI or gh CLI:
gh release create v1.2.0 --title "Version 1.2.0" --notes "Release notes..."
# 8. Deploy to production
# Trigger deployment pipeline
Advanced Git Techniques¶
Interactive Rebase¶
Clean up commit history before merging:
# Rebase last 4 commits
git rebase -i HEAD~4
# In editor:
pick abc1234 feat: add user model
squash def5678 fix: typo in user model
reword ghi9012 feat: add user views
drop jkl3456 debug: temporary debug code
# Save and close editor
# Git will prompt for commit messages as needed
Interactive Rebase Commands:
- pick - Keep commit as-is
- reword - Keep commit, edit message
- edit - Keep commit, pause to amend
- squash - Merge into previous commit
- fixup - Like squash, discard message
- drop - Remove commit entirely
Cherry-Picking¶
Apply specific commits to another branch:
# Copy commit from feature branch to main
git checkout main
git cherry-pick abc1234
# Cherry-pick multiple commits
git cherry-pick abc1234 def5678 ghi9012
# Cherry-pick with edit
git cherry-pick -e abc1234
Stashing Changes¶
Temporarily save work without committing:
# Stash current changes
git stash
# Stash with message
git stash save "WIP: user dashboard feature"
# List stashes
git stash list
# Apply latest stash
git stash apply
# Apply and remove latest stash
git stash pop
# Apply specific stash
git stash apply stash@{2}
# Drop stash
git stash drop stash@{0}
# Clear all stashes
git stash clear
Bisecting¶
Find which commit introduced a bug:
# Start bisect
git bisect start
# Mark current commit as bad
git bisect bad
# Mark last known good commit
git bisect good v1.0.0
# Git checks out middle commit
# Test the commit
python manage.py test
# Mark as good or bad
git bisect good # or git bisect bad
# Repeat until bug is found
# Git will identify the problematic commit
# End bisect
git bisect reset
Reflog¶
Recover lost commits:
# View reflog
git reflog
# Output shows all HEAD movements:
# abc1234 HEAD@{0}: commit: feat: add feature
# def5678 HEAD@{1}: reset: moving to HEAD~1
# ghi9012 HEAD@{2}: commit: fix: bug fix
# Recover lost commit
git checkout ghi9012
git checkout -b recovery-branch
Git Configuration¶
Essential Git Config¶
# User identity
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
# Default branch name
git config --global init.defaultBranch main
# Default editor
git config --global core.editor "code --wait" # VS Code
# or
git config --global core.editor "vim"
# Line endings (important for cross-platform)
git config --global core.autocrlf input # Linux/Mac
git config --global core.autocrlf true # Windows
# Rebase on pull
git config --global pull.rebase true
# Prune on fetch
git config --global fetch.prune true
# Show short status
git config --global status.short true
# Color output
git config --global color.ui auto
Useful Git Aliases¶
Add to ~/.gitconfig:
[alias]
# Short status
st = status -sb
# Clean log
lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
# Amend without editing message
amend = commit --amend --no-edit
# Undo last commit (keep changes)
undo = reset HEAD~1 --soft
# Discard all local changes
nuke = reset --hard HEAD
# List branches sorted by last modified
branches = branch --sort=-committerdate
# Show what's changed
changed = diff --name-status
# Clean up merged branches
cleanup = "!git branch --merged main | grep -v '\\* main' | xargs -n 1 git branch -d"
Best Practices Summary¶
✅ Do¶
- Keep commits atomic - One logical change per commit
- Write clear commit messages - Follow Conventional Commits
- Review your own PR first - Catch obvious issues
- Keep PRs small - Easier to review, faster to merge
- Update branches regularly - Sync with main daily
- Delete merged branches - Keep repository clean
- Use draft PRs - For work in progress
- Respond to reviews promptly - Don't block teammates
- Test before pushing - Run tests and linters locally
- Use descriptive branch names - Clear purpose and scope
❌ Don't¶
- Don't force push to shared branches
- Don't commit secrets or credentials
- Don't commit commented-out code
- Don't commit merge conflicts
- Don't use generic commit messages ("fix", "update")
- Don't create giant pull requests (400+ lines)
- Don't leave PRs open for weeks
- Don't merge your own PRs without review
- Don't commit directly to main
- Don't ignore CI failures
Team Workflow Agreement¶
Document team conventions in CONTRIBUTING.md:
# Contributing to [Project Name]
## Git Workflow
- Create feature branches from `main`
- Use descriptive branch names: `feature/`, `fix/`, `refactor/`
- Follow Conventional Commits for messages
- Keep PRs under 200 lines when possible
- Require 1 approval before merging
- Use squash and merge strategy
- Delete branches after merge
## Before Creating PR
1. Run `ruff format .`
2. Run `ruff check .`
3. Run `pytest`
4. Run `python manage.py check`
5. Update tests and documentation
## PR Review
- Review within 1 business day
- Provide constructive feedback
- Approve when satisfied
- Address all review comments
Next Steps¶
- Gitignore Configuration - Essential file exclusions
- Pre-commit Hooks - Automated code quality checks
- CI/CD Overview - Continuous integration and deployment
- Code Review Best Practices - Security-focused reviews