[Git nâng cao] Hướng dẫn Git Rebase: Hiểu rõ và sử dụng hiệu quả

VnnTools

Trong thế giới lập trình với Git, git merge giống như một ngã tư đường, nơi các nhánh lịch sử giao nhau và tạo ra một điểm chung mới. Nó an toàn, dễ hiểu và ghi lại mọi thứ một cách trung thực. Nhưng đôi khi, sự trung thực đó lại tạo ra một lịch sử commit lộn xộn, phức tạp và khó theo dõi.

Thêm một phép so sánh nhỏ, nếu git merge là một nhà sử học cần mẫn, thì git rebase chính là một đạo diễn tài ba, cho phép bạn "cắt, ghép, sắp xếp" lại các cảnh phim (commit) để tạo ra một câu chuyện (lịch sử commit) liền mạch, sạch sẽ và dễ hiểu hơn.

Hướng dẫn Git Rebase: Hiểu rõ và sử dụng hiệu quả

Bài viết này sẽ giúp bạn làm chủ công cụ mạnh mẽ git rebase.

1. Hiểu cốt lõi vấn đề: Git Rebase là gì? 🌿

Về cơ bản, Git Rebase là quá trình di chuyển hoặc kết hợp một chuỗi các commit sang một commit cơ sở (base) mới.

Hãy tưởng tượng bạn có một nhánh chính (main) và bạn tạo ra một nhánh mới (feature) để phát triển tính năng. Trong lúc bạn đang làm việc trên feature, nhánh main đã có thêm những commit mới từ các thành viên khác trong nhóm.

Lúc này, lịch sử của bạn trông như một cái cây rẽ nhánh. Bạn có hai lựa chọn để cập nhật nhánh feature của mình với những thay đổi mới nhất từ main:

  • git merge main: Git sẽ tạo ra một "merge commit" mới trên nhánh feature. Commit này có hai "cha": commit cuối cùng của feature và commit cuối cùng của main. Nó giống như việc bạn buộc hai cành cây lại với nhau. Lịch sử sẽ ghi lại chính xác thời điểm hợp nhất này.
  • git rebase main: Đây là lúc phép màu xảy ra. Thay vì tạo merge commit, Rebase sẽ:
    1. Tạm thời "nhấc" tất cả các commit mà bạn đã tạo trên nhánh feature ra.
    2. Di chuyển con trỏ của nhánh feature đến commit mới nhất của main.
    3. Áp dụng lại (replay) từng commit bạn đã "nhấc" ra lên trên đỉnh của main.

Kết quả? Lịch sử commit trên nhánh feature của bạn giờ đây trông như thể bạn vừa mới tạo ra nó từ commit mới nhất của main. Nó trở thành một đường thẳng tắp, sạch sẽ và liền mạch.

Git Rebase là gì?

Phép so sánh dễ hiểu: Hãy coi main là thân cây chính. Nhánh feature của bạn mọc ra từ điểm A trên thân cây. Khi thân cây mọc cao thêm đến điểm B, rebase giống như việc bạn cẩn thận "bứng" cả nhánh feature của mình và "ghép" nó vào điểm B. Nhánh của bạn vẫn giữ nguyên các lá (commit) nhưng giờ đây nó mọc ra từ vị trí mới nhất của thân cây.

2. Tại sao phải dùng Git Rebase? 🚀

Việc giữ một lịch sử commit "thẳng và sạch" không chỉ là vấn đề thẩm mỹ. Nó mang lại những lợi ích vô cùng thực tế:

  • Dễ đọc và dễ hiểu: Một lịch sử tuyến tính giúp bất kỳ ai (kể cả bạn trong tương lai) cũng có thể dễ dàng đọc và hiểu dòng chảy phát triển của dự án mà không bị rối bởi hàng tá merge commit vô nghĩa.
  • Debug hiệu quả hơn: Khi cần tìm một bug, các công cụ như git bisect hoạt động hiệu quả hơn rất nhiều trên một lịch sử tuyến tính.
  • Code review dễ dàng hơn: Khi bạn tạo một Pull Request (Merge Request), người review sẽ thấy một chuỗi commit gọn gàng, mỗi commit thực hiện một thay đổi logic. Điều này giúp họ dễ dàng theo dõi và góp ý hơn.
  • Loại bỏ "nhiễu": Rebase giúp loại bỏ các commit không cần thiết như "Merge branch 'main' into feature".

