07 - Practical Exercises: Bài Tập Thực Hành
Đối tượng: Tất cả thành viên team Thời gian: 2-4 giờ Mục tiêu: Thực hành các kỹ năng Git đã học
📖 Mục Lục
- Chuẩn Bị Môi Trường
- Bài Tập Level Junior
- Bài Tập Level Middle
- Bài Tập Level Senior/Git Master
- Kịch Bản Team 3-5 Người
1. Chuẩn Bị Môi Trường
1.1. Tạo Practice Repository
# Tạo repo mới để thực hành
mkdir git-practice
cd git-practice
git init
# Tạo file ban đầu
echo "# Git Practice Repo" > README.md
echo "console.log('Hello Git');" > app.js
# Commit đầu tiên
git add .
git commit -m "Initial commit"
# Tạo remote (nếu có GitHub/GitLab)
# git remote add origin [email protected]:your-username/git-practice.git
# git push -u origin main
1.2. Setup file để thực hành conflict
# Tạo file có nhiều dòng để dễ tạo conflict
cat > calculator.js << 'EOF'
// Calculator module
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
return a / b;
}
module.exports = { add, subtract, multiply, divide };
EOF
git add calculator.js
git commit -m "Add calculator module"
2. Bài Tập Level Junior
🎯 Bài 1: Clone và Commit Đầu Tiên
Mục tiêu: Làm quen với flow cơ bản
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 1 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Nhiệm vụ: │
│ 1. Clone repo từ GitHub (hoặc tạo mới) │
│ 2. Tạo file hello.txt với nội dung "Hello, Git!" │
│ 3. Commit file │
│ 4. Xem lịch sử commit │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Bước 1: Clone hoặc tạo repo
git clone <repo-url>
# hoặc
mkdir my-repo && cd my-repo && git init
# Bước 2: Tạo file
echo "Hello, Git!" > hello.txt
# Bước 3: Kiểm tra status
git status
# Bước 4: Add và commit
git add hello.txt
git commit -m "feat: add hello.txt file"
# Bước 5: Xem lịch sử
git log --oneline
Kết quả mong đợi:
$ git log --oneline
abc1234 (HEAD -> main) feat: add hello.txt file
🎯 Bài 2: Tạo Branch và Merge
Mục tiêu: Hiểu flow branch cơ bản
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 2 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Nhiệm vụ: │
│ 1. Tạo branch mới: feat/add-greeting │
│ 2. Thêm function greeting() vào app.js │
│ 3. Commit │
│ 4. Chuyển về main │
│ 5. Merge branch vào main │
│ 6. Xóa branch │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Bước 1: Tạo và chuyển sang branch mới
git checkout -b feat/add-greeting
# Bước 2: Thêm function vào app.js
cat >> app.js << 'EOF'
function greeting(name) {
return `Hello, ${name}!`;
}
EOF
# Bước 3: Commit
git add app.js
git commit -m "feat: add greeting function"
# Bước 4: Chuyển về main
git checkout main
# Bước 5: Merge
git merge feat/add-greeting
# Bước 6: Xóa branch
git branch -d feat/add-greeting
# Kiểm tra
git log --oneline --graph
Kết quả mong đợi:
* def5678 (HEAD -> main) feat: add greeting function
* abc1234 Initial commit
🎯 Bài 3: Sử Dụng Git Stash
Mục tiêu: Biết cách lưu tạm công việc
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 3 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Kịch bản: Đang code dở, có bug cần fix gấp │
│ │
│ Nhiệm vụ: │
│ 1. Đang ở feat/new-feature, code dở │
│ 2. Stash code │
│ 3. Chuyển sang fix/urgent-bug, sửa bug │
│ 4. Quay lại feat/new-feature │
│ 5. Lấy code từ stash │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Setup: Tạo và sửa file (giả lập đang code dở)
git checkout -b feat/new-feature
echo "// Work in progress..." >> app.js
# Bước 1: Xem status
git status # Modified: app.js
# Bước 2: Stash
git stash save "WIP: new feature"
# Bước 3: Chuyển sang fix bug
git checkout main
git checkout -b fix/urgent-bug
echo "// Bug fixed" >> app.js
git add app.js
git commit -m "fix: urgent bug"
git checkout main
git merge fix/urgent-bug
git branch -d fix/urgent-bug
# Bước 4: Quay lại feature
git checkout feat/new-feature
# Bước 5: Lấy lại code
git stash pop
# Xem stash list (phải rỗng)
git stash list
🎯 Bài 4: Undo Mistakes
Mục tiêu: Biết cách sửa sai
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 4 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Nhiệm vụ: │
│ 1. Sửa file và commit │
│ 2. Nhận ra commit sai → Undo commit (giữ changes) │
│ 3. Sửa lại và commit đúng │
│ 4. Nhận ra message sai → Sửa message │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Tạo commit sai
echo "wrong code" >> app.js
git add app.js
git commit -m "wrong commit message"
# Undo commit (giữ changes)
git reset --soft HEAD~1
# Kiểm tra - file vẫn staged
git status
# Sửa lại code
echo "correct code" > temp.txt && cat temp.txt >> app.js && rm temp.txt
# Commit lại với message sai
git commit -m "feat: add feture"
# Sửa message (chưa push)
git commit --amend -m "feat: add feature"
# Kiểm tra
git log --oneline -1
3. Bài Tập Level Middle
🎯 Bài 5: Xử Lý Conflict
Mục tiêu: Thành thạo resolve conflict
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 5 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Nhiệm vụ: Tạo và resolve conflict │
│ │
│ 1. Từ main, tạo branch feat/change-add │
│ 2. Sửa function add() trong calculator.js │
│ 3. Quay về main, cũng sửa function add() │
│ 4. Merge feat/change-add → Conflict! │
│ 5. Resolve conflict │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Bước 1: Tạo branch và sửa add()
git checkout -b feat/change-add
# Sửa calculator.js - đổi function add
sed -i 's/return a + b;/return a + b; \/\/ Version A/' calculator.js
git add calculator.js
git commit -m "feat: update add function - version A"
# Bước 2: Quay về main và sửa cùng dòng
git checkout main
sed -i 's/return a + b;/return a + b; \/\/ Version B/' calculator.js
git add calculator.js
git commit -m "feat: update add function - version B"
# Bước 3: Merge - sẽ conflict!
git merge feat/change-add
# CONFLICT (content): Merge conflict in calculator.js
# Bước 4: Xem conflict
cat calculator.js
# Bước 5: Resolve - chọn cách kết hợp
# Mở file và sửa thủ công, xóa markers
# Hoặc:
git checkout --theirs calculator.js # Chọn version A
# Hoặc:
git checkout --ours calculator.js # Chọn version B
# Bước 6: Complete merge
git add calculator.js
git commit -m "Merge feat/change-add: resolve conflict"
🎯 Bài 6: Interactive Rebase
Mục tiêu: Biết squash và sửa commits
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 6 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Nhiệm vụ: │
│ 1. Tạo 4 commits nhỏ │
│ 2. Dùng interactive rebase để: │
│ - Squash 3 commits đầu thành 1 │
│ - Reword commit cuối │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Tạo 4 commits
git checkout -b feat/multi-commits
echo "line 1" >> multi.txt && git add . && git commit -m "WIP: add line 1"
echo "line 2" >> multi.txt && git add . && git commit -m "WIP: add line 2"
echo "line 3" >> multi.txt && git add . && git commit -m "WIP: add line 3"
echo "line 4" >> multi.txt && git add . && git commit -m "add line 4 typo"
# Xem log
git log --oneline -4
# Interactive rebase 4 commits gần nhất
git rebase -i HEAD~4
# Editor sẽ mở:
# pick abc1234 WIP: add line 1
# pick def5678 WIP: add line 2
# pick ghi9012 WIP: add line 3
# pick jkl3456 add line 4 typo
# Sửa thành:
# pick abc1234 WIP: add line 1
# squash def5678 WIP: add line 2
# squash ghi9012 WIP: add line 3
# reword jkl3456 add line 4 typo
# Sau đó:
# - Nhập message mới cho squashed commit
# - Nhập message mới cho commit cuối
# Kiểm tra
git log --oneline -2
🎯 Bài 7: Cherry-pick
Mục tiêu: Biết lấy commit cụ thể từ branch khác
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 7 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Kịch bản: │
│ - Branch develop có bug fix quan trọng │
│ - Cần lấy chỉ bug fix đó vào main (không merge cả branch) │
│ │
│ Nhiệm vụ: │
│ 1. Tạo branch với nhiều commits │
│ 2. Cherry-pick chỉ 1 commit vào main │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Tạo branch với nhiều commits
git checkout -b develop
echo "feature 1" >> features.txt && git add . && git commit -m "feat: add feature 1"
echo "bug fix" >> bugfix.txt && git add . && git commit -m "fix: critical bug fix"
echo "feature 2" >> features.txt && git add . && git commit -m "feat: add feature 2"
# Lấy hash của commit cần cherry-pick
git log --oneline
# yyy fix: critical bug fix ← Cần commit này
# Chuyển về main
git checkout main
# Cherry-pick
git cherry-pick <commit-hash>
# Kiểm tra
git log --oneline
# main bây giờ có bug fix, không có feature 1, 2
4. Bài Tập Level Senior/Git Master
🎯 Bài 8: Revert và Reset Nâng Cao
Mục tiêu: Thành thạo undo trong mọi tình huống
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 8 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Nhiệm vụ: │
│ 1. Tạo 5 commits trên main │
│ 2. Reset về commit thứ 3 (soft, mixed, hard) │
│ 3. Recover commits đã reset (reflog) │
│ 4. Revert commit thứ 4 (tạo revert commit) │
│ 5. Revert một merge commit │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Setup: Tạo 5 commits
git checkout -b test-reset
for i in 1 2 3 4 5; do
echo "commit $i" >> history.txt
git add . && git commit -m "Commit $i"
done
# Xem log
git log --oneline
# e5 Commit 5 (HEAD)
# d4 Commit 4
# c3 Commit 3
# b2 Commit 2
# a1 Commit 1
# --- RESET SOFT ---
# Undo commit 5, 4 nhưng giữ changes staged
git reset --soft HEAD~2
git status # Changes staged
git log --oneline # HEAD ở Commit 3
# Commit lại
git commit -m "Commit 4 and 5 combined"
# --- RESET MIXED (default) ---
git reset HEAD~1
git status # Changes NOT staged
git add . && git commit -m "Re-commit"
# --- RESET HARD (cẩn thận!) ---
git reset --hard HEAD~1
# Changes DELETED!
# --- RECOVER với REFLOG ---
git reflog
# abc1234 HEAD@{0}: reset: moving to HEAD~1
# def5678 HEAD@{1}: commit: Re-commit ← Muốn về đây
git reset --hard def5678 # Recover!
# --- REVERT (tạo undo commit) ---
# Revert commit cụ thể
git revert HEAD~1 --no-edit
# --- REVERT MERGE ---
# Giả sử có merge commit
git checkout main
git merge test-reset
# Revert merge (phải chỉ định parent)
git revert -m 1 HEAD # -m 1 = giữ main, undo merge
🎯 Bài 9: Git Bisect (Tìm Bug)
Mục tiêu: Dùng binary search tìm commit gây bug
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 9 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Kịch bản: │
│ - App bị bug, không biết từ commit nào │
│ - Biết commit A (cũ) hoạt động OK │
│ - Commit Z (mới) bị bug │
│ │
│ Nhiệm vụ: Dùng bisect tìm commit gây bug │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# Setup: Tạo history với bug ở giữa
git checkout -b test-bisect
# Commits 1-3: OK
for i in 1 2 3; do
echo "good $i" >> app.txt
git add . && git commit -m "Good commit $i"
done
# Commit 4: Introduce bug!
echo "BUG" >> app.txt
git add . && git commit -m "Bad commit - introduces bug"
# Commits 5-7: Cũng bad (vì bug vẫn còn)
for i in 5 6 7; do
echo "more code $i" >> app.txt
git add . && git commit -m "Commit $i"
done
# Bây giờ tìm bug với bisect
git bisect start
git bisect bad # Commit hiện tại (7) là bad
git bisect good HEAD~6 # Commit 1 là good
# Git sẽ checkout commit giữa
# Kiểm tra xem có bug không (tìm "BUG" trong file)
grep -q "BUG" app.txt && echo "BAD" || echo "GOOD"
# Nếu bad:
git bisect bad
# Nếu good:
git bisect good
# Lặp lại cho đến khi tìm được commit gây bug
# Output: abc1234 is the first bad commit
# Kết thúc bisect
git bisect reset
🎯 Bài 10: Cleanup Repository
Mục tiêu: Dọn dẹp repo lớn, cũ
┌─────────────────────────────────────────────────────────────────────┐
│ BÀI TẬP 10 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Nhiệm vụ: │
│ 1. Xóa tất cả branches đã merge │
│ 2. Xóa file lớn khỏi history (filter-branch) │
│ 3. Garbage collection │
│ │
└─────────────────────────────────────────────────────────────────────┘
Hướng dẫn:
# 1. Xóa branches đã merge
git checkout main
git branch --merged | grep -v "main\|develop" | xargs git branch -d
# 2. Xóa file lớn khỏi history (CẢNH BÁO: Thay đổi history!)
# Giả sử có file large.zip đã commit nhầm
# Cách mới với git-filter-repo (recommended)
# pip install git-filter-repo
git filter-repo --path large.zip --invert-paths
# Cách cũ với filter-branch
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch large.zip' \
--prune-empty --tag-name-filter cat -- --all
# 3. Garbage collection
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# Kiểm tra size
du -sh .git
5. Kịch Bản Team 3-5 Người
🎯 Simulation: Sprint Development
┌─────────────────────────────────────────────────────────────────────┐
│ KỊCH BẢN TEAM 5 NGƯỜI │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Vai trò: │
│ - Person A: Tech Lead / Git Master │
│ - Person B: Senior Developer │
│ - Person C: Developer │
│ - Person D: Developer │
│ - Person E: Junior Developer │
│ │
│ Sprint Goal: Build simple Todo App │
│ │
│ Tasks: │
│ 1. [Person B] feat/todo-model - Todo data model │
│ 2. [Person C] feat/todo-api - Todo API endpoints │
│ 3. [Person D] feat/todo-ui - Todo UI components │
│ 4. [Person E] feat/todo-list - Todo list component │
│ 5. [Person A] Review và merge tất cả │
│ │
└─────────────────────────────────────────────────────────────────────┘
Timeline
Ngày 1 (Setup):
────────────────────────────────────────────────────────────────────
Person A:
$ git init todo-app
$ git checkout -b develop
$ echo "# Todo App" > README.md
$ git add . && git commit -m "Initial commit"
$ git push -u origin main
$ git push -u origin develop
All:
$ git clone <repo-url>
$ git checkout develop
Ngày 2-3 (Development):
────────────────────────────────────────────────────────────────────
Person B (Todo Model):
$ git checkout develop && git pull
$ git checkout -b feat/todo-model
$ # ... code model ...
$ git add . && git commit -m "feat: add Todo model with CRUD"
$ git push -u origin feat/todo-model
$ # Tạo PR → develop
Person C (Todo API):
$ git checkout develop && git pull
$ git checkout -b feat/todo-api
$ # ... code API ...
$ git add . && git commit -m "feat: add REST API for todos"
$ git push -u origin feat/todo-api
$ # Tạo PR → develop
Person D (Todo UI):
$ git checkout develop && git pull
$ git checkout -b feat/todo-ui
$ # ... code UI ...
$ git add . && git commit -m "feat: add TodoItem component"
$ git push -u origin feat/todo-ui
$ # Tạo PR → develop
Person E (Todo List):
$ # Đợi UI merge trước
$ git checkout develop && git pull # Có UI components
$ git checkout -b feat/todo-list
$ # ... code list ...
$ git add . && git commit -m "feat: add TodoList component"
$ git push -u origin feat/todo-list
$ # Tạo PR → develop
Ngày 4 (Merge và Conflict):
────────────────────────────────────────────────────────────────────
Person A (Review):
- Review PR #1 (Model) → Approve → Merge
- Review PR #2 (API) → Request changes
"Cần thêm validation cho todo title"
Person C (Fix):
$ git checkout feat/todo-api
$ # ... fix theo feedback ...
$ git add . && git commit -m "feat: add title validation"
$ git push
# PR tự động cập nhật
Person A:
- Re-review PR #2 → Approve → Merge
- Review PR #3 (UI) → Conflict với develop!
Person D (Resolve conflict):
$ git checkout feat/todo-ui
$ git merge origin/develop
# CONFLICT!
$ # ... resolve ...
$ git add . && git commit -m "Merge develop, resolve conflicts"
$ git push
# PR không còn conflict
Person A:
- Merge PR #3
- Review và merge PR #4
Ngày 5 (Release):
────────────────────────────────────────────────────────────────────
Person A:
$ git checkout develop && git pull
$ git checkout main
$ git merge develop
$ git tag -a v1.0.0 -m "Release version 1.0.0"
$ git push origin main --tags
All:
$ git checkout develop && git pull
$ git checkout main && git pull
$ git branch -d feat/xxx # Xóa local branches
Conflict Scenarios trong Simulation
Scenario 1: Person C và D cùng sửa package.json
──────────────────────────────────────────────────
Person C: Thêm dependency express
Person D: Thêm dependency react
Solution:
- Person merge trước không có vấn đề
- Person merge sau resolve bằng cách thêm cả 2 dependencies
Scenario 2: Person E làm việc trên code chưa merge
──────────────────────────────────────────────────
Person E cần UI components từ Person D
Nhưng Person D chưa merge
Solution A:
- E đợi D merge
- Pull develop
- Tiếp tục làm
Solution B (nếu gấp):
- E tạo branch từ D's branch
$ git checkout feat/todo-ui
$ git checkout -b feat/todo-list
- Sau khi D merge, rebase:
$ git rebase develop
Scenario 3: Bug phát hiện sau merge
──────────────────────────────────────────────────
Person A phát hiện bug trong todo-model
Solution:
$ git checkout develop
$ git checkout -b fix/todo-model-bug
$ # ... fix ...
$ git push -u origin fix/todo-model-bug
$ # Tạo PR → develop
📋 Checklist Hoàn Thành
Junior Level
□ Bài 1: Clone và Commit
□ Bài 2: Tạo Branch và Merge
□ Bài 3: Git Stash
□ Bài 4: Undo Mistakes
Middle Level
□ Bài 5: Xử Lý Conflict
□ Bài 6: Interactive Rebase
□ Bài 7: Cherry-pick
Senior/Git Master Level
□ Bài 8: Revert và Reset Nâng Cao
□ Bài 9: Git Bisect
□ Bài 10: Cleanup Repository
□ Team Simulation completed
➡️ Bước Tiếp Theo
Học file 08-advanced-git.md - Git Nâng Cao
Tài liệu thuộc bộ Git Training Documentation v1.0