git checkout / git switch
Xem thửVấn đề cần giải quyết
Có hai lệnh mà bài hướng dẫn cũ dùng lẫn lộn: git checkout để chuyển nhánh, và cũng git checkout để bỏ thay đổi trong một tệp. Một lệnh làm hai việc khác hẳn nhau nên rất dễ gõ nhầm và mất công việc. Từ Git 2.23, hai việc được tách thành git switch và git restore.
Sau bài này, bạn có thể
- Chuyển và tạo nhánh bằng
git switch(và nhận ragit checkoutcũ). - Giải thích chuyển nhánh làm gì với HEAD, Staging Area và Working Directory.
- Hiểu detached HEAD và cách giữ lại commit tạo ở đó.
Tiên quyết: bài Tổng quan git branch.
Chuyển nhánh
git checkout feature-login
git switch feature-login
Dòng đầu là cách cũ; dòng sau là cách mới, khuyến nghị vì rõ ràng và an toàn hơn.
Thử và quan sát
Hãy dự đoán: hai nhánh có app.js khác nhau. Khi bạn git switch sang nhánh kia, nội dung app.js trên đĩa có đổi không? Chạy mô phỏng, rồi thử biến thể xem một commit cũ.
Đang tải Animation…
Bảng bước (bản đọc tĩnh của mô phỏng)
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git switch feature-login | HEAD chuyển sang feature-login. Git thay Working Directory và Staging Area bằng snapshot của F1, nên app.js đổi sang v2. |
| 2 | git switch main | Quay lại main: app.js trở về v1. Không có commit nào được tạo hay thay đổi trong cả hai lần chuyển. |
| 3 | git switch -c hotfix v1.0.0 | Cờ -c tạo nhánh mới ngay từ tag v1.0.0 rồi chuyển sang đó. Nội dung quay về snapshot của bản phát hành. |
Biến thể: Xem commit cũ (detached HEAD)
| Bước | Lệnh | Điều xảy ra |
|---|---|---|
| 1 | git checkout 5b1a0e2 | Chuyển thẳng tới một commit thay vì một nhánh. HEAD tách khỏi mọi nhánh: đây là detached HEAD. |
| 2 | git commit -m "Thử nghiệm" | Commit mới X được tạo và HEAD đi theo nó, nhưng không nhánh nào trỏ tới X. |
| 3 | git switch -c thu-nghiem | Tạo nhánh ngay khi đang ở đó để giữ X. Nếu chuyển đi mà chưa tạo nhánh, X sẽ không còn tên nào trỏ tới. |
Giải đáp. Có. Chuyển nhánh dời HEAD sang nhánh đích và cập nhật Working Directory cùng Staging Area khớp snapshot của nhánh đó, nên app.js đổi theo. Không có commit nào được tạo hay thay đổi. Cờ -c tạo nhánh mới ngay và có thể chỉ rõ điểm xuất phát.
git switch -c feature-new
git switch -c hotfix v1.0.0
git switch -c fix a1b2c3d
Dòng đầu tạo nhánh từ vị trí hiện tại, dòng hai từ tag, dòng ba từ một commit. Cách cũ tương đương là git checkout -b feature-new.
Vì sao Git tách checkout thành hai lệnh
checkout bị "quá tải": nó vừa chuyển nhánh vừa khôi phục tệp. Nay hai việc tách riêng:
| Việc cần làm | Lệnh hiện đại | Lệnh cũ |
|---|---|---|
| Chuyển nhánh | git switch <nhánh> | git checkout <nhánh> |
| Tạo và chuyển nhánh | git switch -c <tên> | git checkout -b <tên> |
| Bỏ thay đổi ở một tệp | git restore <tệp> | git checkout -- <tệp> |
| Lấy tệp từ commit khác | git restore --source=HEAD~2 <tệp> | git checkout HEAD~2 -- <tệp> |
Nhớ switch là cho nhánh, restore là cho tệp và bạn ít bị nhầm hơn.
Detached HEAD
git checkout a1b2c3d
Chuyển thẳng tới một commit thay vì một nhánh đưa bạn vào trạng thái detached HEAD: HEAD trỏ thẳng vào commit. Bạn xem được và thậm chí commit được, nhưng commit mới không thuộc nhánh nào. Biến thể ở mô phỏng cho thấy cách giữ lại: tạo nhánh ngay bằng git switch -c ten-nhanh. Nếu chuyển đi mà chưa tạo nhánh, không còn tên nào trỏ tới commit đó, tuy vẫn cứu được trong một thời gian qua reflog.
Hiểu lầm thường gặp
- "Chuyển nhánh là sao chép mã." Chỉ HEAD dời và tệp được cập nhật theo snapshot.
- "Detached HEAD là lỗi." Nó là trạng thái hợp lệ để xem lịch sử, chỉ nguy hiểm nếu bạn commit mà không tạo nhánh.
- "Chuyển nhánh luôn giữ được thay đổi chưa commit." Nếu tệp khác nhau giữa hai nhánh, Git từ chối; bạn cần commit hoặc stash trước.
Tự kiểm tra
- Chuyển nhánh làm thay đổi những vùng nào của Git?
- Lệnh nào tạo nhánh mới từ tag
v1.0.0và chuyển sang đó? - Bạn đang ở detached HEAD và vừa commit. Làm sao giữ commit đó?
Đáp án gợi ý. (1) HEAD, Staging Area và Working Directory; lịch sử không đổi. (2) git switch -c hotfix v1.0.0. (3) git switch -c ten-nhanh ngay khi đang ở đó.
Điều cần nhớ
- Switch dời HEAD và cập nhật tệp, không đụng lịch sử.
- switch cho nhánh, restore cho tệp.
- Detached HEAD: tạo nhánh để giữ commit mới.
- Bài sau: git merge, gộp công việc giữa các nhánh.
Nguồn tham khảo
Tài liệu git-switch và git-restore (git-scm.com/docs); Pro Git, chương 3.1 (git-scm.com/book).