Facebook Pixel

So sánh Git Rebase & Git Merge: Những điều bạn cần biết

02 Sep, 2023

Khiem.

Author

Cùng 200Lab phân tích cơ chế hoạt động, so sánh ưu nhược điểm giữa Git Merge và Git Rebase, và quy trình làm việc Git nhé!

So sánh Git Rebase & Git Merge: Những điều bạn cần biết

Mục Lục

Cùng 200Lab phân tích cơ chế hoạt động, so sánh ưu nhược điểm giữa Git Merge và Git Rebase, và quy trình làm việc Git nhé!

Trong quá trình làm việc với Git, các lập trình viên thường làm việc độc lập trên các nhánh (branch) khác nhau và để thống nhất code sau khi phát triển tính năng hoặc vá lỗi, cần tiến hành hợp nhất nhánh.

Hiện nay, khi cần tích hợp code từ nhánh này sang nhánh khác, Git hỗ trợ hai tập lệnh cho cùng một mục đích kết hợp công việc của nhiều nhà phát triển thành một mã, tuy nhiên với hai cách tiếp cận hoàn toàn khác nhau: Git MergeGit Rebase.

Trong bài viết này, chúng ta sẽ tiến hành phân tích cơ chế hoạt động, so sánh ưu nhược điểm giữa MergeRebase, từ đó có được góc nhìn đúng đắn về lợi ích cũng như nhìn ra những rủi ro trong quá trình áp dụng thực tế vào những tình huống cụ thể khác nhau.

Xem Thêm Khóa Học Lập Trình

1. Git Merge là gì?

Bắt đầu với quy trình làm việc phổ biến nhất mà hầu hết chúng ta đã quá quen sử dụng để tích hợp các thay đổi trong quá trình hợp nhất nhánh: Git Merge.

Đặt bối cảnh muốn hợp nhất nhánh feature vào nhánh main, chúng ta thực hiện hợp nhất nhánh bằng git merge thông qua các dòng lệnh đơn giản sau đây

Bash
git checkout feature
git merge main

Kết quả sau khi thực thi là git merge tạo ra một commit mới ngay trên nhánh main, commit này gắn kết lịch sử của cả hai nhánh mainfeature  như một quan hệ của sự hợp nhất.

Merge là một hình thức hợp nhất dễ dàng, an toàn và tương đối dễ chịu. Các nhánh hiện có hoàn toàn không được thay đổi trạng thái lịch sử các commit dưới bất kỳ hình thức nào. Điều này tránh được tất cả những cạm bẫy tiềm ẩn của việc tái xây dựng (rebase) lịch sử commit.

Nếu nhánh main hoạt động rất tích cực, điều này có thể làm rối lịch sử commit của nhánh feature khá nhiều. Mặc dù có thể giảm thiểu vấn đề này bằng các tùy chọn git log nâng cao nhưng nó có thể khiến các nhà phát triển khác khó hiểu được lịch sử của dự án.

Hình bên dưới là kết quả cuối cùng từ hành động hợp nhất. Như bạn có thể thấy, lịch sử phát triển tính năng trên nhánh feature vẫn được giữ nguyên như cũ, chỉ thêm C7.

Git Rebase & Git Merge
Bằng cách hợp nhất các thay đổi từ “main” thành “feature-2”, lịch sử được giữ nguyên như những gì đã xảy ra và chỉ có commit hợp nhất “C7” được đưa vào.

Nói một cách dễ hiểu, git Merge sẽ hợp nhất nhiều lịch sử các cây commits tạo thành một commit mới đại diện cho sự hợp nhất và giữ nguyên các trạng thái lịch sử commit cùng tồn tại song song trên các nhánh khác nhau.

Từ đó gây ra hình dạng “kim cương” trong cây Git và cực kỳ khó khăn khi cây Git trở nên thiếu “tuyến tính” trong việc quan sát theo dõi tiến độ dự án.

2. Git Rebase là gì?

Với cách tiếp cận thứ hai cho cùng một nhiệm vụ hợp nhất nhánh, tuy nhiên cũng khá xa lạ với đa số các lập trình viên cũng như tiềm ẩn nhiều rủi ro trong quá trình hợp nhất: Git Merge.

Đặt bối cảnh muốn hợp nhất nhánh feature vào nhánh main, chúng ta thực hiện hợp nhất nhánh bằng git merge thông qua các dòng lệnh đơn giản sau đây.

Bash
git checkout feature
git rebase main

