Git merge vs rebase: khác biệt, ví dụ và khi nào dùng cái nào
Git merge vs rebase quyết định lịch sử dự án của bạn trông ra sao. Xem cả hai trên cùng một ví dụ với đầu ra log thực tế, học quy tắc duy nhất giúp tránh thảm họa và sao chép một quy trình làm việc nhóm an toàn.
- Merge nối hai nhánh bằng một commit merge mới và giữ lịch sử đúng như đã diễn ra.
- Rebase phát lại các commit của bạn lên trên nhánh khác, cho một đường thẳng nhưng hash commit mới.
- Đừng bao giờ rebase các commit mà người khác đã kéo về.
- Rebase nhánh tính năng của riêng bạn trước khi mở pull request, và dùng merge khi đưa nó vào nhánh chính.
Nhánh tính năng của bạn tụt lại so với main và bạn cần phần việc mới. Git merge vs rebase là lựa chọn giữa hai cách để có nó, và cả hai đều đúng. Chúng khác nhau ở chỗ lịch sử trông thế nào sau đó.
Chúng tôi dựng một kho nhỏ, chạy cả hai lệnh và dán đầu ra thực tế bên dưới, để bạn so sánh kết quả thay vì tưởng tượng. Mới dùng Git? Hãy bắt đầu với hướng dẫn Git cho người mới (tiếng Anh).
git merge và git rebase làm gì
git merge lấy đầu của hai nhánh và kết hợp chúng. Nếu cả hai có commit mới, Git tạo một commit merge có hai cha. Không có gì đã tồn tại bị thay đổi.
git rebase lấy các commit chỉ có ở nhánh của bạn, đặt sang một bên, chuyển nhánh của bạn đến đầu nhánh kia rồi phát lại từng commit. Các commit được phát lại là commit mới với hash mới, dù thay đổi bên trong giống nhau.
Git merge trong thực tế
Điểm xuất phát của chúng tôi: commit A trên main, hai commit (F1, F2) trên feature, và thêm một commit (M1) được thêm vào main sau đó. Bây giờ chúng tôi merge main vào feature.
git switch feature
git merge main
git log --oneline --graph
* f507aab Merge main into feature
|\
| * 8842fb5 M1
* | 082cc6a F2
* | 5a71e3c F1
|/
* 8766865 A
Biểu đồ cho thấy câu chuyện thật: hai dòng công việc được nối lại. Các commit gốc F1 và F2 giữ nguyên hash. Cái giá là thêm một commit merge, và trong dự án sôi động, biểu đồ trở thành một mạng nhện.
Git rebase trong thực tế
Cùng điểm xuất phát, nhưng lần này chúng tôi rebase feature lên main.
git switch feature
git rebase main
git log --oneline --graph
* 1ff474b F2
* bf9149e F1
* 8842fb5 M1
* 8766865 A
Lịch sử là một đường thẳng, như thể bạn bắt đầu làm sau M1. Hãy nhìn các hash: F1 trước là 5a71e3c nay là bf9149e, còn F2 đổi từ 082cc6a sang 1ff474b. Git đã viết lại chúng, và đó là lý do rebase cần cẩn thận.
Merge và rebase song song
| Khía cạnh | Merge | Rebase |
|---|---|---|
| Hình dạng lịch sử | Biểu đồ phân nhánh có commit merge | Một đường thẳng |
| Commit hiện có | Không thay đổi | Được viết lại với hash mới |
| Xung đột | Giải quyết một lần, trong commit merge | Có thể phải sửa ở từng commit được phát lại |
| An toàn trên nhánh dùng chung | Có | Không |
| Phù hợp nhất cho | Đưa công việc đã xong vào main | Cập nhật nhánh của riêng bạn trước khi review |
Mẹo: nếu không quyết định được, hãy hỏi còn ai khác có nhánh của bạn. Nếu không ai, rebase là an toàn. Nếu đồng nghiệp đã kéo về, hãy dùng merge.
Quy tắc vàng của rebase
Đừng rebase các commit tồn tại bên ngoài kho của riêng bạn. Sau khi rebase, nhánh của bạn không còn khớp với bản sao trên máy chủ, nên việc push cần ép buộc. Nếu đồng nghiệp đã xây công việc trên các commit cũ, lịch sử của họ giờ mâu thuẫn với của bạn, và gỡ rối rất đau đầu.
Rebase nhánh tính năng riêng tư của bạn thì không sao. Rebase main hoặc bất kỳ nhánh nào người khác dùng thì không được.
Quy trình làm việc nhóm an toàn
Quy trình này giữ lịch sử sạch mà không làm ai bất ngờ. Đây cũng là cách đa số nhóm dùng trước pipeline CI của họ, như pipeline trong hướng dẫn GitHub Actions CI/CD (tiếng Anh) của chúng tôi.
git switch feature
git fetch origin
git rebase origin/main # or: git pull --rebase
# fix conflicts, then: git add <file> && git rebase --continue
git push --force-with-lease # only on your own branch
--force-with-lease từ chối ghi đè nhánh trên máy chủ nếu ai đó đã push kể từ lần fetch cuối của bạn, điều mà --force thường sẽ âm thầm phá hủy. Khi pull request được duyệt, hãy merge nó vào main.
Cách hoàn tác một rebase hỏng
Rebase trông có vẻ phá hoại, nhưng các commit cũ vẫn nằm trong reflog của Git một thời gian, nên bạn có thể quay lại.
git reflog # find the commit before the rebase
git reset --hard HEAD@{5} # example position, check reflog first
Hãy tạo nhánh sao lưu bằng git branch backup trước bất kỳ rebase rủi ro nào, và việc khôi phục chỉ còn một lệnh. Đang chạy cơ sở dữ liệu hoặc công cụ trong container? Xem hướng dẫn Docker cho người mới (tiếng Anh) của chúng tôi.
Lời kết
Merge giữ sự thật, rebase giữ nó gọn gàng. Hãy dùng rebase để làm mới nhánh của riêng bạn, dùng merge để bàn giao, và đừng bao giờ viết lại lịch sử mà người khác phụ thuộc.
Câu hỏi thường gặp
Khác biệt giữa git merge và git rebase là gì?
Merge kết hợp hai nhánh bằng một commit merge và giữ mọi commit hiện có. Rebase chuyển các commit của bạn lên trên nhánh khác và viết lại chúng, tạo ra lịch sử tuyến tính.
Tôi nên dùng merge hay rebase?
Rebase nhánh tính năng của bạn để luôn cập nhật, và merge khi bàn giao vào nhánh dùng chung như main. Hãy dùng merge bất cứ khi nào người khác cùng dùng nhánh.
git rebase có nguy hiểm không?
An toàn trên nhánh riêng tư và rủi ro trên nhánh dùng chung, vì nó viết lại hash commit. Hãy dùng nhánh sao lưu và reflog để khôi phục nếu có sự cố.
force-with-lease làm gì?
Nó chỉ push ép khi nhánh từ xa vẫn ở nơi bạn thấy lần cuối, nên bạn không vô tình ghi đè các commit mới của đồng nghiệp.
Rebase có xóa commit của tôi không?
Không. Nó tạo commit mới với cùng thay đổi, và các commit cũ vẫn nằm trong reflog cho đến khi Git dọn dẹp.
Tiếp tục học (bài viết tiếng Anh)
Cần hỗ trợ về kiểm thử, WordPress hoặc SEO?
Chúng tôi giúp các nhóm phát hành phần mềm tốt hơn và phát triển website. Hãy cho chúng tôi biết bạn đang làm gì, chúng tôi sẽ gợi ý bước tiếp theo rõ ràng.
Thêm hướng dẫn về thử nghiệm, AI và phát triển hiện đại
Bạn thích bài viết này? Chọn một chủ đề và chuyển ngay sang phần hướng dẫn mới nhất của chúng tôi.
Giải thích về ảo giác AI: Tại sao các mô hình ngôn ngữ lại tạo nên mọi thứ và cách giảm thiểu nó
Ảo giác AI đã giải thích: chúng là gì, tại sao mô hình ngôn ngữ tạo ra chúng, các loại phổ biến, cách giảm thiểu chúng bằng cách căn cứ và nhắc nhở …
Đọc bài viết →DevOpsGiải thích về các biến môi trường: Tệp .env, bí mật và cấu hình được thực hiện đúng
Các biến môi trường được giải thích bằng các ví dụ Python mà chúng tôi đã chạy: đọc và thiết lập chúng, tại sao mọi giá trị đều là một chuỗi, tệp .e…
Đọc bài viết →Phát triển phần mềmGiải thích về giới hạn tỷ lệ API: Thuật toán, tiêu đề và xử lý lỗi 429
Giới hạn tốc độ API được giải thích bằng mô phỏng mà chúng tôi đã chạy: nhóm mã thông báo, cửa sổ cố định, cửa sổ trượt, 429 phản hồi, tiêu đề giới …
Đọc bài viết →Tự động hóa kiểm thửChế giễu bằng Python: unittest.mock, patch và những sai lầm khiến bài kiểm tra nói dối
Mô phỏng bằng Python với unittest.mock, đã được thử nghiệm: Mock và return_value, side_effect, vá lỗi nơi sử dụng tên, thông số kỹ thuật và thông số…
Đọc bài viết →Kiểm thử phần mềmGiải thích về phạm vi bảo hiểm của mã: Phạm vi tuyến so với chi nhánh và ý nghĩa thực sự 100%
Phạm vi bảo hiểm mã được giải thích bằng cách chạy phạm vi bảo hiểm thực tế: phạm vi bảo hiểm dòng, chi nhánh và điều kiện, đọc báo cáo, lý do tại s…
Đọc bài viết →Cơ sở dữ liệu & SQLDELETE vs TRUNCATE vs DROP trong SQL: Sự khác biệt, khôi phục và thời điểm sử dụng mỗi loại
DELETE vs TRUNCATE vs DROP được giải thích bằng SQL đã được thử nghiệm: mỗi loại loại bỏ, khôi phục, kích hoạt, đặt lại danh tính, tốc độ và Postgre…
Đọc bài viết →Cơ sở dữ liệu & SQLGiải thích về Chế độ xem SQL: Tạo, sử dụng và tránh những cạm bẫy, kèm theo ví dụ
Chế độ xem SQL được giải thích bằng các truy vấn chúng tôi đã chạy: tạo chế độ xem, kết quả trực tiếp, ẩn cột, hiệu suất, chế độ xem có thể cập nhật…
Đọc bài viết →Phát triển phần mềmGiao diện TypeScript so với loại: Sự khác biệt thực sự, được thử nghiệm bằng trình biên dịch
Giao diện TypeScript so với loại, được kiểm tra bằng trình biên dịch thực: cả hai có thể làm gì, chỉ một người có thể làm gì, hợp nhất khai báo, mở …
Đọc bài viết →Phát triển phần mềmGiải thích về Python *args và **kwargs: Hàm linh hoạt với ví dụ rõ ràng
Python *args và **kwargs được giải thích bằng mã chúng tôi đã chạy: đóng gói và giải nén, thứ tự tham số, đối số chỉ từ khóa và chỉ vị trí, trình tr…
Đọc bài viết →Phát triển phần mềmHàm Lambda của Python: Cú pháp, cách sử dụng thực tế và khi nào không nên sử dụng chúng
Các hàm lambda của Python được giải thích bằng mã chúng tôi đã chạy: cú pháp, sắp xếp bằng khóa, bản đồ và bộ lọc, các phần đóng, bẫy liên kết muộn …
Đọc bài viết →Trí tuệ nhân tạoĐại lý AI là gì? Cách chúng hoạt động, với vòng lặp tác nhân đơn giản, bạn có thể chạy
Tác nhân AI là gì? Hướng dẫn đơn giản về vòng lặp tác nhân, công cụ, bộ nhớ và lan can, với một vòng lặp Python nhỏ mà chúng tôi đã chạy, những rủi …
Đọc bài viết →DevOpsGiám sát và Khả năng quan sát: Sự khác biệt thực sự, với nhật ký, số liệu và dấu vết
Giải thích về giám sát và khả năng quan sát: lỗi đã biết và chưa biết, nhật ký, số liệu và dấu vết, hướng dẫn về sự cố thanh toán, tín hiệu vàng, Op…
Đọc bài viết →DevOpsDocker Volumes vs Bind Mounts: Duy trì dữ liệu vùng chứa đúng cách
Giải thích về khối lượng Docker và liên kết gắn kết: tại sao dữ liệu vùng chứa biến mất, khối lượng được đặt tên, liên kết gắn kết, tmpfs, ví dụ về …
Đọc bài viết →Tự động hóa kiểm thửTDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each
TDD và BDD được giải thích bằng một tính năng giảm giá được xây dựng theo cả hai cách: tái cấu trúc màu đỏ-xanh với mã chúng tôi đã chạy, kịch bản C…
Đọc bài viết →Phát triển phần mềmGit merge vs rebase: khác biệt, ví dụ và khi nào dùng cái nào
Git merge vs rebase được giải thích bằng đầu ra log thực tế, bảng so sánh, quy tắc vàng của rebase và quy trình làm việc nhóm an toàn.
Đọc bài viết →Phát triển phần mềmList comprehension trong Python: cú pháp, ví dụ và khi nào nên tránh
List comprehension trong Python: cú pháp, lọc dữ liệu, if-else, vòng lặp lồng nhau, thời gian đo thực tế và khi nào vòng lặp for thông thường tốt hơn.
Đọc bài viết →Cơ sở dữ liệu & SQLGiải thích SQL JOIN kèm ví dụ (Inner, Left, Right, Full)
Hiểu SQL JOIN từng bước với bảng và truy vấn mẫu: INNER, LEFT, RIGHT, FULL, CROSS và SELF, cùng lỗi thường gặp và mẹo hiệu năng.
Đọc bài viết →