1. Vì sao Git?
Năm 2005, Linus Torvalds mất quyền dùng BitKeeper (proprietary VCS lúc đó cho Linux kernel). Ông code Git trong 2 tuần. 20 năm sau, Git là VCS (version control system) phổ biến nhất, gần như độc tôn.
Git khác các VCS cũ (CVS, SVN) ở 3 điểm:
- Distributed — mỗi clone là full repository có history. Không cần network để commit, branch, diff.
- Nhanh — operation chỉ trên local file system, không network roundtrip.
- Branching cheap — branch là 1 pointer 40 byte, tạo branch mất 1ms. Khuyến khích branching workflow.
1.1. Vì sao DevOps quan tâm Git?
Git là nền tảng của CI/CD và GitOps:
- CI pipeline trigger bằng git push.
- Deploy bằng git merge/tag.
- GitOps: git là single source of truth của infra (ArgoCD/Flux sync từ git).
- Rollback = git revert (immutable history).
- Audit = git log (ai làm gì, khi nào, vì sao — qua commit message).
Nếu Git là tool của tất cả engineer, thì DevOps engineer cần hiểu sâu hơn — không chỉ commit/push, mà còn rebase, cherry-pick, bisect, reflog để cứu khi đồng đội kẹt.
2. Git Internals — Object Model
Git rất đơn giản về internal: mọi thứ là object được lưu ở .git/objects/, identify bởi SHA-1 hash 40 ký tự.
2.1. Bốn loại object
| Object | Nội dung | SHA của gì |
|---|---|---|
| blob | Nội dung 1 file (chỉ data, không có tên) | SHA1 của data |
| tree | Directory: list (mode, type, sha, name) | SHA1 của tree content |
| commit | Snapshot: tree-sha + parent + author + msg | SHA1 của commit metadata |
| tag | Annotated tag: object-sha + tagger + message | SHA1 của tag content |
Cấu trúc gọn gàng:
2.2. Khám phá object thật
cd myrepo
echo "Hello" > file.txt
git add file.txt
git commit -m "init"
# Xem object database
ls .git/objects/
# 8b/ e6/ fa/ info/ pack/
# Lấy SHA của HEAD commit
git rev-parse HEAD
# fa1234abcd...
# Inspect commit object
git cat-file -p HEAD
# tree 8b1234...
# author Alice <alice@example.com> 1700000000 +0700
# committer Alice <alice@example.com> 1700000000 +0700
#
# init
# Inspect tree
git cat-file -p 8b1234
# 100644 blob e69de29... file.txt
# Inspect blob (= content of file)
git cat-file -p e69de29
# Hello
# Type of object
git cat-file -t HEAD # → commit
git cat-file -t 8b1234 # → tree
2.3. Refs — pointer tới commits
Branch và tag chỉ là file text chứa SHA:
cat .git/refs/heads/main
# fa1234abcd... ← chỉ là 1 commit SHA
cat .git/HEAD
# ref: refs/heads/main ← HEAD trỏ tới branch main
git update-ref refs/heads/myfeature fa1234
# = tạo branch myfeature trỏ tới commit fa1234 (low-level)
Hiểu điều này:
- Branch chỉ là pointer. Tạo/xóa branch không động đến commit.
- "Detached HEAD" = HEAD trỏ commit thay vì branch.
git reset --hard SHA= thay đổi pointer của branch, không xóa commit (commit vẫn nằm ở object DB cho đến khi GC).
git reflog để tìm SHA, rồi git reset --hard SHA hoặc git checkout -b recover SHA. Git GC mặc định 90 ngày — đủ thời gian recover.
2.4. Index (staging area)
Khi git add file.txt, Git tạo blob object cho file content và update .git/index (binary file, list pending changes). Khi git commit, Git tạo tree từ index → tạo commit object trỏ tới tree đó.
3. Lệnh cốt lõi — vượt git add/commit/push
3.1. git log — đọc history thông minh
# Compact 1 dòng/commit
git log --oneline # ngắn
git log --oneline --graph --all # đồ thị mọi branch
git log --oneline --graph --decorate # show ref names
# Filter theo author/date/message
git log --author="Alice"
git log --since="2 weeks ago"
git log --grep="bugfix" # search commit message
git log -S "TODO" # search code: commit nào add/remove "TODO"
git log -G "regex" # tương tự nhưng regex
# Show file
git log -p file.txt # patch (diff) mỗi commit động vào file
git log --stat file.txt # số dòng đổi
git log --follow file.txt # follow rename
# Format custom
git log --pretty=format:"%h %an %ar %s"
# %h = short SHA, %an = author name, %ar = ago time, %s = subject
# Số commit
git log --oneline | wc -l
git log --oneline --since="1 month ago" | wc -l
3.2. git diff
git diff # working dir vs index
git diff --staged # index vs HEAD (= --cached)
git diff HEAD # working dir vs HEAD
git diff main..feature # so 2 branch (commit cuối)
git diff main...feature # = main vs (common ancestor of main, feature)
git diff main feature -- file.txt # chỉ 1 file
git diff --stat # tóm tắt
git diff --name-only # chỉ tên file
3.3. git stash — pause work
git stash # save current changes
git stash push -m "WIP feature X" # với message
git stash push -u # bao gồm untracked files
git stash list # xem stash
git stash pop # apply + xóa stash
git stash apply # apply, giữ stash
git stash apply stash@{2} # apply stash thứ 3
git stash drop # xóa stash mới nhất
git stash clear # xóa hết
3.4. git cherry-pick — chọn commit
git cherry-pick abc1234 # apply commit này lên branch hiện tại
git cherry-pick abc1234..def5678 # range (exclusive abc1234)
git cherry-pick abc1234^..def5678 # range (inclusive)
# Conflict?
git cherry-pick --continue
git cherry-pick --abort
# Use case: hotfix có ở main, cần backport sang release branch v1.2
git checkout v1.2
git cherry-pick <hotfix-commit>
3.5. git revert — "undo" an toàn
git revert HEAD # tạo commit mới đảo ngược HEAD
git revert abc1234 # đảo ngược commit cụ thể
# KHÁC reset:
# - reset xóa commit khỏi history → KHÔNG dùng cho branch đã push
# - revert tạo commit mới → SAFE cho shared branch
3.6. git reset — di chuyển HEAD
git reset HEAD~1 # = --mixed: undo commit, keep changes in working dir
git reset --soft HEAD~1 # undo commit, keep changes in index
git reset --hard HEAD~1 # undo commit, DISCARD changes (DANGEROUS)
git reset --hard origin/main # reset to remote (mất changes local)
# Use cases:
# --soft: gộp 2 commit cuối: reset --soft HEAD~2 && commit
# --mixed (default): undo add: reset HEAD file.txt
# --hard: bỏ hoàn toàn local changes (reset --hard HEAD)
--hard xóa working dir changes vĩnh viễn (không vào reflog!). Trước khi chạy, luôn git stash hoặc verify không có việc đang dở.
3.7. git reflog — cứu commit "đã mất"
git reflog
# fa1234 HEAD@{0}: commit: latest work
# de5678 HEAD@{1}: reset: moving to HEAD~1
# ab9012 HEAD@{2}: commit: lost commit !!
# Recover commit "lost"
git reset --hard ab9012
# hoặc tạo branch:
git checkout -b recovered ab9012
3.8. git bisect — tìm commit gây bug
# Bug có ở HEAD nhưng không có ở commit 3 tuần trước
git bisect start
git bisect bad HEAD
git bisect good 3-weeks-ago-sha
# Git tự checkout midpoint
# Test xem bug có không, rồi:
git bisect good # nếu không có bug
# hoặc
git bisect bad # nếu có bug
# Lặp đến khi git tìm ra commit gây bug
git bisect reset # về branch ban đầu
# Tự động hóa
git bisect run npm test # chạy test ở mỗi midpoint, đánh dấu auto
Bisect dùng binary search — 1024 commit chỉ mất 10 lần test để tìm commit lỗi. Cứu sinh khi truy "regression bug".
4. Rebase vs Merge — debate kinh điển
4.1. Merge — giữ history thật
# Trên branch feature, merge main vào để cập nhật
git checkout feature
git merge main
# Hoặc trên main, merge feature in
git checkout main
git merge feature
Khi merge có divergent history, Git tạo merge commit với 2 parent:
4.2. Rebase — viết lại history
# Trên branch feature, rebase lên main mới nhất
git checkout feature
git rebase main
Rebase: lấy commit feature, "play lại" trên đỉnh main mới:
Quan trọng: D', E' là commit mới (SHA khác). History bị viết lại.
4.3. Khi nào dùng cái nào?
Merge
- Branch shared (nhiều người làm)
- Muốn giữ "context" branching
- Long-running release branch
- An toàn — không viết lại history
Rebase
- Branch cá nhân (chỉ bạn)
- Muốn history thẳng, dễ đọc
- Trước khi merge PR (clean up)
- Chú ý: KHÔNG rebase branch đã push share
4.4. Interactive rebase — clean up trước PR
git rebase -i HEAD~5
# Mở editor:
# pick fa1234 add login form
# pick de5678 fix typo
# pick ab9012 add validation
# pick cd3456 fix typo again
# pick ef7890 final touch
# Bạn sửa thành:
# pick fa1234 add login form
# squash de5678 fix typo
# pick ab9012 add validation
# squash cd3456 fix typo again
# squash ef7890 final touch
# → 5 commit gộp thành 2: "add login form" + "add validation"
# Action có thể: pick, reword (sửa msg), edit (pause để sửa), squash (gộp), fixup (squash + bỏ msg), drop (xóa)
4.5. git pull — merge hay rebase?
# Pull = fetch + merge (default)
git pull origin main
# Pull với rebase (history phẳng hơn)
git pull --rebase origin main
# Set default cho repo
git config pull.rebase true
# Set default cho global
git config --global pull.rebase true
Tip: bật pull.rebase để tránh "Merge branch 'main' of github.com..." commit rác trong history khi nhiều người làm cùng main.
5. Branching Strategies — chọn cái nào?
Không có chiến lược "đúng" — tùy team size, release cadence, deployment model.
5.1. Gitflow (Vincent Driessen, 2010)
Branches:
main— production code, mỗi merge = release.develop— integration branch, code "next release".feature/*— branch từ develop, merge về develop.release/*— branch từ develop khi sắp release, chỉ fix bug.hotfix/*— branch từ main cho urgent fix, merge cả main và develop.
Use case: app desktop/mobile có release cycle dài (mỗi vài tuần-tháng), cần maintain nhiều version song song.
Vấn đề: phức tạp, merge conflict cao, không phù hợp web app deploy hàng ngày. Chính Driessen đã viết lại post 2020 nói "Gitflow không còn phù hợp cho hầu hết web app".
5.2. GitHub Flow (2011)
Quy tắc:
- main luôn deployable.
- Branch off main → feature.
- Push lên remote, mở PR.
- Review + CI pass.
- Merge vào main.
- Deploy main ngay (nếu CD).
Use case: web app, SaaS, deploy nhiều lần/ngày. Đơn giản, ít branch.
5.3. GitLab Flow
GitHub Flow + thêm environment branches: main → pre-prod → production. Mỗi env là 1 branch. Merge vào main → CI tự deploy staging. Sau test, merge staging → production. Tốt nếu cần release window.
5.4. Trunk-Based Development (TBD)
Tên gọi cũ "Continuous Integration" theo nghĩa đúng. Mọi người làm trên main (trunk), commit nhỏ + thường xuyên (nhiều lần/ngày). Branch chỉ tồn tại vài giờ-ngày, gọi là "short-lived feature branch".
Đây là chiến lược của Google, Facebook, Netflix — mục mục 6.
5.5. So sánh nhanh
| Strategy | Phù hợp với | Branch lifetime | Deploy frequency |
|---|---|---|---|
| Gitflow | Mobile/desktop, multi-version | Tuần-tháng | Mỗi vài tuần |
| GitHub Flow | Web app, SaaS | Vài ngày | Hàng ngày |
| GitLab Flow | Có release gate | Vài ngày | Hàng ngày |
| Trunk-Based | High-performing team | Vài giờ | Nhiều lần/ngày |
6. Trunk-Based Development — chuẩn của Elite
Nghiên cứu DORA (Accelerate book) chứng minh: Trunk-Based correlate mạnh với high-performing team. Cụ thể:
- ≤ 3 active branches tại bất kỳ thời điểm.
- Branch lifetime < 1 ngày.
- Mọi commit đi qua CI.
- Không có code freeze.
6.1. Cách làm cụ thể
- Commit trực tiếp main nếu thay đổi nhỏ + có test. (Hoặc PR siêu nhanh review trong giờ.)
- Feature flag cho thay đổi lớn — code merge nhưng tắt feature.
- Branch by abstraction — refactor lớn: tạo abstraction layer trước, swap implementation sau, không cần long branch.
- Strong CI — mọi commit chạy đầy đủ test (< 10 phút), block nếu fail.
- Code review nhanh — PR < 200 dòng, review trong giờ.
6.2. Feature flag pattern
// Old: feature on long branch
// New: merge to main, hide behind flag
if (featureFlags.isEnabled('new-checkout', userId)) {
return renderNewCheckout();
} else {
return renderOldCheckout();
}
// Roll out:
// Day 1: flag off (0% users)
// Day 2: 5% users (monitor metrics)
// Day 3: 50% users
// Day 4: 100% users
// Day 7: remove flag + old code
Ưu điểm: deploy thường xuyên (low risk per deploy), tách deploy/release, A/B test built-in, instant rollback (tắt flag không cần redeploy).
Tools: LaunchDarkly, Unleash, Flagsmith, hoặc tự host.
6.3. Vấn đề thường gặp khi chuyển sang TBD
- "Code chưa xong sẽ vào main!" → dùng feature flag.
- "CI quá chậm" → đầu tư vào speed CI là điều kiện tiên quyết.
- "Ai review nhanh được?" → văn hóa: review trong giờ là priority cao nhất.
- "Junior chưa đủ trình" → pair programming, mob programming.
Trừ khi build distributed software (Linux kernel, PostgreSQL) — TBD nên là default. Phần lớn web app, microservice, SaaS phù hợp TBD.
7. Monorepo vs Polyrepo
7.1. Định nghĩa
- Polyrepo — mỗi service/lib một repo. (Default ở hầu hết startup.)
- Monorepo — toàn bộ code công ty trong 1 repo. (Google, Meta, Twitter.)
7.2. So sánh
| Khía cạnh | Polyrepo | Monorepo |
|---|---|---|
| Visibility | Khó browse code khác team | Tất cả code 1 chỗ — easy to discover |
| Atomic refactor | Khó (đổi API → cập nhật N consumer ở N PR) | Dễ (1 PR sửa cả lib + consumer) |
| Versioning | SemVer giữa lib (publish package) | Single version (latest commit là current) |
| Build/CI | Đơn giản (1 repo = 1 pipeline) | Phức tạp (build chỉ phần đổi — Bazel, Nx, Turborepo) |
| Repo size | Nhỏ, fast clone | Khổng lồ — cần partial clone, sparse checkout |
| Team autonomy | Cao (mỗi team owns repo) | Thấp (chia sẻ code base, có policy) |
| Tooling required | Standard Git | Build tool đặc biệt (Bazel, Pants, Nx) |
7.3. Khi nào chọn monorepo?
- Có nhiều shared lib giữa services.
- Refactor cross-service thường xuyên.
- Có resource đầu tư build infrastructure (Bazel learning curve cao).
- Team size đủ lớn (50+ engineer) để biện minh tooling.
7.4. Tools cho monorepo
| Tool | Languages | Used by |
|---|---|---|
| Bazel | Mọi ngôn ngữ | Google, Spotify, Stripe |
| Nx | JS/TS | Modern web |
| Turborepo | JS/TS | Vercel, smaller teams |
| Pants | Python, JVM | Twitter, Toolchain |
| Lerna | JS | Babel, Jest (legacy) |
Đa số startup nên start polyrepo. Chuyển sang monorepo khi pain point rõ (atomic refactor khó, code duplication nhiều).
8. Semantic Versioning — MAJOR.MINOR.PATCH
SemVer chuẩn (semver.org): vMAJOR.MINOR.PATCH
- MAJOR — breaking change (API thay đổi backward-incompat).
- MINOR — feature mới, backward-compatible.
- PATCH — bug fix, không thay đổi API.
Ví dụ:
1.4.2→ patch fix →1.4.31.4.3→ add feature →1.5.01.5.0→ breaking →2.0.0
8.1. Pre-release
1.0.0-alpha.1, 1.0.0-beta.2, 1.0.0-rc.1 (release candidate). Order: alpha < beta < rc < release.
8.2. Build metadata
1.0.0+20240115.abc1234 — sau dấu +, không ảnh hưởng version compare.
8.3. Tag trong Git
# Annotated tag (khuyến nghị — có message + author)
git tag -a v1.4.2 -m "Release 1.4.2"
git push origin v1.4.2
# Lightweight tag (chỉ pointer)
git tag v1.4.2
# List tag
git tag # tất cả
git tag -l "v1.*" # filter
# Xem tag
git show v1.4.2
# Xóa tag
git tag -d v1.4.2 # local
git push origin :refs/tags/v1.4.2 # remote
# Checkout tag (detached HEAD)
git checkout v1.4.2
8.4. Conventional Commits — auto generate version
Convention quy định format commit message:
<type>[optional scope]: <description>
[optional body]
[optional footer]
# Examples:
feat(auth): add OAuth2 login
fix(api): handle null in user response
docs: update README
refactor(db): split queries into modules
test(payment): cover edge cases
chore(deps): upgrade lodash to 4.17
# Breaking change:
feat(api)!: rename /users to /accounts
# OR in footer:
feat(api): rename users endpoint
BREAKING CHANGE: /users renamed to /accounts
Tools tự động bump version + generate CHANGELOG:
- standard-version / semantic-release (JS).
- commitizen — interactive prompt để format đúng.
- cz-conventional-changelog.
Quy tắc:
fix:→ bump PATCHfeat:→ bump MINORBREAKING CHANGE:→ bump MAJOR
9. Pull Request Workflow
9.1. Quy trình chuẩn
- Branch off main:
git checkout main && git pull git checkout -b feature/add-search - Commit nhỏ, có message tốt:
git add . git commit -m "feat(search): add basic search by name" - Push lên remote:
git push -u origin feature/add-search - Mở Pull Request trên GitHub/GitLab. PR description gồm:
- What — thay đổi gì.
- Why — lý do (link issue/ticket).
- How — approach kỹ thuật.
- How to test — cách reviewer verify.
- Screenshots nếu UI.
- CI chạy tự động — lint, test, security scan.
- Code review — ít nhất 1 approve, không có "request changes" pending.
- Address feedback:
# Sửa code... git add . git commit -m "address review feedback" git push # tự update PR - Squash merge hoặc rebase trước merge để clean history.
9.2. Code review checklist
- ✓ Logic đúng? Edge case?
- ✓ Test coverage cho thay đổi?
- ✓ Naming rõ ràng?
- ✓ Không có TODO không cần thiết?
- ✓ Không leak secret (key, password)?
- ✓ Không over-engineer (premature abstraction)?
- ✓ Backward compatible?
- ✓ Documentation/changelog updated nếu cần?
- ✓ Performance OK?
9.3. Branch protection
GitHub/GitLab settings → Branches → Protect main:
- Require PR before merge (no direct push).
- Require ≥ 1 approver.
- Require CI checks pass.
- Require branch up-to-date với main.
- Dismiss stale review khi push mới.
- Require linear history (no merge commit).
- Block force push.
- Restrict who can push (chỉ release manager).
9.4. Merge strategies
| Strategy | Kết quả | Khi nào |
|---|---|---|
| Merge commit | Tạo merge commit có 2 parent | Giữ branch context, multi-author PR |
| Squash and merge | Gộp tất cả commit của PR thành 1 | Default cho hầu hết PR feature |
| Rebase and merge | Apply commit lên main không tạo merge commit | Muốn linear history, giữ chi tiết |
Đa số team chọn squash and merge — main history clean, mỗi PR = 1 commit, dễ revert.
10. Git Hooks & Pre-commit
10.1. Git hooks native
Hooks là script chạy tự động ở các giai đoạn Git workflow. Lưu trong .git/hooks/:
| Hook | Khi chạy | Use case |
|---|---|---|
| pre-commit | Trước commit (sau git add) | Lint, format, test nhanh |
| commit-msg | Sau khi viết commit message | Validate format (Conventional) |
| pre-push | Trước git push | Run full test, integration |
| post-merge | Sau git merge | Re-install dependency nếu package.json đổi |
# .git/hooks/pre-commit (chmod +x)
#!/bin/bash
# Run linter trước commit
npm run lint
if [ $? -ne 0 ]; then
echo "❌ Lint failed. Fix errors before committing."
exit 1
fi
# Bypass hook (cẩn thận):
git commit --no-verify
10.2. pre-commit framework — share hooks giữa team
Vấn đề với .git/hooks/: không được commit (Git ignore .git/). Mỗi dev tự setup → không sync.
pre-commit là framework Python solve vấn đề: define hooks trong .pre-commit-config.yaml commit vào repo.
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: detect-private-key
- repo: https://github.com/psf/black
rev: 24.1.1
hooks:
- id: black # Python formatter
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.56.0
hooks:
- id: eslint
files: \.[jt]sx?$
types: [file]
- repo: https://github.com/zricethezav/gitleaks
rev: v8.18.1
hooks:
- id: gitleaks # detect secrets!
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.86.0
hooks:
- id: terraform_fmt
- id: terraform_validate
# Setup
pip install pre-commit
pre-commit install # install hooks vào .git/hooks
pre-commit run --all-files # chạy thủ công
pre-commit autoupdate # update version
10.3. Husky (JS ecosystem)
npm install -D husky lint-staged
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
# package.json
{
"lint-staged": {
"*.{js,ts,jsx,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"]
}
}
10.4. Server-side hooks (CI là good enough)
Git hỗ trợ pre-receive, update, post-receive trên server, nhưng modern workflow dùng CI (GitHub Actions, GitLab CI) để enforce checks. Pre-commit hooks là convenience cho dev — CI là gate.
11. Bài tập
- Git internals: tạo repo mới, commit 1 file, dùng
git cat-file -pđể inspect commit, tree, blob. Vẽ relationship. - Recover commit: tạo 3 commit,
git reset --hard HEAD~3để "xóa". Dùng reflog tìm và recover commit cuối cùng. - Bisect drill: tạo repo với 20 commit, 1 commit ở giữa làm app crash. Dùng
git bisect runtự động tìm commit lỗi với binary search. - Interactive rebase: tạo 5 commit messy ("WIP", "fix typo", "more fixes"). Squash thành 2 commit có message ý nghĩa.
- Cherry-pick hotfix: tạo 2 branch (main, release/v1.0). Hotfix critical commit ở main. Cherry-pick sang release/v1.0.
- Trunk-based với feature flag: implement feature lớn (vd checkout flow mới) bằng feature flag. Commit nhỏ vào main, flag tắt. Bật flag cho 10% user, monitor, dần roll out.
- Conventional Commits + semantic-release: setup repo với semantic-release. Commit feat:, fix:, BREAKING CHANGE:. Verify version + changelog tự động.
- Pre-commit hooks: setup pre-commit framework với gitleaks, eslint, prettier. Cố tình commit secret API key, verify hook block.
- So sánh strategy: cho 3 scenario (mobile app monthly release / SaaS web daily / open source library) — chọn strategy nào? Tại sao?
- Branch protection: trên GitHub repo của bạn, set branch protection cho main: require PR + 1 approver + CI pass + linear history. Test bằng cách thử push thẳng main.
- Recovery exercise: bạn rebase một branch đã push (vi phạm Golden Rule). Đồng đội đã pull. Bây giờ làm sao "fix" mà không làm hỏng work của họ?
12. Quiz
Quiz cuối Chương 3
Branch trong Git là:
.git/refs/heads/<branch-name> chứa SHA của commit. Tạo/xóa branch là tạo/xóa file 41 byte → cực rẻ. Đây là lý do Git khuyến khích branching workflow. So với SVN: branch là copy thư mục, đắt → ít người dùng."Golden Rule of Rebase" là:
git revert KHÁC git reset như thế nào?
SemVer 1.4.2 → 2.0.0 nghĩa là:
Trunk-Based Development KHUYẾN NGHỊ branch lifetime:
Khi cần cứu commit "đã mất" sau git reset --hard:
git reset --hard SHA hoặc tạo branch git checkout -b recovered SHA. Lưu ý: git reset --hard xóa working dir changes (không vào reflog) — nên stash trước.Conventional Commits prefix nào trigger MAJOR version bump?
! → MAJOR. semantic-release đọc commit từ tag cuối → tự bump version. chore:, docs:, test:, refactor: mặc định không bump version.Monorepo phù hợp khi:
git cherry-pick dùng khi:
git cherry-pick <hotfix-sha> trên branch v1.2. Lưu ý: cherry-pick tạo commit mới (SHA khác), nội dung tương tự — không phải same commit.Pre-commit framework (.pre-commit-config.yaml) tốt hơn .git/hooks/ ở điểm nào?
pre-commit install để setup. Bonus: ecosystem của hook (gitleaks, trufflehog, terraform_fmt, ...) sẵn có.Hoàn thành Chương 3. Tiếp theo: Chương 4 — CI/CD Fundamentals →