Kết quả sau khi thực thi là git rebase, sẽ đưa toàn bộ những commit mới tạo ở nhánh feature nối tiếp vào "ngọn" của nhánh main, nhưng thay vì sử dụng một commit merge, lịch sử commit của dự án sẽ được viết lại bằng cách tạo ra những commit mới ứng với mỗi commit ban đầu của nhánh feature.

Lợi ích chính của việc rebase là bạn sẽ nhận được một lịch sử commit sạch đẹp, rõ ràng và “tuyến tính” theo một đường thẳng từ đầu đến cuối dự án để dễ theo dõi hơn.  Khi đó chúng ta sẽ dễ dàng điều hướng, kiểm tra lịch sử project với những câu lệnh như git log, git bisectgitk .

Tuy nhiên, có 2 điều cần phải thỏa hiệp đối với lịch sử commit kiểu này: độ an toàn và khả năng truy vết. Nếu chúng ta không tuân theo "nguyên tắc an toàn" khi rebase, việc viết lại lịch sử của dự án có thể là thảm họa khó lường đối với quy trình cộng tác làm việc nhóm.

Một điều ít quan trọng hơn, rebase sẽ làm mất đi ngữ cảnh mà commit merge cung cấp, từ đó chúng ta sẽ không biết được khi nào những thay đổi ở nhánh tích hợp được đưa vào nhánh chủ đề.

Bên dưới là kết quả cuối cùng từ hành động rebase. Lưu ý cách các cam kết C5 và C6 đã được áp dụng lại thẳng vào C4, viết lại lịch sử phát triển và xóa hoàn toàn các cam kết cũ!

Git Rebase & Git Merge
Lưu ý mã băm của các commit C5 và C6 đã thay đổi, đơn giản vì thực tế đó là các cam kết mới đã được tạo (mặc dù nội dung có thể vẫn giống hệt nhau).

3. So sánh Git Rebase và Git Merge

Quan sát hình ảnh bên dưới cách trực quan, kết quả của cả hai quá trình MergeRebase đều giúp hợp nhất và thống nhất code giữa 2 nhánh trong quá trình làm việc.

Với Merge, tổng số lượng commit tăng 1 đơn vị và hình thành đồ thị Git dạng “kim cương”. Với Rebase, tổng số lượng commit không đổi và hình thành đồ thị Git dạng tuyến tính theo một đường thẳng.

Git Rebase & Git Merge
Lợi ích của rebase là nó có 𝐥𝐢𝐧𝐞𝐚𝐫 𝐜𝐨𝐦𝐦𝐢𝐭 𝐡𝐢𝐬𝐭𝐨𝐫𝐲.

3.1. Ưu điểm của Git Merge

  • Không phá hủy: Hợp nhất là một hoạt động không phá hủy trong Git vì nó không thay đổi các nhánh hiện có. Nó chỉ thêm một cam kết bổ sung gọi là cam kết hợp nhất.
  • Thay đổi tích hợp: Việc hợp nhất cho phép người dùng tích hợp các thay đổi từ nhánh này sang nhánh khác. Việc tích hợp này rất hữu ích nếu nhiều nhà phát triển đang làm việc trên các tính năng khác nhau cần được hợp nhất vào nhánh chính.
  • Nhiều phiên bản cơ sở mã: Việc hợp nhất cho phép người dùng giữ nhiều phiên bản cơ sở mã. Điều này hữu ích nếu cần có các phiên bản mã cũ hơn hoặc nếu bạn cần một nhánh riêng để kiểm tra tính năng.
  • Thay đổi theo dõi: Việc hợp nhất cho phép người dùng theo dõi những thay đổi đã được thực hiện đối với cơ sở mã. Việc theo dõi rất hữu ích cho việc gỡ lỗi hoặc kiểm tra.
  • Giải quyết xung đột: Hợp nhất là một cơ chế giải quyết xung đột tuyệt vời cho phép người dùng hợp nhất các thay đổi mà nhiều nhà phát triển đã thực hiện trên cùng một tệp.

3.2. Nhược điểm của Git Merge

  • Hợp nhất xung đột: Một trong những nhược điểm chính của git merge là khả năng xảy ra xung đột khi hợp nhất khi thực hiện nhiều thay đổi trên cùng một tệp. Đôi khi, việc giải quyết những xung đột như vậy có thể tốn thời gian và khó khăn.
  • Mất bối cảnh: Khi những thay đổi từ hai nhánh được hợp nhất, một số ngữ cảnh của những thay đổi có thể bị mất. Do đó, lịch sử cơ sở mã và nguồn gốc của một số thay đổi có thể khó theo dõi hơn.
  • Sự phức tạp: Độ phức tạp của cơ sở mã tăng theo số lượng nhánh và sự hợp nhất, điều này làm tăng độ khó bảo trì và làm phức tạp mối quan hệ giữa các nhánh.
  • Sự phụ thuộc: Việc hợp nhất nhiều nhánh thành một có thể tạo ra sự phụ thuộc giữa các phần khác nhau của cơ sở mã. Điều này có thể cản trở việc thử nghiệm và triển khai thay đổi hơn nữa vì những thay đổi trong một phần của cơ sở mã có thể ảnh hưởng đến các phần khác.