3. Cách sử dụng Git Rebase cơ bản

Kịch bản phổ biến nhất là cập nhật nhánh feature của bạn với những thay đổi mới nhất từ nhánh main.

Các bước thực hiện:

  1. Đầu tiên, đảm bảo nhánh main cục bộ của bạn được cập nhật:
git checkout main
git pull origin main
  1. Chuyển về nhánh feature của bạn:
git checkout feature
  1. Thực hiện rebase:
git rebase main

Xử lý xung đột (Conflict):

Trong quá trình rebase, Git sẽ áp dụng lại từng commit của bạn. Nếu một trong các commit đó thay đổi cùng một dòng code mà commit trên main cũng đã thay đổi, xung đột sẽ xảy ra.

Đừng hoảng sợ! Git sẽ tạm dừng quá trình rebase và cho bạn biết file nào đang bị xung đột.

  1. Mở các file bị xung đột và sửa chúng. Bạn cần xóa các dấu <<<<<<<, =======, >>>>>>> và chọn phiên bản code cuối cùng mà bạn muốn giữ lại.
  2. Sau khi sửa xong, hãy thêm các file đã sửa vào staging area:
    git add <tên-file-đã-sửa>
    
  3. Tiếp tục quá trình rebase:
    git rebase --continue
    
  4. Nếu bạn cảm thấy quá rối và muốn quay lại trạng thái trước khi rebase, hãy dùng lệnh:
    git rebase --abort
    

4. Sức mạnh "tối thượng": Git Interactive Rebase 💡

Đây mới chính là "viên ngọc quý" của rebase. Rebase tương tác cho phép bạn toàn quyền chỉnh sửa chuỗi commit của mình trước khi áp dụng chúng.

Lệnh cơ bản: git rebase -i <commit-cơ-sở>

Ví dụ, bạn muốn dọn dẹp 5 commit gần nhất trên nhánh của mình. Bạn có thể dùng HEAD~5:

git rebase -i HEAD~5

Một file văn bản sẽ mở ra trong editor của bạn, liệt kê 5 commit đó, mỗi commit bắt đầu bằng từ pick.

pick a1b2c3d Thêm tính năng A - phần 1
pick f4e5d6c Sửa lỗi nhỏ
pick 9h8g7f6 Thêm test cho A
pick c1b2a3d Fix typo
pick e5f6g7h Hoàn thiện tính năng A

Bây giờ bạn có thể thay đổi pick thành các lệnh khác để "viết lại lịch sử":

Git Interactive Rebase

  • reword (hoặc r): Giữ nguyên commit nhưng sửa lại commit message.
  • edit (hoặc e): Dừng lại ở commit này để bạn có thể sửa code (ví dụ: git commit --amend).
  • squash (hoặc s): Gộp commit này vào commit phía trên nó. Git sẽ mở một editor mới để bạn viết lại commit message cho commit đã gộp. Đây là lệnh cực kỳ hữu ích để gộp các commit nhỏ, vụn vặt ("fix typo", "wip",...) thành một commit lớn có ý nghĩa.
  • fixup (hoặc f): Tương tự squash, nhưng sẽ loại bỏ commit message của commit này và chỉ giữ lại message của commit phía trên.
  • drop (hoặc d): Xóa hoàn toàn commit này.
  • Bạn cũng có thể thay đổi thứ tự các dòng để sắp xếp lại các commit.

Ví dụ dọn dẹp:

pick a1b2c3d Thêm tính năng A - phần 1
squash 9h8g7f6 Thêm test cho A       # Gộp commit test vào commit "phần 1"
squash e5f6g7h Hoàn thiện tính năng A  # Gộp nốt commit hoàn thiện vào
drop f4e5d6c Sửa lỗi nhỏ             # Bỏ commit sửa lỗi không cần thiết
drop c1b2a3d Fix typo                # Bỏ luôn commit sửa typo

Sau khi lưu và đóng file, Git sẽ thực hiện các hành động trên. Kết quả là từ 5 commit lộn xộn, bạn chỉ còn lại 1 commit duy nhất, sạch sẽ với một message hoàn chỉnh: "Thêm tính năng A".

5. Quy tắc vàng của Rebase ⚠️

Rebase là một công cụ thay đổi lịch sử. Điều này mang lại một quy tắc tối quan trọng, bất khả xâm phạm:

