Skip to content

Staging Area & Atomic Commits

Khi mới học Git, nhiều người nghĩ git add chỉ là bước thủ tục trước git commit. Đó là cách hiểu khiến lịch sử dự án nhanh chóng biến thành mớ hỗn độn: commit vừa có feature, vừa có debug log, vừa có sửa tài liệu, vừa lẫn file rác local. Bài này sửa đúng tư duy đó: Staging Area không phải bãi trung chuyển, mà là bộ lọc để bạn chốt đúng thay đổi cần chốt.

Vấn đề thật sự mà bài này giải quyết là gì?

Trong công việc thực tế, bạn rất hiếm khi sửa đúng một thứ duy nhất rồi commit ngay. Thường thì workflow sẽ giống thế này:

  • Bạn sửa API validation cho endpoint đăng ký.
  • Trong lúc debug, bạn thêm vài console.log() hoặc print().
  • Bạn tiện tay cập nhật luôn tài liệu API.
  • IDE sinh thêm file local cache hoặc log.

Nếu bạn dùng kiểu phản xạ git add . && git commit -m "update stuff", bạn vừa tạo ra một commit:

  • khó review
  • khó revert
  • khó cherry-pick
  • khó tìm bug sau này

Atomic commit giải bài toán đó: mỗi commit chỉ nên đại diện cho một ý định kỹ thuật rõ ràng.

Mental model: Working Directory → Staging Area → Commit History

Đây là mô hình bạn phải nhìn thấy trong đầu mỗi lần chuẩn bị commit:

text
┌──────────────────────┐     git add / git add -p      ┌──────────────────────┐     git commit      ┌──────────────────────┐
│   Working Directory  │ ───────────────────────────▶  │    Staging Area      │ ─────────────────▶ │    Commit History    │
│  Nơi bạn đang sửa    │                               │  Nơi chọn đúng phần  │                    │  Lịch sử đã chốt     │
│  Có thể rất lộn xộn  │ ◀──── git restore / edit ──── │  sẽ đi vào commit    │                    │  để review / revert  │
└──────────────────────┘                               └──────────────────────┘                    └──────────────────────┘

Điểm quan trọng:

  • Working Directory có thể bừa bộn tạm thời.
  • Staging Area phải có chủ đích.
  • Commit History phải sạch, dễ đọc, dễ phục hồi.

Nếu bạn bỏ qua Staging Area như một bước suy nghĩ, bạn đang biến lịch sử commit thành ảnh chụp của sự lộn xộn thay vì nhật ký kỹ thuật có cấu trúc.

TIP

Nếu cần ôn lại trạng thái file và cách đọc output, xem thêm Add & Commit (The Workflow)Status & Log (Inspection).

Atomic commit là gì?

Atomic commit là commit mà:

  • chỉ làm một việc
  • có thể mô tả bằng một câu rõ ràng
  • nếu cần rollback thì rollback một ý định, không phá hỏng thứ khác

Ví dụ xấu

text
feat: update auth flow

Nhưng bên trong lại chứa:

  • sửa API login
  • thêm 6 dòng debug log
  • cập nhật README
  • đổi format vài file không liên quan

Đây không phải atomic commit. Đây là "túi nilon tổng hợp".

Ví dụ tốt

text
fix(auth): validate refresh token rotation
docs(api): document new refresh token response

Hai commit riêng biệt này tốt hơn vì:

  • reviewer đọc dễ hơn
  • CI fail thì khoanh vùng nhanh hơn
  • docs có thể merge riêng
  • hotfix có thể cherry-pick riêng nếu cần

Safe staging habits — thói quen staging an toàn

Đây là phần quan trọng nhất của bài.

1) Luôn kiểm tra trước khi add

Trước khi staged bất kỳ thứ gì, hãy nhìn trạng thái hiện tại:

bash
git status
git diff

git status trả lời câu hỏi: Git đang thấy gì?
git diff trả lời câu hỏi: Mình vừa sửa chính xác cái gì?

2) Đừng mặc định dùng git add .

git add . không sai trong mọi tình huống. Nó chỉ nguy hiểm khi bạn dùng nó như phản xạ.

Không nên dùng git add . khi:

  • working tree đang chứa nhiều thay đổi không liên quan
  • bạn vừa debug bằng log tạm
  • có file untracked mà bạn chưa chắc nên commit
  • IDE hoặc local tooling sinh file rác
  • bạn muốn tách code change và docs change thành 2 commit