3.3. Ưu điểm của Git Rebase

  • Lịch sử dự án tuyến tính: Lợi ích chính của việc khởi động lại Git là lịch sử dự án sạch sẽ vì lệnh này loại bỏ các cam kết hợp nhất không cần thiết. Kết quả là một lịch sử dự án hoàn toàn tuyến tính, không có bất kỳ nhánh nào.
  • Cơ sở mã đơn giản hóa: Lịch sử tuyến tính giúp bạn dễ dàng hiểu cơ sở mã và truy tìm nguồn gốc của những thay đổi cụ thể.
  • Giải quyết xung đột hợp nhất: Lệnh git rebase áp dụng các thay đổi từ nhánh này lên nhánh khác. Điều này có nghĩa là xung đột hợp nhất được đơn giản hóa và các thay đổi được áp dụng theo cách có trật tự hơn so với hợp nhất git.
  • Các nhánh tính năng riêng biệt: Việc rebase có thể được sử dụng để tách các nhánh tính năng trên nhánh chính. Việc tách chúng ra giúp quản lý nhiều nhánh dễ dàng hơn và cập nhật chúng với những thay đổi mới nhất trong nhánh chính.
  • Uyển chuyển: git rebase linh hoạt hơn git merge trong việc quản lý các nhánh và thực hiện các thay đổi vì nó cho phép người dùng sắp xếp lại hoặc sửa đổi các cam kết, thay đổi thông báo cam kết, v.v.

3.4. Nhược điểm của Git Rebase

  • Có thể có xung đột hợp nhất: Việc khởi động lại một quy trình công việc có thể gây ra xung đột hợp nhất thường xuyên hơn nếu có một nhánh tồn tại lâu dài đã đi xa khỏi nhánh chính. Nếu nhánh chứa nhiều cam kết mới, chúng có thể xung đột với nhánh chính. Để tránh những vấn đề như vậy, hãy thường xuyên khởi động lại các nhánh của bạn so với nhánh chính.
  • Mất cam kết: Chạy git rebase ở chế độ tương tác với các lệnh phụ loại bỏ các cam kết khỏi nhánh có thể gây ra các cam kết bị mất trong nhật ký tức thời của nhánh. Tuy nhiên, các cam kết thường có thể được khôi phục bằng cách hoàn tác rebase bằng git reflog.
  • Thiếu thông tin cam kết: Sau khi khởi động lại, bạn không thể biết khi nào các thay đổi ngược dòng được thực hiện và khi nào chúng được tích hợp vào nhánh tính năng.

4. Quy trình làm việc với Git Rebase

Chúng ta đã thấy cách rebase viết lại lịch sử trong khi việc hợp nhất vẫn bảo tồn nó. Nhưng điều này thực sự có ý nghĩa gì theo nghĩa rộng hơn. Và hoạt động này có những khả năng vô hạn và cũng tồn tại nhiều hạn chế tiềm tàng. Vì thế chúng ta cần phải cực kỳ lưu ý và nắm rõ quy trình làm việc cần tuân thủ khi làm việc với Git Rebase.

4.1. Local cleanup

Trong quá trình phát triển một tính năng trên branch riêng, các lập trình viên có thể có nhiều commit. Để cây Git được clean và gọn hơn, chúng ta cần tiến hành squash commit thông qua tính năng tự rebase trên chính nhánh feature .

Ví dụ bạn có 3 commits liên tục cần gộp lại 1 commit, thực hiện lệnh sau:

Bash
git switch feature
git rebase -i HEAD~3

Màn hình hiển thị một tệp editor hiển thị lịch sử commits, chúng ta tiến hành cỉnh sửa file theo syntax. Các options bao gồm:

  • p: pick - giữ lại commit
  • r: reword - giữ lại commit và sửa message
  • s: squash - bỏ qua commit nhưng tích hợp log vào commit liền trước
  • f: fixup - bỏ qua commit và xoá hoàn toàn log commit
