Câu chuyện lúc 2 giờ sáng
Release deadline sáng hôm sau. Ba team chạy song song trên ba branch khác nhau. Giờ cần gộp tất cả vào main trước khi deploy. Bạn chạy git merge và nhận được một đống conflict dài hơn cả tóc giáo viên cũ.
Không phải lúc nào đọc docs cũng giúp được. Cái bạn cần là biết đúng lúc dùng strategy nào — và quan trọng hơn, cái nào không nên dùng trong tình huống đó.
Trong team 8 người mình từng làm, sau khi chuẩn hóa Git flow với merge strategies phù hợp, số lượng conflict giảm hẳn. Mỗi release từ chỗ tốn cả buổi “hội nghị conflict” xuống còn gộp branch xong trong 15 phút.
So sánh các Git merge strategies
Git không chỉ có một kiểu merge. Khi chạy git merge, Git tự chọn strategy phù hợp — nhưng bạn có thể override bằng flag -s (strategy):
git merge -s <strategy> <branch>
Các strategy chính:
- recursive — mặc định khi merge 2 branch, dùng 3-way merge
- resolve — đơn giản hơn recursive, ít dùng trong thực tế
- octopus — merge nhiều hơn 2 branch cùng lúc
- ours — giữ nguyên code của branch hiện tại, bỏ qua toàn bộ thay đổi từ branch kia
- subtree — dùng cho git subtree workflow
Còn theirs? Nó không phải strategy độc lập — mà là option của recursive: -X theirs. Dễ nhầm lắm, mình sẽ giải thích rõ ở dưới.
recursive (mặc định — baseline để so sánh)
Git dùng strategy này mặc định khi merge 2 branch. Nó tìm common ancestor, chạy 3-way merge, rồi dừng lại khi có conflict để bạn tự xử.
# Thông thường không cần chỉ định, nhưng có thể explicit:
git merge -s recursive feature/login
Recursive là lựa chọn an toàn nhất vì nó không tự ý bỏ qua thay đổi của ai. Nhưng đổi lại, conflict thì bạn phải tự ngồi resolve từng cái một.
octopus — merge nhiều branch một lúc
Tên nghe lạ nhưng dùng rất hay. Octopus cho phép merge 3, 4, thậm chí 10 branch cùng một lúc vào branch hiện tại.
# Merge 3 branch cùng lúc vào main
git checkout main
git merge -s octopus feature/auth feature/dashboard feature/api
Octopus phù hợp nhất khi:
- Các branch không có conflict với nhau — octopus từ chối merge nếu phát hiện conflict
- Bạn muốn lịch sử git gọn: thay vì 3 merge commit riêng lẻ, bạn có 1 commit duy nhất với nhiều parents
- Gộp các feature branch nhỏ, độc lập vào integration branch
Cần nhớ: Octopus không xử lý conflict. Hai branch cùng sửa một file? Nó báo lỗi và từ chối merge ngay — không thương lượng. Khác hẳn recursive ở điểm này.
# Output khi octopus gặp conflict:
# error: Merge requires file-level merging
# Merge with strategy octopus failed.
ours — “của tao, tao giữ”
Strategy này hơi ngược logic: bạn merge một branch vào, nhưng 100% code cuối cùng là code của branch hiện tại. Code từ branch kia bị bỏ hoàn toàn — không warning, không hỏi han.
# Merge feature/old-approach nhưng giữ nguyên code của main
git checkout main
git merge -s ours feature/old-approach
Nghe vô dụng? Thực ra có một use case rất cụ thể: đánh dấu branch đã được “merge” trong lịch sử git mà không thực sự lấy code của nó.
Ví dụ thực tế: Team có branch hotfix/v1.2 đã được áp dụng thủ công lên production. Bạn muốn merge nó vào develop để git thôi phàn nàn về “unmerged branch”, nhưng code trong hotfix đó đã được handle theo cách khác trên develop rồi. ours là đúng chỗ.
# Hotfix đã được cherry-pick thủ công vào develop
# Merge để close branch, không lấy code
git checkout develop
git merge -s ours hotfix/payment-bug
# Kết quả: merge commit tồn tại, nhưng code develop không thay đổi một dòng nào
-X theirs — “của mày thì thắng”
Không phải strategy, mà là option của recursive. Khi có conflict, thay vì dừng lại để bạn tự resolve, Git tự động lấy code từ branch đang merge vào.
# Merge feature/new-config, ưu tiên code từ feature branch khi conflict
git checkout main
git merge -X theirs feature/new-config
# Ngược lại: -X ours ưu tiên code của branch hiện tại (khác với strategy ours!)
git merge -X ours feature/new-config
Điểm hay của -X theirs: nó vẫn chạy 3-way merge bình thường, chỉ tự động resolve conflict bằng cách chọn “theirs”. Các file không conflict vẫn được merge như thường.
Phân biệt -s ours và -X ours — hay nhầm:
-s ours: bỏ qua hoàn toàn mọi thay đổi từ branch kia, kể cả file không conflict-X ours: merge bình thường, chỉ tự động resolve conflict bằng cách ưu tiên code của mình
Phân tích ưu nhược điểm
Octopus
Ưu: Lịch sử git sạch hơn (ít merge commit), tiện khi gộp nhiều feature branch nhỏ cùng lúc.
Nhược: Không chịu conflict. Có bất kỳ conflict nào, bạn phải resolve trước rồi mới dùng octopus được.
ours (strategy)
Ưu: Hữu ích để “đóng” branch mà không lấy code, giữ lịch sử git gọn gàng.
Nhược: Dễ bắn vào chân — nếu nhầm, bạn mất toàn bộ thay đổi từ branch kia mà git không hề cảnh báo.
-X theirs / -X ours (option)
Ưu: Merge tự động không cần can thiệp thủ công, tiện cho CI/CD pipeline.
Nhược: Bạn đang mù quáng tin Git biết cái nào đúng. Không phải lúc nào cũng vậy.
Chọn strategy nào cho tình huống nào
| Tình huống | Strategy phù hợp |
|---|---|
| Merge 2 branch bình thường | recursive (mặc định) |
| Gộp 3+ branch feature nhỏ, không conflict | octopus |
| Đóng branch cũ, không lấy code | -s ours |
| Merge tự động, ưu tiên branch mới hơn | -X theirs |
| Merge tự động, ưu tiên code hiện tại | -X ours |
Hướng dẫn triển khai thực tế
Kịch bản 1: Gộp nhiều feature branch vào staging
# Đảm bảo các branch không conflict với nhau trước
git checkout staging
git merge -s octopus feature/user-auth feature/search feature/export
# Nếu octopus fail vì conflict, fallback sang merge từng branch:
git merge feature/user-auth
# resolve conflicts...
git merge feature/search
git merge feature/export
Kịch bản 2: Đóng hotfix branch sau khi cherry-pick
# Hotfix đã được cherry-pick vào develop rồi
# Merge để close branch trong lịch sử git
git checkout develop
git merge -s ours hotfix/critical-fix
git branch -d hotfix/critical-fix
# Kiểm tra code develop không thay đổi:
git diff HEAD~1 HEAD # Chỉ thấy merge commit, không có thay đổi code
Kịch bản 3: Auto-merge trong CI/CD pipeline
#!/bin/bash
# Merge release branch vào main, ưu tiên code từ release khi conflict
set -e
git checkout main
git fetch origin
git merge -X theirs origin/release/v2.1.0
# Log lại những file đã auto-resolve để audit sau:
git log --merges -1 --format="%H" | xargs git diff-tree --no-commit-id -r --name-only
Kịch bản 4: Verify trước khi dùng -X theirs
Trước khi auto-resolve, mình thường chạy một bước kiểm tra nhanh:
# Xem trước những file nào sẽ conflict
git merge --no-commit --no-ff feature/new-approach 2>&1 | grep CONFLICT
# Nếu ok, abort rồi merge thật với -X theirs
git merge --abort
git merge -X theirs feature/new-approach
Một vài điều cần nhớ
Đừng dùng -X theirs hay -s ours theo phản xạ mà không hiểu mình đang làm gì. Mình từng thấy một teammate dùng -s ours khi merge feature vào main — feature không vào được main, nhưng git vẫn báo “merged successfully”. Mất 30 phút mới tìm ra.
Octopus chỉ phát huy tác dụng khi feature branch nhỏ, độc lập và được review kỹ trước khi merge. Team hay có conflict giữa các branch thì octopus không giúp được gì.
Và cuối cùng: strategy tốt nhất vẫn là không cần dùng strategy đặc biệt. Branch nhỏ, merge thường xuyên — conflict ít hơn, recursive mặc định là đủ dùng. Mọi trick ở trên chỉ là giải pháp tình huống, không phải thói quen.

