Merge — nó là gì, các loại hợp nhất và cơ chế hoạt động

Tác giả: IT Sectr Đã đăng: 2026-05-10 Thời gian đọc: 10 phút

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 — thao tác hợp nhất nhánh trong Git có hoặc không có commit hợp nhất
  • Fast-forward merge — hợp nhất tuyến tính không cần commit bổ sung khi không có phân kỳ
  • Three-way merge — tạo merge commit khi các nhánh phân kỳ
  • Squash merge — nén tất cả commit của nhánh thành một trước khi hợp nhất
  • Xung đột phát sinh khi cùng một dòng được thay đổi trong cả hai nhánh

Merge là gì?

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.

Khi nào Merge xảy ra

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.

Các loại hợp nhất trong Git

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 merge

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.

bash
# 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

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.

bash
# 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

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ể.

bash
# 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ẻ.

Chiến lược Ours và Theirs

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ế.

Merge hoạt động như thế nào

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ỳ.

  • Bước 1 — Git xác định merge base: commit cuối cùng có trong cả hai nhánh
  • Bước 2 — Git xây dựng hai diff: từ merge base đến nguồn và từ merge base đến đích
  • Bước 3 — Git cố gắng áp dụng cả hai bộ thay đổi vào merge base
  • Bước 4 — Nếu các thay đổi không xung đột — merge hoàn thành tự động
  • Bước 5 — Nếu có xung đột — Git dừng lại và yêu cầu giải quyết

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.

Ví dụ về cách merge hoạt động

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.

Giải quyết xung đột Merge

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.

bash
# 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.

Merge vs Rebase: khi nào chọn gì

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.

  • Merge — bảo tồn ngữ cảnh: có thể thấy khi nào và từ nhánh nào đã hợp nhất. Tốt hơn cho nhánh công khai (develop, main) và làm việc nhóm
  • Rebase — tạo lịch sử tuyến tính sạch sẽ không có merge commit thừa. Tốt hơn cho nhánh tính năng cá nhân trước khi xem xét
  • Quy tắc: không bao giờ rebase các nhánh công khai mà nhà phát triển khác đang sử dụng

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

Sự khác biệt giữa merge và merge --no-ff là gì?

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.

Làm gì nếu xung đột merge quá lớn?

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ó thể hủy merge không?

: 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.

Có cần tạo merge commit cho mỗi tính năng không?

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.

Merge hoạt động thế nào với tệp nhị phân?

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

  • Merge — thao tác Git cơ bản để kết hợp thay đổi từ nhánh này sang nhánh khác
  • Fast-forward — hợp nhất tuyến tính không cần merge commit khi không có phân kỳ
  • Three-way merge — tạo merge commit với hai cha, bảo tồn ngữ cảnh
  • Squash merge — nén tất cả commit của nhánh thành một, mất lịch sử tính năng
  • Xung đột phát sinh khi cùng dòng bị thay đổi và được giải quyết thủ công
  • Merge khác Rebase: cái trước bảo tồn phân nhánh, cái sau làm lịch sử tuyến tính
  • Cho nhánh công khai khuyến nghị merge với --no-ff, cho cá nhân — rebase hoặc squash

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.

Thảo luận dự án

Đọc thêm