Đây chính là câu trả lời cho câu hỏi bắt buộc số (2): khi nào không nên dùng lệnh obvious nhất?
Không dùng git add . khi working tree đang lẫn nhiều ý định kỹ thuật hoặc có nguy cơ cuốn theo file rác.

3) Stage theo file khi ý định đã rõ

Ví dụ bạn muốn commit riêng phần API:

bash
git add src/api/auth.ts src/api/token-service.ts
git diff --cached
git commit -m "fix(auth): validate refresh token rotation"

Sau đó mới commit docs:

bash
git add docs/api/authentication.md
git diff --cached
git commit -m "docs(api): document refresh token response"

4) Dùng partial staging khi một file chứa nhiều loại thay đổi

Đây là kỹ năng giúp bạn thật sự làm chủ Staging Area:

bash
git add -p

git add -p cho phép bạn stage theo từng hunk thay vì cả file. Cực kỳ hữu ích khi:

  • cùng một file vừa có logic thật vừa có debug log
  • bạn refactor một phần, nhưng chưa muốn commit phần còn lại
  • bạn sửa bug và dọn code trong cùng một file nhưng muốn tách commit

5) Luôn xem lại phần đã staged trước khi commit

bash
git diff --cached

Nếu bạn chưa có thói quen này, hãy tạo nó ngay. Đây là lớp kiểm tra cuối cùng trước khi thay đổi đi vào lịch sử.

6) Giữ junk files ra khỏi commit

Các file sau thường không nên vào repo:

  • log debug local
  • .env
  • local database dump
  • file build tạm
  • file cache từ IDE

Nếu git status cho thấy các file kiểu này, đừng staged cho tới khi bạn chắc chúng là một phần hợp lệ của thay đổi. Với secret và file nhạy cảm, đọc thêm Git Security & Secret Handling.

Tình huống thực tế #1: trộn feature code với debug logs

Bạn đang sửa bug timeout ở service gọi payment provider:

  • src/payment/retry-service.ts có fix logic thật
  • cùng file đó có thêm 4 dòng console.log("retry payload", payload)

Phản xạ nguy hiểm:

bash
git add .
git commit -m "fix(payment): handle retry timeout"

Vấn đề:

  • commit trông có vẻ sạch nhưng thực ra mang theo debug noise
  • reviewer phải tự đoán dòng nào là business logic, dòng nào là rác tạm
  • nếu log lộ dữ liệu nhạy cảm, bạn vừa tạo risk không đáng có

Workflow đúng:

bash
git add -p src/payment/retry-service.ts
git diff --cached
git commit -m "fix(payment): handle retry timeout"

Sau đó:

  • hoặc xóa debug log trước commit kế tiếp
  • hoặc giữ chúng ở working directory cho tới khi debug xong

Tình huống thực tế #2: tách API changes và docs changes

Bạn thêm field tokenExpiresAt vào response của endpoint login và cũng sửa tài liệu API.

Nếu bạn commit chung một cục:

  • PR khó review theo từng concern
  • docs team hoặc backend team khó cherry-pick riêng
  • nếu code bị rollback nhưng docs không rollback theo, lịch sử rất khó hiểu

Workflow tốt hơn:

  1. stage và commit code backend trước
  2. stage và commit docs sau
bash
git add src/api/auth-controller.ts src/api/contracts/login-response.ts
git diff --cached
git commit -m "feat(auth): add tokenExpiresAt to login response"

git add docs/api/authentication.md
git diff --cached
git commit -m "docs(auth): document tokenExpiresAt in login response"

Đây là giá trị cốt lõi của atomic commits: một thay đổi lớn trong đầu bạn được tách thành các đơn vị review được trong mắt người khác.

Câu hỏi bắt buộc (1): chính xác bài này giải quyết vấn đề gì?

Nó giải quyết ba vấn đề rất thực tế:

  1. Lịch sử commit lộn xộn — vì bạn commit mọi thứ đang có thay vì commit đúng ý định.
  2. Code review tốn thời gian — vì reviewer phải lọc tay đâu là thay đổi chính, đâu là noise.
  3. Khó phục hồi khi có sự cố — vì một commit chứa quá nhiều thứ không liên quan, nên revert hoặc cherry-pick đều đau đớn.

