Series Git Nâng Cao - phần III: git rebase

Git rebase là một chức năng của Git, được sử dụng để gộp các commit từ nhánh này vào nhánh khác, bằng cách xây dựng lại các commit base kế thừa từ nhánh khác và viết lại lịch sử commit sau các commit cơ sở mới.

Series Git Nâng Cao - phần III: git rebase

1. Git rebase là gì ?

  • Git rebase là một chức năng của Git, được sử dụng để gộp các commit từ nhánh này vào nhánh khác, bằng cách xây dựng lại các commit base kế thừa từ nhánh khác và viết lại lịch sử commit sau các commit cơ sở mới.
  • Rebasing là quá trình di chuyển hoặc kết hợp một chuỗi các commit thành một commit cơ sở mới.

2. Sử dụng git rebase khi nào ?

Giả sử ta có tình huống như sau:
Before rebase

  • Khi bắt đầu làm việc tại branch feature thì tại branch master đã có những commit mới. Và bây giờ ta muốn cập nhật những commit mới này từ branch master vào branch feature mà vẫn muốn giữ lại các lịch sử commit của nó. Lúc này git rebase thực sự hữu dụng.

Sau khi rebase master, branch feature sẽ trở thành như sau:
After rebase

  • Lúc này, các commit mới tại branch master đã trở thành các commit base của branch feature

Tác dụng của lưu lại lịch sử commit

  • Khi một lập trình viên được join vào dự án, họ có thể dùng git log để hiểu được tiến trình của dự án
  • Một tính năng trước đang làm việc hiệu quả thì giờ không hoạt động được. Nhờ các lịch sử chỉnh sửa không bị thay đổi vị trí nên dev có thể xác định lỗi nhanh hơn.

3. Phân loại git rebase

Có 2 loại là: Git Rebase Standard và Git Rebase Interactive

Git Rebase Standard (tiêu chuẩn)

  • Git Rebase tiêu chuẩn là khi chức năng này được thực thi không kèm theo đối số.
  • Git Rebase tiêu chuẩn sẽ tự động lấy những commit trong branch bạn đang làm việc và để chúng lên đầu của branch đã hoàn thành khi gộp.
  • Cú pháp:
git rebase -i <branch>

Git Rebase Interactive (tương tác)

  • Git Rebase tương tác là khi chức năng này được thực thi với đối số -i.
  • Với Git Rebase interactive, thay vì cứ tự động di chuyển tất cả các commit tới base mới, nó cho phép lựa chọn các commits riêng biệt trong quá trình đó. Điều này giúp các bạn dọn dẹp lịch sử chỉnh sửa gọn gàng hơn bằng cách xoá bỏ, hoặc chia tách và lựa chọn một chuỗi các commits đang tồn tại.
  • Cú pháp:
git rebase -i [base]

hoặc

git rebase --interactive [base]
  • Ví dụ muốn rebase lại 4 commit gần nhất của nhánh hiện tại thì:
git rebase -i HEAD~4

4. Một số cách sử dụng git rebase

a. Git Rebase Standard
Tại branch master, tạo file file_1.js và thực hiện commit với message là C1, sau đó tạo file file_2.js và thực hiện commit với message là C2.

create-file-4

Lúc này, branch master có 2 commit là C1 và C2. Sử dụng câu lệnh git log --oneline để kiểm tra:
git-log-master

Tạo branch feature từ branch master. Lúc này các commit C1 và C2 là commit base của branch feature
git-log-feature

Tại branch feature, chỉnh sửa file file_1.js và thực hiện commit với message là C3, tiếp tục chỉnh sửa file file_2.js và thực hiện commit với message là C4
C3-and-C4

Sử dụng câu lệnh git log --oneline để kiểm tra, branch feature có thêm các commits là C3 và C4
new-1

Checkout về branch master, thực hiện sửa đổi file_1.js và thực hiện commit với message là C5, tiếp tục chỉnh sửa file file_2.js và thực hiện commit với message là C6
C5-and-C6

Sử dụng git log --oneline để kiểm tra:
C5-and-C6

Tại branch master, thực hiện gộp branch feature vào branch master
Câu lệnh:

git rebase feature>

Nếu không xảy ra conflic, việc rebase sẽ được hoàn thành.
Nếu conflict xảy ra tại một hoặc nhiều commit thì commit nào có conflict thì rebasing sẽ tạm dừng ở đó.

rebase

