Sử dụng Git Rebase thay cho Merge

By | April 2, 2020

Giả sử team bạn đang làm có 3 người, 1 người là leader và 2 người làm feature. Trong mỗi sprint, team bạn có một nhánh code cho sprint dùng để merge kết quả của mọi người trong team làm vào và leader sẽ là người chịu trách nhiệm việc merge đó.

Giả sử team bạn có 3 người là L (leader), A (người làm feature A), C (người làm feature C) và team bạn đang làm thế này:

  • Từ sprint checkout ra nhánh feature cho từng người
  • Khi A làm xong feature A thì thông báo cho leader merge code của mình vào sprint
  • Leader merge code của A vào và thông báo cho mọi người pull về từ nhánh sprint
  • C đang làm dở nhánh feature C và cần cập nhật code mới theo thông báo của leader
  • Leader thêm một chút sửa đổi vào phần chung của project và thông báo mọi người cập nhật. A cập nhật một kiểu, C cập nhật một kiểu và khi merge, leader phải giải quyết conflict nhiều hơn
  • A và B đều đang làm dở tính năng của mình, có sửa 1 chút liên quan đến file chung (thêm 1 file ignore vào .gitignore chẳng hạn), và cập nhật code từ leader theo yêu cầu

Câu chuyện là: Các commit được gộp vào theo trình tự thời gian. Vì vậy:

  • các commit trong feature A có thể đan xen vào các commit trong feature C, nhìn vậy chắc hơi rối nhỉ? :/
  • Khi A làm xong mà merge vào nhánh chính, rồi C phải merge về nhánh của mình, C sẽ phải giải quyết conflict giữa các commit của A với các commit của C đã thực hiện. Nếu merge đơn thuần mà các commit này đan xen và conflict xảy ra nhiều, C sẽ rất khó khăn để đảm bảo việc merge mà không làm ảnh hưởng ngược lại hỏng feature của A
  • Giả sử C sẽ xong trước khi A được leader merge vào nhánh chính, lúc này leader sẽ phải merge lần lượt cả A và C vào nhánh chính và sẽ là người phải chịu gánh nặng trên: làm sao giải quyết conflict giữa 2 nhánh của A và C mà đảm bảo không ảnh hưởng đến tính năng của ai.

Giả sử project của chúng ta đang có worktree như thế này:

  • Khi đó, nếu merge vào theo cách thông thường, worktree trông sẽ như thế này:


Vấn đề là các commit tuần tự nhau, vì vậy:

  • Khi merge A vào nhánh chính (sprint0), leader phải giải quyết commit conflict giữa nhánh chính (sprint0 được checkout từ master) và nhánh A
  • Sau khi merge xong bước trên, leader sẽ phải giải quyết tiếp nhánh đã được merge với C, làm sao không break tính năng của nhánh chính và tính năng A vừa được merge thành công, đồng thời không làm mất code của ai khi xảy ra conflict với C

Để giải quyết được vấn đề trên, chúng ta có thể sử dụng rebase:
Khi sử dụng rebase, commit từ các nhánh riêng sẽ được gom lại thành một cục, và các nhánh được sắp xếp theo thời điểm mà nó được rebase. Vì vậy:

  • Nhánh chính sprint được tạo ra trước. Vì vậy khi có một commit mới được đẩy vào nhánh chính, chẳng hạn merge A vào sprint0 mà C rebase về nhánh mình, thì các commit của A sẽ được tính là nằm trước các commit của C, kể cả các commit đó được tạo ra sau. Và lúc này C có thể coi như mình vừa checkout mới khi A đã có sẵn rồi. C chỉ cần giải quyết conflict file làm sao cho không xóa code của A đi là được
  • Khi A và C đều tự rebase về nhánh mình, công việc của leader sẽ giảm đi bội phần, vì khi merge tuần tự thì A đã rebase trước với sprint, leader merge code của A vào sprint chắc chắn chạy được, C đã tự rebase với sprint và A, vì vậy sau khi merge A, độ phức tạp của việc merge C với nhánh chính cũng giảm đi rất nhiều.
  • Và kết quả rebase như sau:

    Rõ ràng là thẳng tắp theo thứ tự, phải không? 😀

Chúc mọi người làm việc vui vẻ 😀

Leave a Reply