TUYỆT ĐỐI KHÔNG REBASE TRÊN CÁC NHÁNH ĐÃ ĐƯỢC CHIA SẺ (PUBLIC/SHARED BRANCHES).

Các nhánh như main, develop, hoặc bất kỳ nhánh nào mà các thành viên khác trong nhóm đang làm việc dựa trên đó đều là bất khả xâm phạm.

Tại sao? Khi bạn rebase, bạn đang tạo ra các commit hoàn toàn mới (dù nội dung giống hệt) và xóa đi các commit cũ. Nếu người khác đã clone hoặc pull nhánh cũ đó, lịch sử của họ và của bạn sẽ bị phân kỳ. Khi họ cố gắng pull code mới, Git sẽ bị "bối rối", gây ra xung đột cực lớn và có thể tạo ra các commit trùng lặp.

Quy tắc đơn giản: Chỉ rebase trên các nhánh cục bộ, của riêng bạn, mà bạn chưa đẩy lên remote hoặc chưa ai khác làm việc cùng.

Khi nào dùng Rebase, khi nào dùng Merge? 🏆

Đây là câu hỏi muôn thuở, và câu trả lời phụ thuộc vào quy trình làm việc của nhóm bạn. Tuy nhiên, một quy trình phổ biến và hiệu quả là:

  • Dùng Rebase khi:

    • Cập nhật nhánh feature cá nhân của bạn với những thay đổi từ nhánh chính (main/develop).
    • Dọn dẹp lịch sử commit cục bộ của bạn trước khi tạo Pull Request.
  • Dùng Merge khi:

    • Hợp nhất một nhánh feature đã hoàn thành và được review vào nhánh chính (main/develop). Việc này tạo ra một merge commit, ghi lại dấu vết rõ ràng rằng "tính năng X đã được hợp nhất tại thời điểm Y".

Khi nào dùng Rebase, khi nào dùng Merge?

Nói cách khác: Rebase để giữ lịch sử bên trong nhánh feature sạch sẽ, và Merge để hợp nhất kết quả cuối cùng của nhánh feature vào lịch sử chung.

Kết luận: Git Rebase không hề đáng sợ

Bạn thấy đó, git rebase không hề đáng sợ như lời đồn! Nó là một kỹ năng nâng cao giúp bạn từ một người "biết dùng Git" trở thành một người "làm chủ Git". Bằng cách giữ cho lịch sử commit luôn sạch sẽ, tuyến tính và có ý nghĩa, bạn không chỉ giúp bản thân mà còn giúp cả nhóm làm việc hiệu quả hơn, ít lỗi hơn và dễ dàng bảo trì dự án trong dài hạn.

Hãy bắt đầu thực hành với các nhánh cá nhân của bạn, và bạn sẽ nhanh chóng nhận ra sức mạnh của việc "viết lại lịch sử" một cách có chủ đích.

Bài viết liên quan

[Git nâng cao] Git Reset hay Git Revert? Lựa chọn hoàn tác đúng cách

Không còn nhầm lẫn giữa git reset và git revert nữa! Hướng dẫn này sẽ giúp bạn hiểu rõ cách hoạt động của từng lệnh, từ đó lựa chọn công cụ đúng đắn để hoàn tác thay đổi trong Git một cách chuyên nghiệp.

[Git cơ bản] Tệp tin .gitignore là gì? Cách sử dụng hiệu quả cho mọi dự án

Học cách sử dụng tệp tin .gitignore để bỏ qua các file tạm, file log, và các thư mục không cần thiết. Đơn giản hóa dự án và giữ cho Git repository của bạn luôn sạch sẽ.

[Git cơ bản] Học thành thạo Git Merge với hướng dẫn và ví dụ chi tiết

Bạn gặp khó khăn với Git Merge? Tham khảo ngay bài hướng dẫn cách sử dụng Git Merge để hợp nhất code và giải quyết các xung đột một cách đơn giản, hiệu quả khi làm việc nhóm.

[Git cơ bản] Git Workflow: Hướng dẫn chi tiết các mô hình làm việc nhóm phổ biến

Hướng dẫn cách áp dụng các Git Workflow hàng đầu. Nắm vững Git Flow, GitHub Flow, GitLab Flow để tối ưu hóa quy trình phát triển, đảm bảo chất lượng code và tăng tốc độ dự án.