Khi xuất hiện conflict, ta có thể giải quyết bằng 1 trong những cách sau:

  • Fix conflic, lưu thay đổi và thực thi lệnh git rebase --continue để tiếp tục rebasing.
  • Thực thi câu lệnh git rebase --skip để bỏ qua commit và tiếp tục rebasing.
  • Thực thi git rebase --abort để quay lại trạng thái trước khi rebase

Ở đây ta thực hiện fix conflict, dùng git add -A để lưu sự thay đổi và thực thi git rebase --continue.

Lúc này các commits mới của branch feature sẽ được nối tiếp vào commit base của branch master và trở thành commit base của branch này. Như vậy, commit base của branch master gồm: C1, C2, C3, C4. Nối tiếp chuỗi commit base là các commits mới của của branch master là C5, C6 và ta có thể thay đổi message của commit C5.

Sau khi thực thi git rebase --continue, trình soạn thảo sẽ hiện ra để ta có thể thay đổi message của commit C5.
edit-message

ta có thể sửa message này bằng cách thêm # vào trước message cũ và viết lại message mới. Ví dụ đổi C5 thành C5'

edit-message-C5-

Thực hiện xong bước trên, nếu được thông báo successfully rebased nghĩa là rebase đã thành công. Nếu xuất hiện thêm conflicts, ta chỉ cần thực hiện lặp lại các bước trên đến khi việc rebase hoàn thành.

Sau khi hoàn thành rebase, dùng câu lệnh git log --oneline để kiểm tra:
git log after rebase

=> Công việc rebase được hoàn thành.

b. Git Rebase Interactive

Trước khi sử dụng git rebase thì working directory phải đang sạch sẽ. Nếu như bạn đang làm dở công việc mà muốn rebase thì có thể sử dụng git stash để lưu tạm công việc còn dang dở vào stash list. Sau đó, có thể lấy ra để tiếp tục công việc.

Giả sử ta có một repository có rất nhiều commit như sau:

git-repo

Để rebase lại 4 commit gần nhất là C3, C4, C5, C6
Câu lệnh:

git rebase -i HEAD~4

Lúc này, trình soạn thảo hiện ra như bên dưới
pick-commit

Chúng ta có một số lựa chọn như sau:

  • p/pick: giữ nguyên commit.
  • r/reword: giữ commit và edit lại commit message.
  • e/edit: giữ commit nhưng dừng lại để sửa đổi.
  • s/squash: gộp commit này với commit trước đó, giữ lại commit message.
  • f/fixup: tương tự squash nhưng hủy luôn commit message của commit này.

Ở đây ta gộp commit C5 và C4 vào C3 và chỉnh sửa lại message của C6, ta thực hiện sửa từ pick ở đầu C4, C5 thành sr ở đầu C6. Lưu và thoát trình soạn thảo.
option-rebase

Sau bước trên, trình soạn thảo mới sẽ xuất hiện để ta chỉnh sửa message cho các commit được gộp.
edit-message-commits

Ban đầu ta có 3 commit với 3 message khác nhau, giờ chúng ta gộp chúng lại thành một thì chúng ta nên thay bằng một message rõ ràng hơn. Ta chỉnh sửa bằng cách thêm kí tự # vào đầu các message cũ và viết message mới cho commit sau khi gộp. Lưu và thoát trình soạn thảo.
add-message-new-commit

Tiếp theo, một trình soạn thảo mới được hiện ra để ta thay đổi message của commit C6.
edit-message-C6

Thực hiện chỉnh sửa message. Lưu và thoát.
edit-message-C6-N

Khi có thông báo Successfully rebased thì công việc được hoàn thành

done

Sử dụng git log --oneline để kiểm tra. Các commit C3, C4, C5 đã được gộp thành 1 commit mới và message commit C6 đã được sửa đổi.
log-after-rebase

Lưu ý:

  • Sau khi rebase thành công, để có thể push lên server ta phải thêm option -f để ghi đè lên các commit trước git push -f origin <branch>
  • Quá trình gộp này khá nguy hiểm nếu nhánh làm việc của bạn đã được push lên remote trước đó, vì vậy tốt nhất là các bạn nên gộp trên local branch trước khi các bạn push lên remote.
  • Quá trình rebasing có thể gây mất commit nên phải cẩn thận khi sử dụng git rebase.
  • Nếu đã push lên trước đó sau đó mới gộp các commit thì có thể dẫn đến việc lịch sử commit trên remote branch và local branch là khác nhau, lúc này khi pull code mới của branch này các bạn không thể pull theo cách thông thường , các bạn có thể dùng git reset --hard origin <branch>.

Tài liệu tham khảo: