Mục Lục
1. Chức năng của lệnh Git Merge
Lệnh git merge
trong Git được sử dụng để hợp nhất các thay đổi từ một nhánh khác vào nhánh hiện tại. Khi bạn thực hiện một git merge
, Git sẽ kết hợp các thay đổi từ nhánh được chỉ định vào nhánh hiện tại, tạo ra một commit hợp nhất nếu cần thiết.
1.1. Các trường hợp sử dụng phổ biến:
- Hợp nhất nhánh phát triển vào nhánh chính:
- Khi bạn phát triển một tính năng mới trên một nhánh phụ, sau khi hoàn thành, bạn sẽ sử dụng
git merge
để hợp nhất nhánh này vào nhánh chính (thường làmain
hoặcmaster
).
- Khi bạn phát triển một tính năng mới trên một nhánh phụ, sau khi hoàn thành, bạn sẽ sử dụng
- Hợp nhất nhánh mà không có xung đột:
- Nếu các thay đổi trên cả hai nhánh không xung đột với nhau, Git sẽ tự động hợp nhất chúng và tạo ra một commit hợp nhất (merge commit).
- Hợp nhất nhánh có xung đột:
- Nếu có xung đột giữa các thay đổi trên hai nhánh, Git sẽ dừng quá trình hợp nhất và yêu cầu bạn giải quyết xung đột thủ công. Sau khi xung đột được giải quyết, bạn sẽ hoàn tất quá trình bằng cách tạo một commit hợp nhất.
1.2. Các lệnh và tuỳ chọn phổ biến:
git merge <branch-name>
: Hợp nhất nhánh<branch-name>
vào nhánh hiện tại.git merge --no-ff <branch-name>
: Buộc tạo một merge commit ngay cả khi hợp nhất có thể được thực hiện bằng cách fast-forward.git merge --abort
: Nếu bạn muốn hủy bỏ một merge đang thực hiện (khi gặp xung đột chẳng hạn), lệnh này sẽ quay lại trạng thái trước khi merge bắt đầu.
Lệnh git merge
giúp duy trì lịch sử phát triển của dự án rõ ràng và cho phép tích hợp các thay đổi từ các nhánh khác nhau một cách có tổ chức.
2. Lịch sử lệnh Git Merge
Lịch sử của lệnh git merge
trong Git phản ánh quá trình phát triển của Git từ khi nó ra đời vào năm 2005 bởi Linus Torvalds. git merge
là một trong những lệnh quan trọng nhất trong Git, được sử dụng để kết hợp các thay đổi từ các nhánh khác nhau trong một dự án.
2.1. Các cột mốc chính trong lịch sử của git merge
:
- Git 1.0 (2005):
- Lệnh
git merge
được giới thiệu từ phiên bản đầu tiên của Git. Ban đầu, nó được thiết kế để hợp nhất các nhánh trong một dự án mà không làm mất lịch sử các thay đổi.
- Lệnh
- Merge Commit:
- Từ những ngày đầu, lệnh
git merge
đã tạo ra một “merge commit” để ghi lại việc hợp nhất các thay đổi từ hai hoặc nhiều nhánh. Merge commit giữ lại lịch sử của cả hai nhánh, giúp theo dõi nguồn gốc của các thay đổi.
- Từ những ngày đầu, lệnh
- Fast-Forward Merge:
- Khi hợp nhất một nhánh vào nhánh khác mà không có thay đổi nào sau điểm chung cuối cùng, Git sẽ thực hiện một “fast-forward” merge. Điều này giúp tránh tạo ra một merge commit không cần thiết.
- –no-ff (No Fast-Forward) Option:
- Phiên bản Git 1.6.5 (2009) giới thiệu tùy chọn
--no-ff
, cho phép người dùng buộc tạo một merge commit ngay cả khi có thể fast-forward. Điều này giúp duy trì lịch sử rõ ràng hơn, đặc biệt khi làm việc với các nhánh tính năng (feature branches).
- Phiên bản Git 1.6.5 (2009) giới thiệu tùy chọn
- Conflict Handling:
- Git từ đầu đã có khả năng phát hiện xung đột trong quá trình hợp nhất. Khi có xung đột, Git dừng quá trình merge và yêu cầu người dùng giải quyết chúng thủ công. Khả năng này đã được cải tiến qua các phiên bản, bao gồm các công cụ như
git mergetool
để hỗ trợ việc giải quyết xung đột.
- Git từ đầu đã có khả năng phát hiện xung đột trong quá trình hợp nhất. Khi có xung đột, Git dừng quá trình merge và yêu cầu người dùng giải quyết chúng thủ công. Khả năng này đã được cải tiến qua các phiên bản, bao gồm các công cụ như
- Git 2.x (2014 trở đi):
- Nhiều cải tiến đã được thêm vào lệnh
git merge
, bao gồm khả năng hỗ trợ tốt hơn cho các merge phức tạp, cải thiện hiệu suất, và tích hợp tốt hơn với các công cụ CI/CD hiện đại.
- Nhiều cải tiến đã được thêm vào lệnh
git merge --abort
:- Giới thiệu trong các phiên bản sau này, lệnh
git merge --abort
giúp người dùng dễ dàng hủy bỏ một merge đang thực hiện nếu gặp vấn đề.
- Giới thiệu trong các phiên bản sau này, lệnh
2.2. Tầm quan trọng của lệnh git merge
:
git merge
đóng vai trò quan trọng trong việc quản lý và duy trì lịch sử của dự án. Nó giúp các nhóm phát triển có thể làm việc song song trên các nhánh khác nhau, sau đó hợp nhất các thay đổi lại với nhau một cách an toàn và có tổ chức. Qua thời gian, Git và lệnh git merge
đã trở thành một phần không thể thiếu trong quy trình làm việc của hầu hết các nhà phát triển phần mềm.
3. Chiến lược hợp nhất Ours/Theirs trong Git Merge cho sự tồn tại của file
Bài toán: Nếu nhánh source-branch có file a1.txt, a2.txt, a3.txt, s.txt. Nhánh target-branch có a1.txt, a2.txt, a3.txt, t.txt. Cách dùng git merge để merge source-branch vào target-branch thoả mãn kết quả có nhánh mới có các file: a1.txt, a2.txt, a3.txt, s.txt và không có t.txt?
Để thực hiện quá trình git merge
mà đảm bảo kết quả chỉ có các file từ source-branch
(a1.txt, a2.txt, a3.txt, s.txt) và loại bỏ file t.txt
có trong target-branch
, bạn có thể làm theo các bước sau:
3.1. Tạo một nhánh tạm thời từ target-branch
Bắt đầu bằng việc tạo một nhánh tạm thời từ target-branch
để đảm bảo bạn có thể quay lại nếu cần thiết:
git checkout target-branch
git checkout -b temp-branch
3.2. Reset nhánh temp-branch
về nhánh source-branch
Sử dụng lệnh git reset --hard
để làm cho nhánh temp-branch
khớp hoàn toàn với source-branch
, giữ nguyên các thay đổi của source-branch
:
git reset --hard source-branch
3.3. Thực hiện merge theo chiến lược “ours”
Bây giờ, merge nhánh target-branch
vào nhánh temp-branch
nhưng chỉ giữ lại các thay đổi từ temp-branch
:
git merge -s ours target-branch
3.4. Commit và kết quả
Sau khi hoàn tất, kết quả của nhánh temp-branch
sẽ bao gồm tất cả các file từ source-branch
và loại bỏ file t.txt
từ target-branch
.
Bạn cần commit các thay đổi này:
git commit -m "Merged source-branch into temp-branch, keeping only files from source-branch"
3.5. Kiểm tra kết quả và đổi tên nhánh (nếu cần)
Lúc này, bạn có thể kiểm tra các file trong nhánh temp-branch
. Nó sẽ chỉ có các file từ source-branch
và không có t.txt
.
Nếu bạn muốn, bạn có thể đổi tên nhánh temp-branch
thành target-branch
để tiếp tục làm việc:
git branch -M target-branch
3.6. Đẩy thay đổi lên remote (nếu cần)
Cuối cùng, nếu bạn đang làm việc với một kho lưu trữ từ xa (remote repository), hãy đẩy thay đổi lên:
git push -f origin target-branch
3.7. Tóm tắt quá trình:
- Mục tiêu: Bạn muốn hợp nhất nhánh
source-branch
vàotarget-branch
sao cho kết quả chỉ giữ lại các file từsource-branch
và loại bỏ các file không có trongsource-branch
. - Kết quả: Sau khi thực hiện các bước trên, nhánh cuối cùng sẽ chỉ có các file
a1.txt
,a2.txt
,a3.txt
, vàs.txt
.
Cách tiếp cận này đảm bảo rằng chỉ các file từ source-branch
được giữ lại và loại bỏ t.txt
từ target-branch
.
4. Chiến lược hợp nhất Ours/Theirs trong Git Merge cho sự khác biệt nội dung file
rong Git, chiến lược hợp nhất “Ours” và “Theirs” được sử dụng để giải quyết xung đột trong quá trình hợp nhất (merge
). Cả hai chiến lược này cho phép bạn chọn giữ lại các thay đổi từ một nhánh cụ thể khi có xung đột giữa các nhánh.
4.1. Chiến lược “Ours”
- Mô tả: Khi sử dụng chiến lược “Ours”, Git sẽ giữ lại tất cả các thay đổi từ nhánh hiện tại của bạn (nhánh mà bạn đang làm việc) và bỏ qua các thay đổi từ nhánh được hợp nhất vào.
- Cách sử dụng: Chiến lược này thường được sử dụng khi bạn muốn bỏ qua toàn bộ thay đổi từ nhánh kia trong quá trình hợp nhất.
- Ví dụ:
git merge -s ours feature-branch
Trong ví dụ này, nếu có bất kỳ xung đột nào, Git sẽ giữ lại các thay đổi từ nhánh hiện tại và bỏ qua các thay đổi từ feature-branch
.
4.2. Chiến lược “Theirs”
- Mô tả: Khi sử dụng chiến lược “Theirs”, Git sẽ giữ lại tất cả các thay đổi từ nhánh được hợp nhất vào và bỏ qua các thay đổi từ nhánh hiện tại.
- Cách sử dụng: Chiến lược này hữu ích khi bạn muốn bỏ qua toàn bộ thay đổi trong nhánh hiện tại và chấp nhận toàn bộ thay đổi từ nhánh được hợp nhất.
- Ví dụ:
git merge -s theirs feature-branch
- Chiến lược “Theirs” không được hỗ trợ trực tiếp bằng một tùy chọn của lệnh
git merge
, nhưng có thể thực hiện bằng cách giải quyết thủ công các xung đột và chọn tất cả các thay đổi từ nhánh kia.
4.3. Lưu ý:
- Các chiến lược này chủ yếu được sử dụng khi có xung đột (conflict) giữa các thay đổi từ hai nhánh.
- Khi hợp nhất với
-s ours
hoặc thủ công với “theirs”, bạn đang chọn không kết hợp các thay đổi từ cả hai nhánh mà thay vào đó chỉ giữ lại thay đổi từ một nhánh cụ thể.
4.4. Tiếng Việt:
- Ours: Chiến lược “của chúng ta” – giữ lại tất cả các thay đổi từ nhánh hiện tại.
- Theirs: Chiến lược “của họ” – giữ lại tất cả các thay đổi từ nhánh được hợp nhất vào.
4.5. Khi nào nên sử dụng?
- Ours: Khi bạn chắc chắn rằng các thay đổi từ nhánh hiện tại là chính xác và bạn không muốn các thay đổi từ nhánh kia can thiệp vào.
- Theirs: Khi bạn muốn chấp nhận hoàn toàn các thay đổi từ nhánh khác và bỏ qua những thay đổi cũ trên nhánh hiện tại.
5. Chiến lược hợp nhất cần Review khác biệt trong Git Merge
Khi thực hiện git merge
và có xung đột trong các file, Git yêu cầu bạn phải xem xét và giải quyết các xung đột đó trước khi hoàn tất việc hợp nhất. Quá trình này được gọi là “three-way merge” hoặc “manual conflict resolution.” Dưới đây là cách chiến lược này hoạt động:
5.1. Three-Way Merge (Hợp nhất ba chiều)
- Mô tả: Đây là chiến lược mặc định khi Git gặp xung đột trong quá trình hợp nhất. Git sẽ cố gắng tự động hợp nhất các thay đổi từ hai nhánh (nhánh gốc và nhánh được hợp nhất) và sẽ thông báo cho bạn nếu không thể thực hiện việc này mà không gây xung đột.
- Cách hoạt động: Git sẽ sử dụng “common ancestor” (tổ tiên chung) của hai nhánh làm cơ sở để xác định các thay đổi từ mỗi nhánh. Khi có sự khác biệt, Git sẽ yêu cầu bạn xem xét và quyết định cách giải quyết.
- Khi có xung đột:
- Git sẽ đánh dấu các vùng xung đột trong các file có vấn đề, cho phép bạn xem xét từng thay đổi và chọn cách xử lý.
- Các vùng xung đột thường được đánh dấu bằng các dấu
<<<<<<
,======
,>>>>>>
, cho biết sự khác biệt giữa các nhánh.
- Ví dụ về xung đột:
<<<<<<< HEAD
Đây là nội dung trong nhánh hiện tại (HEAD).
=======
Đây là nội dung trong nhánh được hợp nhất.
>>>>>>> feature-branch
Trong đoạn ví dụ trên:
- HEAD: Nội dung từ nhánh hiện tại.
- feature-branch: Nội dung từ nhánh được hợp nhất vào.
5.2. Quy trình giải quyết xung đột thủ công
Khi Git thông báo có xung đột, bạn cần thực hiện các bước sau:
Bước 1: Kiểm tra các file có xung đột
git status
Git sẽ liệt kê các file có xung đột mà bạn cần xem xét.
Bước 2: Mở các file có xung đột và xem xét
Mở từng file và tìm các vùng xung đột (được đánh dấu bằng các dấu <<<<<<
, ======
, >>>>>>
). Sau đó, bạn cần quyết định giữ lại phần nào hoặc kết hợp nội dung từ cả hai phần.
Bước 3: Xóa các dấu hiệu xung đột và lưu file
Sau khi chỉnh sửa, bạn cần xóa các dấu hiệu xung đột và lưu file.
Bước 4: Đánh dấu file đã được giải quyết
Sau khi giải quyết xong xung đột, bạn cần thông báo cho Git biết rằng xung đột đã được giải quyết:
git add <file>
Bước 5: Hoàn tất merge
Khi tất cả các xung đột đã được giải quyết và các file được đánh dấu, bạn hoàn tất việc merge bằng lệnh commit:
git commit
5.3. Chiến lược khác: Git mergetool
- Mô tả: Thay vì giải quyết xung đột thủ công, bạn có thể sử dụng một công cụ hợp nhất (merge tool) tích hợp với Git như
kdiff3
,meld
,vimdiff
… Công cụ này giúp bạn xem và chọn các thay đổi dễ dàng hơn. - Cách sử dụng:
git mergetool
Git sẽ mở công cụ hợp nhất và bạn có thể giải quyết xung đột thông qua giao diện đồ họa hoặc giao diện dòng lệnh.
5.4. Tóm tắt
- Three-Way Merge: Là chiến lược hợp nhất mặc định khi Git gặp xung đột, yêu cầu bạn phải xem xét và giải quyết các thay đổi.
- Mergetool: Sử dụng công cụ hợp nhất để hỗ trợ quá trình giải quyết xung đột dễ dàng hơn.
- Giải quyết thủ công: Mở file, xem xét xung đột, chọn các thay đổi phù hợp, sau đó lưu và hoàn tất hợp nhất.
Cả hai phương pháp này đều giúp bạn xem xét cẩn thận các thay đổi trong file trước khi chấp nhận chúng trong quá trình hợp nhất.
6. Các thuật ngữ trong Git Merge
Ngoài “Three-Way Merge,” còn có một số thuật ngữ khác trong Git liên quan đến quá trình hợp nhất và giải quyết xung đột mà bạn có thể gặp:
6.1. Fast-Forward Merge
- Mô tả: Fast-Forward Merge xảy ra khi nhánh được hợp nhất là một nhánh con trực tiếp của nhánh hiện tại, nghĩa là không có bất kỳ thay đổi nào trên nhánh hiện tại kể từ khi nhánh được hợp nhất được tách ra. Trong trường hợp này, Git chỉ cần di chuyển con trỏ của nhánh hiện tại lên phía trước đến commit mới nhất trên nhánh kia mà không cần tạo ra một commit hợp nhất mới.
- Ví dụ:
git merge feature-branch
Nếu không có thay đổi nào khác trên nhánh hiện tại, Git sẽ thực hiện Fast-Forward Merge.
6.2. Recursive Merge
- Mô tả: Đây là chiến lược hợp nhất mặc định khi Git cần hợp nhất hai nhánh có thay đổi độc lập. Git sẽ tìm “common ancestor” (tổ tiên chung) và thực hiện một “Three-Way Merge.” Nếu có nhiều tổ tiên chung, Git sẽ thực hiện hợp nhất các tổ tiên trước khi tiếp tục hợp nhất với các nhánh chính. Phương pháp này giúp tạo ra một hợp nhất chính xác hơn khi có nhiều nhánh phân nhánh từ một commit chung.
- Ví dụ: Được sử dụng tự động bởi Git khi không có điều kiện để thực hiện Fast-Forward Merge.
6.3. Octopus Merge
- Mô tả: Octopus Merge được sử dụng khi bạn muốn hợp nhất nhiều hơn hai nhánh cùng một lúc. Chiến lược này thường được sử dụng trong các tình huống không có xung đột lớn, vì nếu có quá nhiều xung đột, quá trình hợp nhất có thể phức tạp và khó kiểm soát.
- Ví dụ:
git merge branch1 branch2 branch3
Trong ví dụ này, Git sẽ hợp nhất ba nhánh (branch1
, branch2
, branch3
) vào nhánh hiện tại.
6.4. Ours Merge Strategy
- Mô tả: Chiến lược này cho phép bạn thực hiện một merge và giữ lại tất cả các thay đổi từ nhánh hiện tại, bỏ qua các thay đổi từ nhánh được hợp nhất vào, ngay cả khi có xung đột. Chiến lược này hữu ích khi bạn muốn bỏ qua hoàn toàn các thay đổi từ một nhánh nhất định.
- Ví dụ:
git merge -s ours branch-to-ignore
6.5. Rebase
- Mô tả: Mặc dù không phải là một loại hợp nhất (
merge
),rebase
là một kỹ thuật kết hợp lịch sử commit của một nhánh vào một nhánh khác bằng cách áp dụng lại các commit của nhánh hiện tại trên đầu của một nhánh khác.Rebase
giúp giữ cho lịch sử commit rõ ràng và tuyến tính hơn. - Ví dụ:
git rebase main
Sau khi rebase, nhánh hiện tại sẽ có tất cả các commit của nhánh main
, và các commit của nhánh hiện tại sẽ được đặt lên trên.
6.6. Cherry-Pick
- Mô tả: Cherry-Pick là một cách để chọn và áp dụng một hoặc nhiều commit từ một nhánh khác vào nhánh hiện tại mà không cần hợp nhất toàn bộ nhánh. Kỹ thuật này rất hữu ích khi bạn chỉ muốn đưa một số thay đổi cụ thể từ một nhánh sang nhánh khác.
- Ví dụ:
git cherry-pick <commit-hash>
6.7. Squash Merge
- Mô tả: Squash Merge kết hợp tất cả các commit từ nhánh được hợp nhất vào một commit duy nhất khi hợp nhất với nhánh hiện tại. Điều này giúp giữ cho lịch sử commit sạch sẽ và dễ theo dõi, đặc biệt khi bạn muốn giảm số lượng commit không cần thiết.
- Ví dụ:
git merge --squash feature-branch
6.8. Tóm tắt
- Three-Way Merge: Phương pháp hợp nhất chính, sử dụng khi có sự thay đổi trên cả hai nhánh.
- Fast-Forward Merge: Di chuyển con trỏ của nhánh hiện tại lên phía trước mà không tạo ra commit mới.
- Recursive Merge: Sử dụng khi có nhiều tổ tiên chung, đặc biệt trong các nhánh phân nhánh phức tạp.
- Octopus Merge: Hợp nhất nhiều nhánh cùng lúc.
- Ours Merge Strategy: Bỏ qua các thay đổi từ nhánh khác và giữ lại thay đổi từ nhánh hiện tại.
- Rebase: Áp dụng lại các commit từ nhánh hiện tại lên trên đầu của một nhánh khác.
- Cherry-Pick: Áp dụng một commit cụ thể từ một nhánh khác.
- Squash Merge: Gộp tất cả các commit từ một nhánh vào một commit duy nhất.
Những thuật ngữ này cung cấp các cách tiếp cận khác nhau trong việc quản lý và hợp nhất các thay đổi từ nhiều nhánh trong Git.
Xin chào,
Bài viết này sử dụng AI ChatGPT để viết sau đó được lựa chọn, biên tập lại nội dung, chỉ một phần nhỏ các bài là tôi tự viết. Nội dung thể hiện ý chí cá nhân về các vấn đề, giải pháp. Tôi lưu tại website này để học, tra cứu và chia sẻ.
Tôi là Minh, sống tại Hà Nội, kỹ sư phần mềm với hơn 20 năm kinh nghiệm. Các ngôn ngữ yêu thích của tôi là Swift, Objective-C, Java, Kotlin, .NET, HTML, JavaScript, CSS, Bootstrap, jQuery, AngularJS, Angular…
Các bạn có thể tải app của tôi trên App Store hoặc Google Play:
QuestionBank-Ôn thi vào 10: phiên bản iOS, phiên bản Android
TypingTest by QuestionBank: phiên bản iOS, phiên bản Android
Xin cảm ơn,
Minh