git commit

Xem thử

Vấn đề cần giải quyết

Ba tháng sau, một lỗi xuất hiện và bạn mở git log. Thấy toàn thông điệp như "fix", "update", "sửa lần 3" thì không thể biết commit nào gây lỗi hay vì sao nó tồn tại. Một commit tốt vừa đúng về mặt kỹ thuật (nó chứa gì) vừa đúng về mặt giao tiếp (thông điệp nói gì).

Sau bài này, bạn có thể

  • Mô tả commit chứa những gì và vì sao đổi nội dung thì hash đổi.
  • Dùng commit -m, commit -am và viết thông điệp nhiều dòng đúng quy ước.
  • Dùng --amend an toàn và biết khi nào không được dùng.

Tiên quyết: bài Tổng quan lưu thay đổi.

Commit chứa những gì

bash
git commit -m "Sửa lỗi tính tổng giỏ hàng"

git commit chụp lại nội dung trong Staging Area và ghi vào lịch sử như một điểm mốc bất biến. Mỗi commit có một hash duy nhất. Điều quan trọng: commit không lưu phần khác biệt, mà trỏ tới snapshot đầy đủ.

Cách đọc hình. Hộp xanh lá là một commit với bốn trường. Trường tree trỏ tới cây thư mục gốc của snapshot, từ đó trỏ tiếp tới các blob (nội dung tệp) và tree con (thư mục). Trường parent trỏ về commit cha. Hash của commit được tính từ toàn bộ nội dung này, nên đổi bất kỳ thành phần nào thì hash đổi. Đó là lý do một commit đã tạo thì không thể sửa tại chỗ.

Stage và commit trong một bước

bash
git commit -am "Cập nhật README"

Tùy chọn -a tự stage các tệp đã được theo dõi rồi commit. Nó không áp dụng cho tệp mới chưa theo dõi: muốn đưa tệp mới vào commit, phải git add nó trước.

Viết thông điệp tốt

Chạy git commit không có -m sẽ mở trình soạn thảo. Quy ước phổ biến:

text
Tóm tắt ngắn gọn, dưới 50 ký tự

Phần thân giải thích vì sao thay đổi, không chỉ cái gì.
Xuống dòng ở khoảng 72 ký tự. Có thể tham chiếu issue:

Refs #123

Dòng đầu viết ở thì mệnh lệnh: "Thêm...", "Sửa...", "Xóa...". Mỗi commit là một thay đổi logic hoàn chỉnh; không trộn refactor với sửa lỗi.

Thử và quan sát: sửa commit gần nhất

git commit --amend thay thế commit cuối. Hãy dự đoán: nếu bạn amend để sửa lỗi gõ trong thông điệp, commit cũ có bị xóa thật không và hash có đổi không? Chạy mô phỏng, rồi thử biến thể amend sau khi đã push.

Đang tải Animation…

Bảng bước (bản đọc tĩnh của mô phỏng)

BướcLệnhĐiều xảy ra
1git commit -m "Thêm form dang nhap"Commit C2 có thông điệp gõ thiếu dấu.
2git commit --amend -m "Thêm form đăng nhập"Git tạo commit mới C2' có cùng cha C1 và main chuyển sang đó. C2 cũ (nét đứt) không còn nằm trên main.
3git reflog -2Reflog vẫn nhớ commit cũ nên bạn có thể tìm lại nếu cần.

Biến thể: Amend sau khi đã push

BướcLệnhĐiều xảy ra
1git commit -m "Thêm form dang nhap"Commit C2 ở máy bạn.
2git push origin mainĐồng đội có thể lấy C2 về từ remote.
3git commit --amend -m "Thêm form đăng nhập"Amend tạo C2' mới. Remote vẫn giữ C2, nên hai bên bắt đầu rẽ nhánh.
4git push origin mainRemote từ chối vì C2 không phải tổ tiên của C2'. Quy tắc: chỉ amend commit chưa chia sẻ.

Giải đáp. Amend tạo một commit mới C2' có hash khác; C2 cũ không còn nằm trên main nhưng vẫn tìm lại được qua reflog. Ở biến thể, remote vẫn giữ C2 còn máy bạn có C2', hai bên rẽ nhánh và push bị từ chối. Quy tắc: chỉ amend commit chưa được push tới nơi người khác đã lấy về.

Hiểu lầm thường gặp

  • "Commit lưu phần thay đổi." Commit trỏ tới snapshot đầy đủ; phần khác biệt chỉ được tính khi so sánh.
  • "commit -am lấy cả tệp mới." Không. Tệp mới chưa theo dõi vẫn phải git add.
  • "Amend sửa commit tại chỗ." Amend thay thế bằng commit mới. Đó là lý do nó chỉ an toàn với commit chưa chia sẻ.

Tự kiểm tra

  1. Vì sao đổi thông điệp commit thì hash của commit đổi?
  2. git commit -am bỏ sót loại tệp nào?
  3. Bạn đã push commit và phát hiện lỗi gõ trong thông điệp. Có nên amend rồi push lại không?

Đáp án gợi ý. (1) Hash tính từ cả thông điệp, tree, parent và author, nên đổi một thứ là đổi hash. (2) Tệp mới chưa theo dõi. (3) Không nên nếu người khác có thể đã lấy commit về; hãy để nguyên hoặc sửa bằng commit mới.

Điều cần nhớ

  • Commit trỏ tới snapshot, commit cha và thông tin mô tả; hash tính từ tất cả.
  • -am chỉ stage tệp đã theo dõi.
  • Thông điệp: dòng đầu ngắn, mệnh lệnh; thân giải thích vì sao.
  • Amend là thay thế, chỉ dùng với commit chưa chia sẻ.
  • Bài sau: git diff, soi thay đổi trước khi commit.

Nguồn tham khảo

Pro Git, chương 2.4 "Undoing Things" và 10.2 "Git Objects" (git-scm.com/book).