Nói ngắn gọn: Staging Area + atomic commits biến Git từ công cụ lưu snapshot thành công cụ tổ chức thay đổi.

Câu hỏi bắt buộc (2): khi nào bạn không nên dùng lệnh obvious?

Lệnh obvious ở đây thường là:

bash
git add .

Không nên dùng khi:

  • bạn chưa đọc git status
  • bạn chưa kiểm tra diff
  • working tree chứa nhiều luồng công việc
  • có file local/junk/untracked chưa được xác minh
  • bạn cần một commit đủ sạch để review, revert, hoặc cherry-pick

Nếu mọi thay đổi trong working tree đều thực sự thuộc cùng một mục tiêu, git add . có thể chấp nhận được. Nhưng trong môi trường kỹ thuật thực tế, điều đó ít xảy ra hơn bạn tưởng.

Câu hỏi bắt buộc (3): nếu dùng sai thì phục hồi thế nào?

Trường hợp A — stage nhầm nhưng chưa commit

Đây là case dễ nhất:

bash
git diff --cached
git restore --staged <file>

Nếu lỡ stage nhiều thứ:

bash
git restore --staged .

Sau đó stage lại cho đúng bằng file cụ thể hoặc git add -p.

Trường hợp B — đã commit local nhưng chưa push

Bạn vẫn còn cửa sửa lịch sử an toàn:

bash
git reset --soft HEAD~1
git restore --staged .
git status
git add -p
git commit -m "..."

--soft giữ nguyên thay đổi trong working tree, chỉ lùi commit pointer để bạn đóng gói lại cho sạch hơn. Sau đó bỏ toàn bộ index hiện tại khỏi staging area và stage lại đúng phần cần giữ.

Trường hợp C — đã push lên branch shared

Lúc này đừng hoảng và đừng rewrite shared history bừa bãi. Hướng xử lý an toàn hơn thường là:

  • tạo commit follow-up để xóa junk
  • hoặc revert nếu commit sai gây rủi ro thật sự
  • phối hợp với team nếu có secret hoặc dữ liệu nhạy cảm

Phục hồi sâu hơn nằm ở các bài sau:

Một workflow staging an toàn mà bạn nên tập thành phản xạ

text
1. git status
2. git diff
3. Chọn đúng phần cần commit
4. git add <file> hoặc git add -p
5. git diff --cached
6. git commit -m "một ý định rõ ràng"

Nếu bạn chỉ nhớ một điều từ bài này, hãy nhớ câu sau:

Working Directory có thể bừa bộn, nhưng commit history thì không được phép bừa bộn.

🧠 Quiz

Câu 1: Staging Area hữu ích nhất ở điểm nào?

  • [ ] A. Nó tự động tối ưu performance của Git repository
  • [x] B. Nó cho phép chọn đúng phần thay đổi sẽ đi vào commit tiếp theo
  • [ ] C. Nó thay thế hoàn toàn cho git diff
  • [ ] D. Nó giúp push code nhanh hơn

💡 Giải thích: Staging Area là lớp chọn lọc giữa Working Directory và Commit History. Giá trị của nó nằm ở khả năng đóng gói thay đổi theo ý định, không phải ở tốc độ.

Câu 2: Khi nào không nên dùng git add .?

  • [ ] A. Khi repo đang clean
  • [x] B. Khi working tree chứa feature code, debug logs, docs update, hoặc file rác lẫn với nhau
  • [ ] C. Khi chỉ có đúng một file thay đổi
  • [ ] D. Khi bạn đang ở branch feature

💡 Giải thích: git add . nguy hiểm nhất khi nó kéo vào staging những thứ không cùng một mục tiêu kỹ thuật.

Câu 3: Nếu bạn stage nhầm file nhưng chưa commit, bước phục hồi an toàn là gì?

  • [ ] A. git reset --hard
  • [x] B. git restore --staged <file> rồi stage lại cho đúng
  • [ ] C. git push --force
  • [ ] D. Xóa cả branch và làm lại từ đầu

💡 Giải thích: Khi mới stage nhầm, vấn đề còn rất rẻ để sửa. Chỉ cần bỏ file khỏi staging area và chọn lại đúng phần cần commit.