When contributing to open source projects or working on team projects Fork·PR(Pull Request)·Issue·Code Review are the core flows of collaboration. In this section, you will understand GitHub’s UI-centric features and Learn how to protect code quality with branch protection rules and CODEOWNERS settings.
Learning Objectives
- The fork → branch → PR flow is explained and can be performed directly.
- Issues can be used to plan work and connect with PR.
- You can leave comments and reflect requested changes in Code Review.
- The main branch can be protected from direct push with branch protection rules.
- You can automatically designate a specific of-file/directory reviewer with the CODEOWNERS file.
Key concepts
1) Fork & PR flow
Original repository (upstream)
↓ fork
my repository (origin)
↓ clone
local operation
↓ git switch -c feature/xxx
↓ commit
↓ git push origin feature/xxx
Create a PR from the GitHub UI
↓ upstream main ← feature/xxx
Code Review → Approval → Merge
2) Issue creation and PR connection
Include the issue number (e.g. #42) in your PR description or commit message and it will be automatically linked.
# PR description example
Closes #42
# Example commit message
fix: resolve login error (#42)
Issue is automatically closed when merging PRs when using keywords Closes, Fixes, Resolves.
3) Code Review word collection
| Abbreviations | Meaning |
|---|---|
| LGTM | Looks Good To Me (Approved) |
| nit | Minor improvement suggestions |
| RFC | Request for Comments |
| WIP | Work In Progress |
| NB | Nota Bene (notice) |
4) Branch protection rules
GitHub repository → Settings → Branches → Add rule
Branch name pattern: main
✅ Require a pull request before merging
✅ Require approvals: 1
✅ Dismiss stale pull request approvals
✅ Require status checks to pass
✅ Restrict who can push to matching branches
5) CODEOWNERS
.github/CODEOWNERS file:
# Default owner of the entire repository
* @username
# The docs directory belongs to the technical-writers team
docs/ @org/technical-writers
# Security-related files
security.md @security-team
See as example
Example 1 — ex01_fork.sh: fork workflow simulation (using bare repository)
#!/usr/bin/env bash
#GitHub fork is a UI operation, so it simulates the flow with a local bare repository.
WORKDIR=$(mktemp -d)
git init --bare "$WORKDIR/upstream.git" -q
git init --bare "$WORKDIR/fork.git" -q #my fork role
#upstream initialization
git clone "$WORKDIR/upstream.git" "$WORKDIR/upstream_local" -q
cd "$WORKDIR/upstream_local"
git config user.name "maintainer" && git config user.email "[email protected]"
echo "#project" > README.md
git add . && git commit -q -m "init"
git push origin main -q
#Synchronize with fork (fork in GitHub = upstream clone push)
git clone "$WORKDIR/upstream.git" "$WORKDIR/my_fork" -q
cd "$WORKDIR/my_fork"
git config user.name "contributor" && git config user.email "[email protected]"
git remote add upstream "$WORKDIR/upstream.git"
#Working on feature branches
git switch -c feature/add-docs
echo "## How to use" >> README.md
git add . && git commit -q -m "docs: add usage section"
echo "=== feature branch log ==="
git log --oneline --graph --all
rm -rf "$WORKDIR"
Execution result
=== feature branch log ===
* a1b2c3d (HEAD -> feature/add-docs) docs: add usage section
* b2c3d4e (origin/main, main) init
Key point: The open source flow is to work on the feature branch after forking and contribute upstream with PR.
Example 2 — ex02_pr.sh: PR simulation (replaced by local merge)
#!/usr/bin/env bash
WORKDIR=$(mktemp -d)
git init --bare "$WORKDIR/origin.git" -q
git clone "$WORKDIR/origin.git" "$WORKDIR/repo" -q
cd "$WORKDIR/repo"
git config user.name "developer" && git config user.email "[email protected]"
echo "main function" > main.py && git add . && git commit -q -m "init"
git push origin main -q
git switch -c feature/login
echo "def login(): pass" >> main.py
git add . && git commit -q -m "feat: add login function"
git push origin feature/login -q
echo "=== PR simulation: feature/login → main merge ==="
git switch main
git merge --no-ff feature/login -m "merge: PR#1 add login function"
git push origin main -q
echo ""
echo "=== Log after merge ==="
git log --oneline --graph
rm -rf "$WORKDIR"
Execution result
=== PR simulation: feature/login → main merge ===
=== Log after merge ===
* c3d4e5f merge: PR#1 add login function
|\
| * b2c3d4e feat: add login function
|/
* a1b2c3d init
Bottom line: PR is a safety measure that merges branches after review rather than pushing them directly.
Example 3 — ex03_issue.sh : Concatenate issue number to commit message
#!/usr/bin/env bash
REPO=$(mktemp -d)
cd "$REPO"
git init -q
git config user.name "developer" && git config user.email "[email protected]"
echo "app code" > app.py && git add . && git commit -q -m "init"
#Issue #7: Login bug fix
git switch -c fix/issue-7
echo "# Bug fixed" >> app.py
git add .
git commit -m "fix: resolve login error
Closes #7"
echo "=== Check commit message ==="
git log --oneline -n 1
git show HEAD --format="%B" --no-patch
rm -rf "$REPO"
Execution result
=== Check commit message ===
a1b2c3d fix: resolve login error
fix: resolve login error
Closes #7
Key point: If you use Closes #N in the PR description, the issue will be automatically closed when merging.
Example 4 — ex04_codeowners.sh: Creating CODEOWNERS file
#!/usr/bin/env bash
REPO=$(mktemp -d)
cd "$REPO"
git init -q
git config user.name "manager" && git config user.email "[email protected]"
mkdir -p .github docs src
cat > .github/CODEOWNERS << 'EOF'
#Default owner: All files
* @admin-user
#document directory
docs/ @tech-writer
#source code
src/ @backend-team
#CI/CD settings
.github/workflows/ @devops-team
EOF
echo "app code" > src/app.py
echo "document" > docs/guide.md
git add .
git commit -m "chore: add CODEOWNERS and project structure"
echo "=== CODEOWNERS contents ==="
cat .github/CODEOWNERS
rm -rf "$REPO"
Execution result
=== CODEOWNERS contents ===
# Default owner: All files
* @admin-user
...
Key point: CODEOWNERS automatically requests the Team/Individual reviewer specified when creating a PR.
Seeing from a different perspective
| GitHub features | Metaphor |
|---|---|
| Issue | to-do notes |
| PR | Request approval from superior |
| Code Review | Payment review |
| LGTM / Approve | Payment stamp |
| Merge | Official reflection |
| CODEOWNERS | List of contact persons by department |
Common Mistakes
- Push directly to main without PR — Without branch protection rules, team member review will be skipped.
- Creating a PR without an Issue — Reviewing is difficult because there is no context as to why this work is needed.
Closes #Nomitted in PR description — Issue is not automatically closed, making management cumbersome.- Omit upstream synchronization in fork repository — Conflicts increase due to no upstream changes.
- Do not resolve all review comments and merge — unresolved feedback remains in the code.
Cleanup
- fork → feature branch → PR → review → Merge is the standard flow of team collaboration.
- By linking Issue and PR to
Closes #N, task tracking is automated. - Branch protection rules prevent direct pushes to main and enforce a minimum number of reviewers.
- Designate the By-file/directory person in charge as CODEOWNERS and receive automatic review requests.
Try it yourself
cd 05_GitHub_Collaboration/src
chmod +x *.sh
./ex01_fork.sh
./ex02_pr.sh
./ex03_issue.sh
./ex04_codeowners.sh
Application:
- Fork the actual GitHub repository and upload a PR for correcting typos.
- Create an issue template (
.github/ISSUE_TEMPLATE/) to create a bug report form.