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 Merge
và Git 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 Merge
và Rebase
, 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.
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
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 main
và feature
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.
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.
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 bisect
và gitk
.
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ũ!
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 Merge
và Rebase
đề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.
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 gitmerge
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ácrebase
bằnggit 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:
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
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 :wq
và 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 main
và tiến hành pull code từ remote để cập nhật các thay đổi mới nhất trên main
.
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
git push origin feature --force
Lưu ý:
- Sau khi
rebase
, lịch sử commit local trên nhánhfeature
đã thay đổi và conflict so với nhánhfeature
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àomain
.
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:
- https://blog.git-init.com/differences-between-git-merge-and-rebase-and-why-you-should-care/
- https://www.atlassian.com/git/tutorials/merging-vs-rebasing
- https://www.freecodecamp.org/news/git-rebase-handbook/
- https://phoenixnap.com/kb/git-rebase-vs-merge
- https://twitter.com/alexxubyte/status/1617926489579851777
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.
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
Whisper AI là gì? Công cụ chuyển giọng nói thành văn bản của Open AI
Oct 17, 2024 • 8 min read
Cursor AI là gì? Hướng dẫn Sử dụng Cursor AI cơ bản
Sep 16, 2024 • 13 min read
IDE là gì? Những công cụ IDE phổ biến nhất hiện nay
Aug 22, 2024 • 10 min read
Cookies là gì? Cookies được sử dụng như thế nào?
Aug 12, 2024 • 9 min read
SDLC là gì? Các mô hình Software Development Life Cycle phổ biến
Jul 13, 2024 • 27 min read
System Design là gì? Tại sao Thiết kế hệ thống lại quan trọng với Developer?
Jun 17, 2024 • 10 min read