Merge là một thao tác trong Git kết hợp các thay đổi từ một nhánh này sang nhánh khác, tạo ra một commit hợp nhất (merge commit). Git hỗ trợ nhiều chiến lược: fast-forward (lịch sử tuyến tính), three-way merge (tạo merge commit) và squash merge (nén tất cả commit thành một). Theo git-scm.com, 2025, merge vẫn là cơ chế tích hợp mã được sử dụng nhiều nhất trong phát triển Git nhóm.
Những điểm chính
Merge (hợp nhất) là một thao tác cơ bản trong Git kết hợp các thay đổi từ một nhánh (nguồn) vào một nhánh khác (đích). Kết quả của việc hợp nhất, nhánh đích nhận được tất cả commit từ nhánh nguồn mà chưa có trong nó. Tùy theo tình huống, Git có thể thực hiện merge theo ba cách khác nhau.
Giá trị chính của merge là bảo tồn lịch sử: merge commit ghi lại sự kiện hợp nhất nhánh, lưu giữ thông tin về thời điểm và nhánh nào đã được hợp nhất. Điều này tạo điều kiện thuận lợi cho việc kiểm tra thay đổi, tìm kiếm hồi quy và hiểu trình tự phát triển. Trong các dự án lớn, merge commit là cách tiêu chuẩn để tích hợp mã.
Theo GitLab Flow, merge commit được sử dụng trong 73% nhóm làm việc với Git. Các phương pháp thay thế (rebase, squash) được ưa chuộng bởi các nhóm tập trung vào lịch sử tuyến tính. Việc chọn chiến lược phụ thuộc vào quy mô nhóm, tần suất phát hành và các quy ước được chấp nhận trong dự án.
Merge cần thiết khi một nhà phát triển hoàn thành công việc trên một tính năng và muốn tích hợp nó vào develop hoặc main. Một kịch bản điển hình: nhà phát triển tạo một nhánh tính năng từ develop, làm việc trên đó vài ngày, và trong thời gian đó, các commit mới từ thành viên khác xuất hiện trong develop. Trước khi hợp nhất, cần kết hợp các thay đổi — và merge được sử dụng cho việc này.
Không có merge, không thể làm việc cộng tác trên một mã nguồn trong Git. Mỗi khi hai nhà phát triển đồng thời thực hiện thay đổi trên cùng một cơ sở mã, nhánh của họ phân kỳ. Merge là cách duy nhất để đưa các thay đổi này trở lại cùng nhau mà không mất dữ liệu.
Git hỗ trợ ba loại merge, mỗi loại được thiết kế cho kịch bản riêng. Việc chọn loại hợp nhất ảnh hưởng đến lịch sử commit, sự thuận tiện khi khôi phục và khả năng đọc log.
Fast-forward xảy ra khi nhánh đích không có commit mới kể từ khi nhánh nguồn được tạo. Trong trường hợp này, Git chỉ đơn giản di chuyển con trỏ nhánh đích về phía trước, đến commit cuối cùng của nhánh nguồn. Lịch sử vẫn tuyến tính, không có merge commit.
# Fast-forward merge: develop không thay đổi kể từ khi tạo feature
git checkout develop
git merge feature/new-login
# Kết quả: con trỏ develop di chuyển đến cuối feature
# Không có merge commit nào được tạo
Fast-forward thuận tiện cho các nhánh ngắn hạn nơi một nhà phát triển làm việc một mình. Nhưng phương pháp này có nhược điểm: mất thông tin về sự tồn tại của nhánh — tất cả commit trông như được thực hiện trực tiếp vào develop.
Three-way merge được thực hiện khi cả hai nhánh đều có commit mới sau điểm phân kỳ. Git tạo một merge commit riêng biệt với hai cha, ghi lại sự kiện hợp nhất nhánh. Phương pháp này được khuyến nghị cho các nhánh tính năng trong phát triển nhóm.
# Three-way merge bắt buộc với cờ --no-ff
git checkout develop
git merge --no-ff feature/new-login
# Một merge commit đã được tạo với thông điệp mặc định
# Bạn có thể đặt thông điệp riêng qua -m
git merge --no-ff feature/new-login -m "Merge feature/new-login into develop"
Cờ --no-ff đảm bảo tạo merge commit, ngay cả khi fast-forward có thể thực hiện được. Đây là phương pháp tốt nhất để bảo tồn thông tin phân nhánh trong dự án.
Squash merge nén tất cả commit của nhánh nguồn thành một và áp dụng nó vào nhánh đích. Lịch sử tính năng bị mất — một commit duy nhất với tất cả thay đổi đi vào nhánh. Điều này thuận tiện khi các commit chi tiết trong nhánh tính năng không mang lại giá trị cho lịch sử tổng thể.
# Squash merge: tất cả commit của feature được nén thành một
git checkout develop
git merge --squash feature/experimental
git commit -m "feat: experimental login flow (squashed)"
Squash phù hợp cho bản nháp, nhánh thử nghiệm và các tình huống quan trọng cần duy trì lịch sử sạch sẽ. Nhược điểm là mất kết nối với các commit gốc, gây khó khăn cho việc khôi phục các thay đổi riêng lẻ.
Ours và Theirs là hai chiến lược merge đặc biệt trong Git. Ours hoàn toàn bỏ qua các thay đổi từ nhánh nguồn, chỉ giữ những gì có trong nhánh đích. Theirs, ngược lại, chấp nhận phiên bản của nhánh nguồn trong bất kỳ xung đột nào. Các chiến lược này hữu ích khi hợp nhất khối lượng mã lớn khi biết trước phiên bản nào sẽ chiếm ưu thế.
Cơ chế merge trong Git dựa trên việc so sánh ba điểm: tổ tiên chung (merge base), trạng thái của nhánh nguồn và trạng thái của nhánh đích. Git tìm merge base — commit cuối cùng chung cho cả hai nhánh — và tính toán những thay đổi đã xảy ra trong mỗi nhánh sau khi phân kỳ.
Git sử dụng thuật toán hợp nhất ba bên không chỉ xem xét hai phiên bản tệp được so sánh mà còn cả tổ tiên chung của chúng. Nhờ đó, Git có thể tự động giải quyết các tình huống mà thay đổi trong một nhánh không ảnh hưởng đến các khu vực đã sửa đổi của nhánh kia — ngay cả khi cả hai tệp đều được sửa đổi.
Hãy xem xét một kịch bản: hai nhà phát triển làm việc trên các tệp khác nhau trong cùng một nhánh tính năng. Người thứ nhất thay đổi LoginActivity.kt, người thứ hai thay đổi ProfileFragment.kt. Khi họ hợp nhất các thay đổi của mình, Git thấy rằng các thay đổi ảnh hưởng đến các tệp khác nhau và thực hiện merge tự động, không cần can thiệp thủ công.
Nếu cả hai nhà phát triển đều thay đổi LoginActivity.kt, nhưng trong các phương thức khác nhau — Git cũng sẽ xử lý tự động, hợp nhất các thay đổi theo từng dòng. Xung đột chỉ xảy ra nếu cả hai thay đổi cùng một dòng hoặc nếu một người xóa mã mà người kia đã thay đổi.
Xung đột merge xảy ra khi Git không thể tự động hợp nhất các thay đổi vì cả hai nhánh đã sửa đổi cùng một dòng theo cách khác nhau. Trong trường hợp này, Git đánh dấu các khu vực xung đột trong tệp và chờ nhà phát triển giải quyết thủ công.
Các khu vực xung đột được đánh dấu bằng các điểm đánh dấu đặc biệt: <<<<<<< HEAD hiển thị mã từ nhánh đích, ======= là dấu phân cách, >>>>>>> source-branch hiển thị mã từ nhánh nguồn. Nhà phát triển phải tự chọn phiên bản nào để giữ hoặc kết hợp chúng.
# 1. Chạy merge và thấy xung đột
git merge feature/new-login
# Đầu ra: CONFLICT (content): Merge conflict in LoginActivity.kt
# 2. Xem danh sách tệp có xung đột
git status
# both modified: src/ui/login/LoginActivity.kt
# 3. Giải quyết xung đột: sửa tệp, xóa điểm đánh dấu
# 4. Thêm tệp đã giải quyết và hoàn thành merge
git add src/ui/login/LoginActivity.kt
git merge --continue
# hoặc: git commit (không có --continue)
Có các công cụ để giải quyết xung đột: git mergetool mở trình hợp nhất trực quan (Meld, Beyond Compare, VS Code). Nhiều nhà phát triển thích giải quyết xung đột trong IDE — IntelliJ IDEA và Android Studio cung cấp công cụ tích hợp với so sánh ba bảng, đơn giản hóa đáng kể quy trình này.
Lời khuyên để giải quyết xung đột: luôn hiểu mỗi bên của xung đột làm gì, không xóa mã của người khác mà không hiểu logic của nó, và nếu xung đột quá phức tạp — hãy mời tác giả của cả hai nhánh cùng giải quyết.
Lựa chọn giữa Merge và Rebase là một trong những quyết định kiến trúc phổ biến nhất trong Git. Cả hai phương pháp đều kết hợp các thay đổi, nhưng thực hiện khác nhau: merge bảo tồn lịch sử phân nhánh, rebase viết lại lịch sử làm cho nó tuyến tính.
Nhiều nhóm sử dụng phương pháp kết hợp: rebase để cập nhật nhánh tính năng với develop (git rebase develop), sau đó merge với cờ --no-ff để ghi lại việc hợp nhất. Điều này mang lại lịch sử sạch sẽ trong tính năng và các điểm hợp nhất thông tin ở cấp develop.
Các câu hỏi thường gặp
Không có --no-ff Git thực hiện fast-forward merge nếu có thể — chỉ đơn giản di chuyển con trỏ nhánh. Có --no-ff Git luôn tạo merge commit, bảo tồn thông tin phân nhánh. Khuyến nghị cho các nhánh tính năng trong phát triển nhóm.
Sử dụng git mergetool hoặc công cụ tích hợp trong IDE. Nếu xung đột liên quan đến hàng chục tệp — các nhánh có thể đã phân kỳ quá xa. Trong trường hợp này, hãy thảo luận kế hoạch hợp nhất với nhóm, có thể chia nó thành nhiều giai đoạn.
Có: git merge --abort hủy merge nếu nó chưa hoàn thành (xung đột). Nếu merge đã hoàn thành — sử dụng git reset --hard HEAD~1 hoặc git revert -m 1 <merge-commit> để khôi phục an toàn.
Khuyến nghị cho làm việc nhóm. Merge commit ghi lại sự kiện hợp nhất, chứa tham chiếu đến cả hai nhánh và đơn giản hóa việc hiểu lịch sử. Đối với nhánh cá nhân hoặc thử nghiệm, squash merge hoặc fast-forward có thể chấp nhận được.
Git không thể tự động hợp nhất các tệp nhị phân — nó chọn hoàn toàn một phiên bản. Đối với tệp nhị phân (hình ảnh, .aab, .apk), khuyến nghị giảm thiểu thay đổi song song và sử dụng Git LFS cho tệp lớn.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm