Chương 03 · Foundation

Git & Workflows

Git là thư viện code chung của thế giới. DevOps engineer phải hiểu hơn cú pháp lệnh — cần biết Git internals (object, ref, refspec), branching strategy nào dùng khi nào (Gitflow vs Trunk-based vs GitHub Flow), monorepo vs polyrepo, semantic versioning, và pull request workflow.

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:

  1. Distributed — mỗi clone là full repository có history. Không cần network để commit, branch, diff.
  2. Nhanh — operation chỉ trên local file system, không network roundtrip.
  3. 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

ObjectNội dungSHA của gì
blobNội dung 1 file (chỉ data, không có tên)SHA1 của data
treeDirectory: list (mode, type, sha, name)SHA1 của tree content
commitSnapshot: tree-sha + parent + author + msgSHA1 của commit metadata
tagAnnotated tag: object-sha + tagger + messageSHA1 của tag content

Cấu trúc gọn gàng:

commit ← parent ← parent ← parent │ tree (root dir) ├── blob (file1.txt) ├── blob (file2.js) └── tree (subdir/) ├── blob └── blob

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).
Hệ quả thực tế Lỡ xóa commit chứa code quan trọng? Đừng panic — chạy 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 đó.

Working Dir ──add──→ Index ──commit──→ Repository ↓ push ↓ Remote

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)
DANGER — git reset --hard --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:

main: A ─── B ─── C ─── M ←─ HEAD (main) ╲ ╱ feature: D ─── E ──╯

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:

Trước: main: A ─── B ─── C ╲ feature: D ─── E Sau git rebase main (đứng ở feature): main: A ─── B ─── C ╲ feature: D' ─── E' ← commit MỚI, history phẳng

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
Golden Rule of Rebase Đừng bao giờ rebase branch đã push lên remote và đang được người khác dùng. Rebase tạo commit mới (SHA khác); người khác đã pull commit cũ sẽ bị conflict không thể giải.

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)

main ────●────────●────────●──── ← prod, only releases │ │ │ release │ ● │ ← release prep │ │ │ develop ──●─┴────●───┴────●───┴─ ← integration │ │ │ feature ●──●───● │ ← short-lived │ hotfix ● ← urgent fix from main

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)

main ────●────●────●────●────●────●──── ← always deployable ╲ ╲ ╲ ╲ feature ● ● ● ● ← branch + PR ╲ ╲ ╲ ╲ └───┴────────┴───┴── merge via PR

Quy tắc:

  1. main luôn deployable.
  2. Branch off main → feature.
  3. Push lên remote, mở PR.
  4. Review + CI pass.
  5. Merge vào main.
  6. 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".

main ●●●●●●●●●●●●●●●●●●●●●●●●●●● ← mọi người commit thẳng ╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲ short ●●●●●●●●●●●●●●●●●●●● ← merge trong vài giờ

Đây là chiến lược của Google, Facebook, Netflix — mục mục 6.

5.5. So sánh nhanh

StrategyPhù hợp vớiBranch lifetimeDeploy frequency
GitflowMobile/desktop, multi-versionTuần-thángMỗi vài tuần
GitHub FlowWeb app, SaaSVài ngàyHàng ngày
GitLab FlowCó release gateVài ngàyHàng ngày
Trunk-BasedHigh-performing teamVà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ể

  1. Commit trực tiếp main nếu thay đổi nhỏ + có test. (Hoặc PR siêu nhanh review trong giờ.)
  2. Feature flag cho thay đổi lớn — code merge nhưng tắt feature.
  3. Branch by abstraction — refactor lớn: tạo abstraction layer trước, swap implementation sau, không cần long branch.
  4. Strong CI — mọi commit chạy đầy đủ test (< 10 phút), block nếu fail.
  5. 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ạnhPolyrepoMonorepo
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

ToolLanguagesUsed by
BazelMọi ngôn ngữGoogle, Spotify, Stripe
NxJS/TSModern web
TurborepoJS/TSVercel, smaller teams
PantsPython, JVMTwitter, Toolchain
LernaJSBabel, 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.3
  • 1.4.3 → add feature → 1.5.0
  • 1.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 PATCH
  • feat: → bump MINOR
  • BREAKING CHANGE: → bump MAJOR

9. Pull Request Workflow

9.1. Quy trình chuẩn

  1. Branch off main:
    git checkout main && git pull
    git checkout -b feature/add-search
  2. Commit nhỏ, có message tốt:
    git add .
    git commit -m "feat(search): add basic search by name"
  3. Push lên remote:
    git push -u origin feature/add-search
  4. 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.
  5. CI chạy tự động — lint, test, security scan.
  6. Code review — ít nhất 1 approve, không có "request changes" pending.
  7. Address feedback:
    # Sửa code...
    git add .
    git commit -m "address review feedback"
    git push                                 # tự update PR
  8. 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

StrategyKế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/:

HookKhi chạyUse case
pre-commitTrước commit (sau git add)Lint, format, test nhanh
commit-msgSau khi viết commit messageValidate format (Conventional)
pre-pushTrước git pushRun full test, integration
post-mergeSau git mergeRe-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

  1. Git internals: tạo repo mới, commit 1 file, dùng git cat-file -p để inspect commit, tree, blob. Vẽ relationship.
  2. 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.
  3. Bisect drill: tạo repo với 20 commit, 1 commit ở giữa làm app crash. Dùng git bisect run tự động tìm commit lỗi với binary search.
  4. Interactive rebase: tạo 5 commit messy ("WIP", "fix typo", "more fixes"). Squash thành 2 commit có message ý nghĩa.
  5. Cherry-pick hotfix: tạo 2 branch (main, release/v1.0). Hotfix critical commit ở main. Cherry-pick sang release/v1.0.
  6. 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.
  7. Conventional Commits + semantic-release: setup repo với semantic-release. Commit feat:, fix:, BREAKING CHANGE:. Verify version + changelog tự động.
  8. Pre-commit hooks: setup pre-commit framework với gitleaks, eslint, prettier. Cố tình commit secret API key, verify hook block.
  9. 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?
  10. 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.
  11. 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à:

  • Một bản sao của toàn bộ codebase
  • Một file lớn chứa diff
  • Một file text 41 byte chứa SHA của commit (pointer)
  • Một database riêng
Branch chỉ 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à:

  • Luôn rebase trước khi push
  • Đừng bao giờ rebase branch đã push share với người khác
  • Rebase nhanh hơn merge
  • Chỉ rebase khi không có conflict
Rebase tạo commit mới (SHA khác). Người khác đã pull commit cũ → khi pull lại sẽ thấy 2 history khác nhau, conflict không thể giải. Nguyên tắc: chỉ rebase branch cá nhân, chưa share. Đã push share → dùng merge.

git revert KHÁC git reset như thế nào?

  • Revert nhanh hơn
  • Reset tốt hơn cho remote branch
  • Hai lệnh giống hệt
  • Revert tạo commit mới đảo ngược (an toàn cho shared branch); Reset xóa commit khỏi history (chỉ dùng cho local)
Revert: thêm commit mới có content "ngược lại" — history immutable, không break collaborator. Reset: thay đổi pointer của branch, commit cũ bị bỏ — nếu đã push, force push gây loss code. Quy tắc: shared branch → revert; local branch → reset OK.

SemVer 1.4.2 → 2.0.0 nghĩa là:

  • Breaking change — API thay đổi backward-incompatible
  • Bug fix lớn
  • Feature mới
  • Pre-release version
MAJOR.MINOR.PATCH. Bump MAJOR khi có breaking change (consumer phải sửa code). Bump MINOR khi add feature backward-compatible. Bump PATCH khi fix bug. Conventional Commits + semantic-release tự động bump dựa trên commit message (feat → MINOR, fix → PATCH, BREAKING → MAJOR).

Trunk-Based Development KHUYẾN NGHỊ branch lifetime:

  • Vài tuần
  • 1 sprint (2 tuần)
  • Vài giờ → 1 ngày
  • Càng dài càng tốt
TBD: branch tồn tại < 1 ngày, ≤ 3 active branches tại bất kỳ thời điểm. Feature lớn → dùng feature flag (commit nhỏ vào main, flag off). Nghiên cứu DORA: TBD correlate mạnh với high-performing team. Long branch → merge conflict → fear of merging → infrequent deploy.

Khi cần cứu commit "đã mất" sau git reset --hard:

  • Không thể cứu
  • Dùng git reflog tìm SHA, rồi git reset hoặc git checkout về SHA đó
  • Phải clone lại repo
  • Xin admin restore backup
git reflog log mọi thay đổi của HEAD/branch trong 90 ngày (default). Commit "deleted" vẫn ở object DB cho đến khi GC. Tìm SHA, 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?

  • feat:
  • fix:
  • BREAKING CHANGE: trong footer hoặc dấu ! sau type (feat!: hoặc fix!:)
  • refactor:
feat: → MINOR; fix: → PATCH; BREAKING CHANGE: hoặc ! → 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:

  • Có nhiều shared lib + atomic refactor cross-service thường xuyên + đầu tư build tooling (Bazel)
  • Team chỉ có 2-3 dev
  • Mỗi service độc lập hoàn toàn
  • Open source library
Monorepo benefits: atomic refactor (1 PR cho lib + tất cả consumer), shared code, single version. Costs: build tooling phức tạp (Bazel learning curve), repo size lớn cần partial clone, tooling phổ biến (IDE, Git GUI) đôi khi không scale. Google/FB/Twitter có monorepo vì có resource đầu tư. Startup nhỏ nên start polyrepo.

git cherry-pick dùng khi:

  • Merge nhiều branch cùng lúc
  • Reset branch về commit cũ
  • Tạo branch mới
  • Apply chỉ 1 commit (hoặc range) từ branch khác lên branch hiện tại — vd hotfix backport sang release branch
Use case classic: hotfix critical commit ở main (vd CVE patch), cần backport sang release/v1.2 đang chạy production. 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?

  • Chạy nhanh hơn
  • Có thể commit vào repo → share giữa team (file .git/hooks/ không được Git track)
  • Có nhiều hook hơn
  • Bypass được
.git/hooks/ ở trong .git/ — Git không track. Mỗi dev phải tự setup. Pre-commit framework: file YAML commit vào repo. Khi clone, dev chạy 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 →