Bash
pick 1fc6c95 Patch A
pick 6b2481b Patch B
pick dd1475d something I want to split
pick c619268 A fix for Patch B
pick fa39187 something to add to patch A
pick 4ca2acc i cant' typ goods
pick 7b36971 something to move before patch B

# Rebase 41a72e6..7b36971 onto 41a72e6
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Tiến hành giữ lại commit đầu tiền và squash toàn bộ các commits liền sau bằng cách thay thế pick thành squash. Lưu file :wqvà thoát qa!.

4.2. Rebasing from main

Tiếp theo, sau khi đã gom tất cả các commit của mình làm một chúng ta bắt đầu tiến hành rebase so với branch main. Lưu ý, trước đó ta cần nhảy sang nhánh mainvà tiến hành pull code từ remote để cập nhật các thay đổi mới nhất trên main.

Bash
git switch main
git pull origin main
git switch feature
git rebase -i main feature

4.3. Push force to feature

Sau khi đã xử lý các conflicts liên quan và squash các commit mong muốn, lúc này các commits trên mainđã được cắt nối tuyến tính vào ngay đầu commit trên feature . Khi đó, chúng ta sẵn sàng push lên remote và sẵn sàng tạo Merge/Pull request

Bash
git push origin feature --force 

Lưu ý:

  • Sau khi rebase, lịch sử commit local trên nhánh feature đã thay đổi và conflict so với nhánh feature trên remote, vì thế ta cần push force để ghi đè toàn bộ cây Git trên branch featue.

4.4. Create merge/pull request

Lưu ý:

  • Trong quá trình tạo PR/MR, quá trình approve cần được diễn ra lần lượt có thứ tự và các PR/MR còn lại cần lập tức rebase ngay khi 1 PR/MR đã được merge vào main.
Git Rebase & Git Merge

5. Kết luận về Git Rebase & Git Merge

Cả hai phương pháp đều giúp đạt được mục tiêu hợp nhất các thay đổi từ một nhánh vào nhánh chính trong quy trình làm việc cần thống nhất code giữa các lập trình viên. Thông qua bài viết hi vọng là chúng ta đã có thể hiểu rõ nguyên lý hoạt động khác nhau và cân nhắc sự lựa chọn phù hợp tuỳ theo nhu cầu dự án.

Nếu chúng ta cần ưu tiên một cây Git sạch sẽ, gọn gàng và “tuyến tính” dễ dàng theo dõi theo tiến độ dự án và không có sự dư thừa những commit merge thì Git rebase là một lựa chọn tối ưu và thông minh.

Ngược lại, nếu chúng ta cần ưu tiên bảo toàn lịch sử đầy đủ của dự án và tránh những nguy cơ mất mát dữ liệu và không ngại các hình dạng “kim cương” rối mắt trên cây Git khi merge chéo qua lại giữa các branch thì Git merge là một lựa chọn đơn giản và hiệu quả.

Tài liệu tham khảo:

Lựa chọn giữa Git Rebase và Git Merge không phải lúc nào cũng đơn giản, bởi mỗi phương pháp đều có ưu điểm và hạn chế riêng. Dù bạn là người mới bắt đầu hay đã quá sành sỏi về Git, việc hiểu rõ cách sử dụng cả hai phương pháp này là vô cùng quan trọng. Hy vọng rằng bài viết này đã cung cấp cho bạn cái nhìn tổng quan về Git Rebase và Git Merge, giúp bạn đưa ra quyết định đúng đắn và tối ưu hóa quy trình làm việc của mình.

Xem Thêm Khóa Học Lập Trình

Hãy thường xuyên theo dõi trang Blog của 200Lab để thu thập thêm các kiến thức hữu ích về công nghệ, dữ liệu và lập trình nhé.

Một vài bài viết có thể bạn sẽ thích:

Bitbucket là gì? GitHub là gì? So sánh Bitbucket và GitHub
Cách giải quyết lỗi password authentication Github
Frontend Du Ký | EGANY Apps : Đừng thấy hoa nở mà ngỡ xuân về
Mobile & Web UI Kit For Flutter (100+ screens)
Tự học Dart: Các Dart Operators (toán tử) bạn cần biết

Bài viết liên quan

Lập trình backend expressjs

xây dựng hệ thống microservices
  • Kiến trúc Hexagonal và ứng dụngal font-
  • TypeScript: OOP và nguyên lý SOLIDal font-
  • Event-Driven Architecture, Queue & PubSubal font-
  • Basic scalable System Designal font-

Đăng ký nhận thông báo

Đừng bỏ lỡ những bài viết thú vị từ 200Lab