Nội dung chính
Một trong những tính năng nổi bật giúp Git trở thành một công cụ quản lý phiên bản mạnh mẽ đó chính là khả năng phân tách nhánh và hợp nhất nhánh. Điều này giúp quá trình làm việc giữa các thành viên trở nên dễ dàng hơn. Git merge là một trong những cách phổ biến để hợp nhất các thay đổi từ nhánh này sang nhánh khác.
Đọc bài viết này để hiểu rõ hơn về:
- Git merge là gì?
- Cơ chế hoạt động của git merge
- Phân loại git merge
- Cách sử dụng git merge
Git branch là gì?
Git Branch là tính năng nổi bật của Git, nó giúp phân tách các luồng phát triển mã nguồn. Trong git, mỗi branch (nhánh) là một phiên bản riêng biệt của mã nguồn. Một branch bao gồm tập hợp các commit được kế thừa từ branch cha, và các commit mới của riêng branch hiện tại.
Việc sử dụng Branch, bạn có thể phát triển tính năng mới một cách độc lập nhưng khi hợp nhất vào nhánh Gốc, vẫn đảm bảo mã nguồn không bị thiếu sót. Git merge chính là một công cụ mạnh mẽ để thực hiện việc hợp nhất này.
Đọc thêm: Git branch: Hướng dẫn chi tiết 10+ thao tác branch cơ bản
Git merge là gì?
Git merge là một lệnh phổ biến trong Git, được sử dụng để hợp nhất các thay đổi (commits) từ một nhánh vào nhánh hiện tại. Đây là công cụ quan trọng để thực hiện gộp nhánh trong quá trình làm việc giữa nhiều luồng phát triển khác nhau trong mã nguồn.
Cú pháp cơ bản của Git merge:
git merge <tên nhánh>
Trường hợp sử dụng của Git merge:
- Khi hợp nhất commits từ một nhánh tính năng (feature branch) và nhánh chính (main, master hoặc develop)
- Khi cần đồng bộ hóa nội dung giữa nhánh khác vào nhánh hiện tại
Ví dụ, bạn có một nhánh tính năng được tách ra từ nhánh develop, và bây giờ bạn cần cập nhật các thay đổi mới nhất vừa được đưa vào nhánh develop, bạn sẽ thực hiện như sau:
git checkout <nhánh tính năng> git merge develop
Cơ chế hoạt động của Git merge
Giả sử ban đầu ta có nhánh A với commit C1, và nhánh B được tách ra từ nhánh A:
Nhánh B sử dụng để phát triển tính năng mới, nên đã tạo thêm 2 commit mới là C2 và C4. Còn đối với nhánh A, cũng đã được bổ sung commit C3 từ nhánh khác. Lúc này nếu chúng ta thực hiện:
git checkout A git merge B
Lúc này, Git sẽ tạo một commit merge mới trong nhánh A, kết nối lịch sử của cả nhánh B vào nhánh A. Để làm điều đó, Git tìm kiếm 3 commit mục tiêu:
- Đầu tiên là “common ancestor commit”: Nếu ta theo dõi lịch sử của hai branch trong một dự án, chúng luôn có ít nhất một commit chung (trong trường hợp này là C1)
- Commit cuối cùng của nhánh A
- Commit cuối cùng của nhánh B
Mục tiêu của Git merge là kết hợp các trạng thái hiện tại của hai nhánh. Vì vậy, các bản sửa đổi mới nhất của chúng rất quan trọng.
Việc sử dụng Merge sẽ tạo ra một ”merge commit” trên nhánh A, lúc này lịch sử commit của 2 nhánh sẽ như sau:
Phân loại Git merge
Three-way merge
Đây là kiểu hợp nhất xảy ra khi cả hai nhánh merge và nhánh được merge được có những thay đổi kể từ thời điểm có commit chung. Giống như ở ví dụ trong mục 3, khi mà ở nhánh chính và nhánh tính năng đều có những commit mới, lúc này nếu chúng ta thực hiện merge commit từ nhánh tính năng vào nhánh chính, Git sẽ thực hiện Three-war merge.
Đặc điểm của Three-way merge:
- Tạo ra một commit merge (Thường sẽ có nội dung message như “Merge branch A into B”)
- Lịch sử commit không được tuyến tính
- Có thể xảy ra xung đột (Conflict) khi các thay đổi ở hai nhánh đều xảy ra trên cùng một tập tin
- Thực hiện mặc định khi cả hai nhánh đều có những thay đổi riêng
Fast-forward merge
Trong trường hợp khi thực hiện lệnh git merge từ nhánh B vào nhánh A, nhưng trước đó nhánh A không có thêm thay đổi mới nào kể từ khi nhánh B được tách ra (commit cuối cùng của nhánh A chính là commit chung với nhánh B), thì lúc này Git sẽ thực hiện Fast-forward merge.
Git không tạo ra commit hợp nhất (merge commit), mà chỉ đơn giản “di chuyển” HEAD của nhánh A đến vị trí của commit cuối cùng của nhánh B.
Đặc điểm của Fast-forward merge:
- Không tạo ra commit merge
- Lịch sử commit được tuyến tính
- Thực hiện mặc định khi nhánh hiện tại không có thêm commit mới khác
Có thể chủ động thực hiện Fast-forward merge bằng tùy chọn –ff-only:
git merge --ff-only <tên nhánh>
Khi sử dụng tùy chọn này, nếu nhánh hiện tại đã có những thay đổi riêng, và không thể thực hiện fast-forward merge, Git sẽ báo lỗi và dừng hành động.
Squash merge
Khác với 2 loại merge phía trên, Squash merge là tùy chọn có thể được chỉ định. Loại merge này kết hợp tất cả các commits từ nhánh mục tiêu thành một commit duy nhất trước khi hợp nhất vào nhánh hiện tại.
Cú pháp sử dụng:
git merge --squash <nhánh mục tiêu> git commit -m “nội dung merge”
Ví dụ như ở tình huống thuộc phần 3, chúng ta có thể thực hiện Squash merge thay đổi của nhánh B sang nhánh A bằng cách:
git checkout A git merge --squash B git commit -m “squash merge from B”
Đặc điểm của Squash merge:
- Tạo ra một commit gộp trên nhánh hiện tại
- Không giữ lại lịch sử commit của nhánh mục tiêu
- Được thực hiện khi có chỉ định (sử dụng tùy chọn –squash khi merge)
- Nội dung của nhánh hiện tại được đơn giản hóa
Dưới đây là bảng so sánh nhánh 3 loại merge thường thấy trong Git:
Tiêu chí | Three-way | Fast-forward | Squash |
Trường hợp sử dụng | Thực hiện tự động khi cả 2 nhánh merge đều có những thay đổi riêng | Thực hiện tự động hoặc thực hiện thủ công khi dùng tùy chọn –ff-only khi nhánh hiện tại không có thay đổi mới so với nhánh mục tiêu | Thực hiện thủ công khi sử dụng tùy chọn –squash |
Tạo ra commit mới | Có. Tạo ra commit merge | Không tạo ra commit mới | Tạo ra commit gộp với nội dung là các thay đổi từ những commits của nhánh mục tiêu |
Lịch sử commit của nhánh hiện tại | Lịch sử commit trở nên không tuyến tính | Lịch sử commit được tuyến tính | Lịch sử commit được tuyến tính |
Lưu lại commit của nhánh mục tiêu | Có | Có | Không. Chỉ giữ lại commit gộp |
Xảy ra xung đột | Có khả năng xảy ra xung đột | Không có hoặc hiếm khi xảy ra xung đột | Không có hoặc hiếm khi xảy ra xung đột |
Độ phổ biến | Thường được sử dụng | Chỉ những trường hợp đặc biệt mới xảy ra | Chỉ khi cần đơn giản hóa nhánh hiện tại |
Quy trình thực hiện Git merge
Giả sử chúng ta đang có nhánh main và nhánh tính năng feature/login, nhánh tính năng này đã có những commits mới về việc xây dựng tính năng đăng nhập cho người dùng. Bây giờ chúng ta cần hợp nhất những thay đổi đó vào nhánh main để thực hiện kiểm thử.
Chúng ta sẽ cần thực hiện những bước sau:
Bước 1: Checkout qua nhánh cần được merge (ở đây sẽ là nhánh main)
git checkout main
Bước 2: Đảm bảo nhánh main đã ở trạng thái mới nhất
git pull origin main
Bước 3: Thực hiện merge các thay đổi từ nhánh tính năng
git merge feature/login
Bước 4:
- Trường hợp nếu không có xung đột, hành động merge sẽ được kết thúc
- Trường hợp có xung đột xảy ra, thực hiện xử lý xung đột:
Sử dụng git status để kiểm tra các tập tin nào đang bị xung đột và mở tập tin đó lên bằng các trình soạn thảo như vscode, sublime text,… để giải quyết. Git sẽ đánh dấu phần mã bị xung đột như sau:
<<<<<<< HEAD # Đây là thay đổi trên nhánh main ... ======= # Đây là thay đổi từ nhánh feature/login ... >>>>>>> main
Trong đó:
- <<<<<<< HEAD: Phần mã nguồn trên nhánh cục bộ.
- =======: Ranh giới giữa phần mã cục bộ và remote.
- >>>>>>> main: Phần mã từ remote repository.
Thực hiện xử lý xung đột bằng cách chọn 1 trong 2 nội dung hoặc kết hợp cả 2.
Sau khi hoàn tất, sử dụng lệnh sau để hoàn tất hành động merge:
git add <tên tập tin> git commit -m "nội dung commit"
So sánh Git merge và Git rebase
Git merge và Git rebase đều là những phương pháp được sử dụng để hợp nhất các thay đổi từ nhánh này vào nhánh khác trong Git. Tuy nhiên giữa chúng có cơ chế hoạt động khác nhau, dưới đây là những so sánh chi tiết:
Tiêu chí | Git merge | Git rebase |
Cú pháp | git merge <tên nhánh> | git rebase <tên nhánh> |
Lịch sử commit | Không làm xáo trộn lịch sử, chỉ bổ sung thêm commit merge (nếu có) | Sắp xếp lại lịch sử commit, đưa các commit của nhánh hiện tại lên đầu của nhánh mục tiêu. Lịch sử commit trở nên tuyến tính. |
Merge commit | Có thể tạo ra commit merge (khi thực hiện Three-way merge) | Không tạo ra commit merge |
Xử lý xung đột | Xử lý xung đột một lần duy nhất trong quá trình merge | Có thể cần xử lý xung đột nhiều lần đối với từng commit khi rebase |
Trường hợp sử dụng | Khi cần giữ lại thông tin nguồn gốc commit của các nhánh | Khi muốn giữ cho lịch sử commit được sạch sẽ và đơn giản |
Đọc thêm: Git rebase: Hướng dẫn sử dụng từ cơ bản đến nâng cao
Những lưu ý khi sử dụng Git merge
Khi sử dụng git merge để làm việc trong Git, để tránh những vấn đề tiềm ẩn và đảm bảo cho quy trình làm việc được hiệu quả, bạn cần lưu ý một số điểm quan trọng như sau:
- Kiểm tra nhánh hiện tại đã đúng nhánh cần được merge hay chưa
- Cần cập nhật trạng thái mới nhất cho nhánh hiện tại trước khi merge để tránh các vấn đề xung đột sau này
- Không nên merge trực tiếp vào nhánh chính (main, master): Đối với các dự án lớn, bạn nên thực hiện merge thông qua Pull Request để đảm bảo các thay đổi đều được kiểm duyệt trước khi thực hiện hợp nhất
- Xóa các nhánh không còn được sử dụng sau khi merge để giữ cho mã nguồn được sạch sẽ
- Khi có xung đột xảy ra, nên trao đổi với các thành viên khác trước khi lựa chọn đoạn mã cần được giữ lại. Điều này tránh xảy ra mất dữ liệu và tránh lỗi.
Các câu hỏi thường gặp về Git merge
Khi nào nên sử dụng git merge thay vì git rebase?
Git merge và Git rebase đều được sử dụng để hợp nhất các thay đổi từ nhánh này vào nhánh khác. Tuy nhiên giữa chúng lại có cách hoạt động và tác động đến lịch sử commit khác nhau.
Dưới đây là một số trường hợp bạn nên sử dụng Git merge thay vì Git rebase:
- Khi muốn giữ lại các lịch sử commit gốc của các nhánh thành phần: Bởi vì git merge không làm thay đổi các commit đã có, mà chỉ tạo thêm commit merge, vậy nên lịch sử của các nhánh tính năng vẫn sẽ được giữ nguyên. Điều này thuận tiện cho việc điều tra lịch sử commit.
- Khi bạn cần thể hiện rõ các hành động merge: Bởi vì git merge thường tạo ra các merge commit, vậy nên trong lịch sử commit, chúng ta sẽ dễ dàng thấy được những thời điểm có xảy ra hành động merge.
- Khi nhánh được chia sẻ hoặc đang được sử dụng chung: Git rebase sẽ sắp xếp lại trật tự các commit, vì vậy dễ gây ra xung đột và mất đồng bộ với những thành viên khác đang sử dụng chung nhánh hiện tại. Vì vậy git merge sẽ là lựa chọn an toàn, giúp đảm bảo khả năng hoạt động cho nhóm làm việc.
Làm sao để hạn chế xung đột (conflict) khi sử dụng git merge?
Xung đột thường xảy ra khi Git không thể tự động hợp nhất các thay đổi giữa hai nhánh. Điều này thường xuất hiện khi cả hai nhánh đều có những thay đổi ở cùng một phần thuộc một tập tin.
Điều này rất khó để phòng tránh hoàn toàn, chúng ta chỉ có thể hạn chế một phần khả năng xuất hiện của những xung đột này bằng cách:
- Thực hiện cập nhật, đồng bộ dữ liệu nhánh một cách thường xuyên trước, sau khi tách nhánh, trước khi hợp nhất nhánh.
- Các nhánh tính năng không nên quá lớn và sử dụng quá lâu. Tránh chứa nhiều commit cho nhiều tính năng trên cùng một nhánh làm việc. Điều này giúp hạn chế số lượng commit thay đổi giữa nhánh làm việc và nhánh chính.
- Tối ưu hóa quy trình làm việc nhóm để tránh việc nhiều người cùng làm song song trong một mô-đun hoặc một tập tin.
Làm sao để hủy bỏ hành động git merge?
Trong trường hợp xuất hiện xung đột và quá trình merge bị gián đoạn, bạn có thể hoàn tác hành động merge bằng lệnh sau:
git merge -abort
Điều này sẽ dừng hành động merge và trả về trạng thái trước khi thực hiện merge cho nhánh hiện tại.
Trong trường hợp quá trình merge đã hoàn tất và commit merge đã được tạo ra trên nhánh hiện tại. Bạn có thể sử dụng git reset hoặc git revert để hoàn tác các thay đổi vừa được bổ sung sau khi merge.
Tiêu chí | Git reset | Git revert |
Cú pháp | git reset –hard HEAD~1 | git revert -m “thông điệp” <merge commit hash> |
Cơ chế | Loại bỏ merge commit khỏi lịch sử commit, các thay đổi cũng sẽ bị loại bỏ | Tạo ra một commit mới với nội dung đảo ngược so với merge commit |
Lịch sử commit | Trở về trạng thái như trước khi merge | Lịch sử sẽ vẫn còn merge commit và có thêm commit mới |
Trường hợp sử dụng | Khi nội dung merge chưa được đẩy lên kho lưu trữ từ xa và không muốn giữ lại dấu vết của hành động merge | Để đảm bảo an toàn dữ liệu và tránh gây bất đồng bộ nếu khi đã đẩy nội dung merge lên kho lưu trữ từ xa |
Tổng kết
Git merge là một lệnh mạnh mẽ trong Git, giúp bạn hợp nhất các thay đổi từ nhánh này sang nhánh khác một cách nhanh chóng và an toàn. Nó đóng một vai trò quan trọng trong hoạt động quản lý mã nguồn.
Qua bài viết này, ITviec hi vọng bạn đọc có thể hiểu hơn về cách thức hoạt động của Git merge, cũng như cách để sử dụng nó hiệu quả.