Một trong những tính năng mạnh mẽ nhất của Git là theo dõi lịch sử của nó. Trong phần này, chúng ta sẽ khám phá cách điều hướng qua lịch sử dự án của bạn, kiểm tra các thay đổi theo thời gian và thậm chí viết lại lịch sử khi cần thiết.
Khám phá lịch sử Git của bạn
Xem lịch sử commit
Lệnh cơ bản để xem lịch sử là:
git log
Điều này hiển thị danh sách commit với:
- Hàm băm SHA-1 (ID commit)
- tác giả
- Ngày
- Tin nhắn commit
Để có cái nhìn nhỏ gọn hơn:
git log --oneline
Điều này hiển thị mỗi commit trên một dòng với hàm băm rút ngắn.
Trực quan hóa lịch sử chi nhánh
Để xem biểu diễn đồ họa về lịch sử chi nhánh của bạn:
git log --graph --oneline --all
Điều này cho thấy các nhánh và sự hợp nhất có liên quan với nhau như thế nào. Thêm --decorate hiển thị các tham chiếu nhánh và thẻ:
git log --graph --oneline --all --decorate
Lịch sử lọc
Bạn có thể lọc nhật ký để chỉ hiển thị những gì bạn quan tâm:
Theo ngày:
git log --after="2023-01-01" --before="2023-02-01"
Theo tác giả:
git log --author="Jane Smith"
Theo nội dung:
git log -S"login" # Commits that add or remove the string "login"
Theo tập tin:
git log -- path/to/file.js
Theo nội dung tin nhắn:
git log --grep="fix bug"
Xem các thay đổi trong commit
Để xem những thay đổi được giới thiệu trong mỗi commit:
git log -p
Để có bản tóm tắt thống kê về các thay đổi:
git log --stat
Để xem các thay đổi cho một tệp cụ thể:
git log -p -- path/to/file.js
Xem commit cụ thể
Để hiển thị một commit:
git show commit-hash
Để xem phiên bản cụ thể của tệp:
git show commit-hash:path/to/file.js
Thời gian du hành qua mã của bạn
Kiểm tra các phiên bản trước
Để tạm thời chuyển sang trạng thái trước đó trong dự án của bạn:
git checkout commit-hash
Điều này đưa bạn vào một trạng thái "tách rời", nơi bạn có thể nhìn xung quanh nhưng những thay đổi sẽ không được lưu vào bất kỳ nhánh nào.
Để kiểm tra một tệp cụ thể từ commit trước đó:
git checkout commit-hash -- path/to/file.js
Thao tác này sẽ thêm phiên bản cũ của tệp vào vùng staging của bạn.
Làm việc với Thẻ
Thẻ được đặt tên tham chiếu đến commit cụ thể, thường được sử dụng cho các bản phát hành:
Tạo một thẻ nhẹ:
git tag v1.0.0
Tạo thẻ chú thích bằng tin nhắn:
git tag -a v1.0.0 -m "Version 1.0.0 release"
Thẻ danh sách:
git tag
Kiểm tra một thẻ:
git checkout v1.0.0
So sánh các phiên bản khác nhau
Để thấy sự khác biệt giữa hai commit:
git diff commit1..commit2
Để xem sự khác biệt cho một tệp cụ thể:
git diff commit1..commit2 -- path/to/file.js
Để so sánh các nhánh:
git diff main..feature-branch
Gỡ lỗi với Lịch sử Git
Tìm lỗi với git bisect
Git bisect sử dụng tìm kiếm nhị phân để tìm ra commit nào đã gây ra lỗi:
# Start the bisect process
git bisect start
# Mark the current commit as bad
git bisect bad
# Mark a known good commit
git bisect good a1b2c3d4
# Git will checkout a commit halfway between good and bad
# Test your code, then mark it:
git bisect good # Or git bisect bad
# Continue until Git identifies the first bad commit
# When done, reset to your original branch
git bisect reset
Điều này có thể tiết kiệm hàng giờ gỡ lỗi bằng cách thu hẹp chính xác thời điểm lỗi được đưa ra.
Tìm ai đã thay đổi một dòng
Để xem ai đã sửa đổi lần cuối từng dòng của tệp:
git blame filename.txt
Phần này hiển thị hàm băm commit, tác giả, ngày tháng và nội dung cho mỗi dòng. Nó rất hữu ích để hiểu tại sao mã được viết theo một cách nhất định.
Để biết thêm ngữ cảnh, hãy sử dụng cờ -C để phát hiện mã được di chuyển từ các tệp khác:
git blame -C filename.txt
Khôi phục công việc bị mất
Sử dụng Relog
Git lưu giữ một bản ghi về vị trí các tham chiếu HEAD và nhánh của bạn trong 30 ngày qua trong reflog:
git reflog
Điều này giúp bạn tìm thấy các hàm băm commit mà bạn có thể đã đánh mất thông qua các thao tác như đặt lại hoặc khởi động lại.
Để khôi phục commit bị mất:
git checkout -b recovery-branch commit-hash
Khôi phục các nhánh đã xóa
Nếu bạn vô tình xóa một nhánh, bạn có thể khôi phục nó bằng cách sử dụng reflog:
# Find the commit at the tip of the deleted branch
git reflog
# Create a new branch at that commit
git checkout -b recovered-branch commit-hash
Khôi phục các thay đổi chưa được cam kết
Nếu bạn vô tình loại bỏ các thay đổi với git checkout -- file:
Kiểm tra xem các thay đổi có trong kho không:
git stash list git stash applyTrên một số hệ thống, lịch sử cục bộ của IDE có thể đã lưu một bản sao
Thật không may, những thay đổi thực sự chưa được thực hiện và chưa được cam kết đã bị loại bỏ không thể được phục hồi bằng Git
Viết lại lịch sử
⚠️ Warning: Việc viết lại lịch sử nên được thực hiện một cách thận trọng, đặc biệt nếu commit đã được đẩy vào kho lưu trữ dùng chung.
Sửa đổi commit cuối cùng
Để sửa đổi commit gần đây nhất của bạn:
git commit --amend
Thao tác này sẽ mở trình chỉnh sửa để thay đổi thông báo commit. Để bao gồm các thay đổi đã đưa vào staging trong commit đã sửa đổi:
git add .
git commit --amend
Để giữ cùng một tin nhắn:
git commit --amend --no-edit
Sắp xếp lại và sửa đổi commit với Rebase tương tác
Rebase tương tác cho phép bạn sửa đổi một loạt commit:
git rebase -i HEAD~3 # Modify the last 3 commits
Thao tác này sẽ mở trình chỉnh sửa có danh sách commit. Bạn có thể:
pick- giữ commitreword- thay đổi thông báo commitedit- dừng và sửa đổi commitsquash- kết hợp với commit trước đófixup- kết hợp với commit trước đó, loại bỏ tin nhắndrop- loại bỏ commit
Ví dụ: để kết hợp ba commit thành một:
pick 1a2b3c4 Add user authentication
squash 2b3c4d5 Fix login form styling
squash 3c4d5e6 Add password validation
Lưu và đóng trình chỉnh sửa và Git sẽ kết hợp commit.
Tách commit
Để chia commit thành nhiều commit:
git rebase -i HEAD~3
# Change "pick" to "edit" for the commit you want to split
# Save and close
# Reset that commit but keep the changes in your working directory
git reset HEAD^
# Now add and commit the changes in smaller logical chunks
git add file1.js
git commit -m "First part of the feature"
git add file2.js
git commit -m "Second part of the feature"
# Continue the rebase
git rebase --continue
Xóa dữ liệu nhạy cảm
Nếu bạn vô tình sử dụng dữ liệu nhạy cảm của đã commit:
git filter-branch --force --index-filter "git rm --cached --ignore-unmatch path/to/sensitive-file" --prune-empty --tag-name-filter cat -- --all
Đối với các kho lưu trữ lớn hơn, hãy cân nhắc sử dụng BFG Repo-Cleaner, cách này nhanh hơn và đơn giản hơn.
Quan trọng: Sau khi xóa dữ liệu nhạy cảm, bạn sẽ cần phải ép buộc các thay đổi. Ngoài ra, hãy lưu ý rằng dữ liệu có thể vẫn tồn tại trong các bản sao của kho lưu trữ của bạn.
Các phương pháp hay nhất để quản lý lịch sử
Khi nào nên viết lại lịch sử
Thời điểm thích hợp để viết lại lịch sử:
- Trước khi đẩy commit vào kho lưu trữ chung
- Trong một chi nhánh cá nhân chỉ có bạn đang làm việc
- Để dọn dẹp commit của bạn trước khi tạo pull request
- Để xóa thông tin nhạy cảm
Khi KHÔNG viết lại lịch sử:
- Sau khi đẩy tới kho lưu trữ được chia sẻ
- Trên các nhánh mà người khác đang làm việc
- Trên
mainhoặc các nhánh quan trọng khác - Nếu bạn không chắc chắn về việc mình đang làm
Giữ một lịch sử trong sạch
Lịch sử commit rõ ràng, có ý nghĩa giúp bạn dễ hiểu hơn về quá trình phát triển của dự án:
- Viết tin nhắn commit mô tả
- Làm cho mỗi commit thể hiện một sự thay đổi hợp lý
- Bíp "đang tiến hành" commit trước khi hợp nhất
- Rebase các nhánh tính năng trên
maintrước khi hợp nhất - Sử dụng các chiến lược hợp nhất để duy trì lịch sử rõ ràng
Xử lý lịch sử công khai
Đối với các kho lưu trữ công cộng hoặc các nhánh dùng chung:
- Tránh viết lại lịch sử đã bị đẩy
- Sử dụng
git revertđể hoàn tác các thay đổi thay vì viết lại - Hãy cân nhắc sử dụng pull request để đánh giá thay vì buộc phải xóa lịch sử
- Ghi lại những thay đổi đáng kể trong thông báo và ghi chú phát hành commit
Kỹ thuật lịch sử nâng cao
Hái anh đào
Để áp dụng commit cụ thể từ nhánh này sang nhánh khác:
git cherry-pick commit-hash
Điều này tạo ra một commit mới trên nhánh hiện tại của bạn với những thay đổi tương tự như commit được chỉ định.
Sử dụng Rerere (Tái sử dụng độ phân giải đã ghi)
Nếu bạn thường xuyên gặp phải những xung đột hợp nhất tương tự, tính năng rerere của Git có thể giúp:
git config --global rerere.enabled true
Điều này yêu cầu Git ghi nhớ cách bạn giải quyết xung đột để nó có thể tự động giải quyết vào lần tiếp theo.
Tạo bản vá
Để tạo tệp vá để chia sẻ các thay đổi mà không cần đẩy vào kho lưu trữ:
git format-patch -1 HEAD
Điều này tạo ra một tệp như 0001-commit-message.patch.
Để áp dụng một bản vá:
git apply path/to/file.patch
Hoặc để áp dụng và tạo commit:
git am path/to/file.patch
Kết luận
Tính năng theo dõi lịch sử của Git cung cấp hồ sơ vô giá về quá trình phát triển dự án của bạn. Bằng cách tìm hiểu các công cụ này để khám phá và thao tác lịch sử, bạn có thể hiểu rõ hơn cách mã của mình phát triển theo thời gian, gỡ lỗi các vấn đề hiệu quả hơn và duy trì lịch sử rõ ràng, có ý nghĩa.
Hãy nhớ rằng quyền lực lớn đi kèm với trách nhiệm lớn lao, đặc biệt là khi viết lại lịch sử. Luôn thận trọng khi thay đổi commit đã được chia sẻ với người khác và trao đổi rõ ràng khi bạn cần thực hiện những thay đổi đó.
Trong phần tiếp theo, chúng ta sẽ thảo luận về các phương pháp hay nhất về Git để giúp bạn duy trì quy trình làm việc hiệu quả và năng suất.