Hands-on Lab TP5 — Pull Requests & Team Workflows
Hands-on Lab 75 min Module 04
Objectives
By the end of this lab, you will have:
- Configured a GitHub repository with protected branches
- Applied GitHub Flow on a real project
- Created professional PRs with descriptions and labels
- Performed a self-review and addressed feedback
- Merged PRs using different strategies (squash, merge)
- Simulated a complete team workflow with multiple PRs
Setup
mkdir git-tp5-collaboration
cd git-tp5-collaboration
git init
echo "*.pyc" > .gitignore
echo "__pycache__/" >> .gitignore
echo ".env" >> .gitignore
Create app.py:
"""Task management application."""
TASKS = []
def create_task(title, priority="medium"):
"""Create a new task."""
task = {
"id": len(TASKS) + 1,
"title": title,
"priority": priority,
"completed": False
}
TASKS.append(task)
return task
def get_tasks(completed=None):
"""Get all tasks, optionally filtered by status."""
if completed is None:
return TASKS
return [t for t in TASKS if t["completed"] == completed]
def complete_task(task_id):
"""Mark a task as completed."""
for task in TASKS:
if task["id"] == task_id:
task["completed"] = True
return task
raise ValueError(f"Task {task_id} not found")
Create README.md:
# Task Manager
A simple task management application.
## Features
- Create tasks with priorities
- List tasks (all, pending, or completed)
- Mark tasks as completed
git add .
git commit -m "feat: initial task manager application"
gh repo create git-tp5-collaboration --public
git push -u origin main
Part 1: Configure Protected Branch
gh api repos/:owner/git-tp5-collaboration/branches/main/protection \
--method PUT \
--field required_status_checks='{"strict":true,"contexts":[]}' \
--field enforce_admins=false \
--field required_pull_request_reviews='{"required_approving_review_count":1}' \
--field restrictions=null
Or manually on GitHub:
- Settings → Branches → Add rule
- Pattern:
main - Check: "Require a pull request before merging"
- Required approvals: 1
- Save
Part 2: Feature 1 — Search and Priority Filter
git switch -c feature/search-filter
Add these functions to the end of app.py:
def search_tasks(query):
"""Search tasks by title (case-insensitive)."""
query = query.lower()
return [t for t in TASKS if query in t["title"].lower()]
def get_tasks_by_priority(priority):
"""Get tasks filtered by priority level."""
valid_priorities = ["low", "medium", "high", "critical"]
if priority not in valid_priorities:
raise ValueError(f"Priority must be one of: {valid_priorities}")
return [t for t in TASKS if t["priority"] == priority]
git add app.py
git commit -m "feat(search): add search and priority filter functions"
Create test_app.py:
"""Tests for task manager."""
from app import create_task, get_tasks, complete_task, search_tasks, get_tasks_by_priority, TASKS
def setup():
"""Clear tasks before each test."""
TASKS.clear()
def test_create_task():
setup()
task = create_task("Write tests", "high")
assert task["title"] == "Write tests"
assert task["priority"] == "high"
assert task["completed"] == False
def test_search():
setup()
create_task("Write documentation")
create_task("Fix login bug")
create_task("Write tests")
results = search_tasks("write")
assert len(results) == 2
def test_priority_filter():
setup()
create_task("Task 1", "high")
create_task("Task 2", "low")
create_task("Task 3", "high")
high_priority = get_tasks_by_priority("high")
assert len(high_priority) == 2
print("Tests pass!")
git add test_app.py
git commit -m "test: add tests for search and priority filter"
git push -u origin feature/search-filter
Create the Pull Request
gh pr create \
--title "feat(search): add search and priority filter" \
--body "Adds search by title and priority filter functions. Tests included." \
--base main \
--head feature/search-filter
Part 3: Feature 2 — Statistics
git switch main
git switch -c feature/statistics
Add this function to the end of app.py:
def get_statistics():
"""Return task statistics."""
total = len(TASKS)
if total == 0:
return {"total": 0, "completed": 0, "pending": 0, "completion_rate": 0.0}
completed = len([t for t in TASKS if t["completed"]])
pending = total - completed
completion_rate = (completed / total) * 100
priority_counts = {}
for task in TASKS:
p = task["priority"]
priority_counts[p] = priority_counts.get(p, 0) + 1
return {
"total": total,
"completed": completed,
"pending": pending,
"completion_rate": round(completion_rate, 1),
"by_priority": priority_counts
}
git add app.py
git commit -m "feat(stats): add task statistics function"
git push -u origin feature/statistics
gh pr create \
--title "feat(stats): add task statistics" \
--body "Adds get_statistics() returning totals, completion rate, and priority breakdown." \
--base main \
--head feature/statistics
Part 4: Review and Respond to Feedback
gh pr list
gh pr view 1
gh pr diff 1
Simulate a Review Comment
gh pr review 1 --comment \
--body "Good work! Suggestion: add validation for empty query string in search_tasks."
Address the Feedback
Update the search_tasks function in app.py:
def search_tasks(query):
"""Search tasks by title (case-insensitive)."""
if not query or not query.strip():
raise ValueError("Search query cannot be empty")
query = query.lower().strip()
return [t for t in TASKS if query in t["title"].lower()]
git switch feature/search-filter
git add app.py
git commit -m "fix(search): add empty query validation based on review feedback"
git push
Approve and Merge
gh pr review 1 --approve --body "Good fix! Empty validation is important."
gh pr merge 1 --squash --delete-branch
git switch main
git pull
git switch feature/statistics
git rebase origin/main
git push --force-with-lease
Part 5: Hotfix Workflow
git switch main
git switch -c hotfix/divide-by-zero-stats
The get_statistics() function already handles empty task list. Simulate a fix anyway:
git add app.py
git commit -m "fix(stats): add explicit empty list guard in statistics"
git push -u origin hotfix/divide-by-zero-stats
gh pr create \
--title "fix(stats): handle empty task list - HOTFIX" \
--body "Ensures get_statistics() never divides by zero. Fixes #5" \
--label "bug" \
--base main
gh pr review 3 --approve
gh pr merge 3 --merge --delete-branch
Part 6: View Final State
git switch main
git pull
git log --oneline --graph
gh pr list --state merged
gh repo view
Validation Checklist
- At least 3 PRs created with complete descriptions
- Branch protection is configured on
main - At least one PR had a review comment addressed
- Both merge strategies used (squash and merge commit)
- Hotfix PR created with appropriate urgency label
- Clean history on
mainafter all merges
Summary
You've simulated a professional team workflow:
- Protected branches prevent direct pushes to main
- Features developed in isolation on separate branches
- PRs serve as documentation AND code review
- Feedback addressed in new commits before merging
- Different merge strategies for different contexts
- Hotfix workflow for urgent production issues
Ready for Module 05 — Advanced Git!