Pular para o conteúdo principal

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:

  1. Settings → Branches → Add rule
  2. Pattern: main
  3. Check: "Require a pull request before merging"
  4. Required approvals: 1
  5. 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 main after all merges

Summary

You've simulated a professional team workflow:

  1. Protected branches prevent direct pushes to main
  2. Features developed in isolation on separate branches
  3. PRs serve as documentation AND code review
  4. Feedback addressed in new commits before merging
  5. Different merge strategies for different contexts
  6. Hotfix workflow for urgent production issues

Ready for Module 05 — Advanced Git!