Git: Git merge (Hợp nhất Git)
Hợp nhất (Merge) là cách Git sắp xếp lại lịch sử rẽ nhánh. Lệnh git merge
cho phép bạn lấy các dòng phát triển độc lập được tạo bởi git branch
và tích hợp chúng vào một nhánh duy nhất.
Lưu ý rằng tất cả các lệnh được trình bày bên dưới hợp nhất vào nhánh hiện tại. Nhánh hiện tại sẽ được cập nhật để phản ánh việc hợp nhất, nhưng nhánh mục tiêu sẽ hoàn toàn không bị ảnh hưởng. Một lần nữa, điều này có nghĩa là git merge
thường được sử dụng cùng với git checkout
cho việc chọn nhánh hiện tại và git branch -d
để xóa nhánh mục tiêu lỗi thời.
Cách thức hoạt động
Git merge
sẽ kết hợp nhiều chuỗi xác nhận thành một lịch sử thống nhất. Trong các trường hợp sử dụng thường xuyên nhất, git merge
được sử dụng để kết hợp hai nhánh. Các ví dụ sau trong tài liệu này sẽ tập trung vào mẫu hợp nhất nhánh này. Trong các tình huống này, git merge
nhận hai con trỏ xác nhận, thường là các mẹo rẽ nhánh và sẽ tìm thấy một commit cơ sở chung giữa chúng. Khi Git tìm thấy một commit cơ sở chung, nó sẽ tạo một "commit hợp nhất" mới kết hợp các thay đổi của từng chuỗi cam kết hợp nhất được xếp hàng đợi.
Giả sử chúng ta có một tính năng nhánh mới dựa trên nhánh main
. Bây giờ chúng ta muốn hợp nhất nhánh tính năng này vào main
.
Gọi lệnh này sẽ hợp nhất tính năng nhánh đã chỉ định vào nhánh hiện tại, chúng ta sẽ giả sử main
. Git sẽ tự động xác định thuật toán hợp nhất (thảo luận bên dưới).
Các commit hợp nhất là duy nhất so với các commit khác trong thực tế là chúng có hai commit cha. Khi tạo một commit hợp nhất, Git sẽ cố gắng tự động hợp nhất một cách kỳ diệu các lịch sử riêng biệt cho bạn. Nếu Git gặp một phần dữ liệu bị thay đổi trong cả hai lịch sử, nó sẽ không thể tự động kết hợp chúng. Trường hợp này là xung đột kiểm soát phiên bản và Git sẽ cần sự can thiệp của người dùng để tiếp tục.
Chuẩn bị hợp nhất
Trước khi thực hiện hợp nhất, có một số bước chuẩn bị cần thực hiện để đảm bảo quá trình hợp nhất diễn ra suôn sẻ.
Xác nhận nhánh nhận
Thực thi git status
để đảm bảo rằng HEAD
đang trỏ đến đúng nhánh nhận hợp nhất. Nếu cần, thực hiện git checkout
để chuyển sang nhánh nhận. Trong trường hợp của chúng ta, ta sẽ thực hiện git checkout main.
Tìm nạp các commit từ xa mới nhất
Đảm bảo rằng nhánh nhận và nhánh hợp nhất được cập nhật với các thay đổi từ xa mới nhất. Thực thi git fetch
để lấy các xác nhận từ xa mới nhất. Khi quá trình tìm nạp hoàn tất, cần đảm bảo nhánh main
có các bản cập nhật mới nhất bằng cách thực thi git pull.
Hợp nhất
Khi bước "chuẩn bị hợp nhất" được thực hiện, việc hợp nhất có thể được bắt đầu bằng cách thực hiện git merge
trong đó là tên của nhánh sẽ được hợp nhất vào nhánh nhận.
Hợp nhất chuyển tiếp nhanh
Hợp nhất chuyển tiếp nhanh có thể xảy ra khi có một đường dẫn tuyến tính từ đầu nhánh hiện tại đến nhánh mục tiêu. Thay vì “thực sự” hợp nhất các nhánh, tất cả những gì Git phải làm để tích hợp lịch sử là di chuyển (nghĩa là “tua nhanh”) đầu nhánh hiện tại đến đầu nhánh mục tiêu. Điều này kết hợp các lịch sử một cách hiệu quả, vì tất cả các commit có thể truy cập được từ nhánh mục tiêu hiện có sẵn thông qua nhánh hiện tại. Ví dụ: hợp nhất nhanh một số tính năng vào main
sẽ giống như sau:
Tuy nhiên, không thể hợp nhất chuyển tiếp nhanh nếu các nhánh đã phân kỳ. Khi không có đường dẫn tuyến tính đến nhánh mục tiêu, Git không có lựa chọn nào khác ngoài việc kết hợp chúng thông qua hợp nhất 3 chiều. Hợp nhất 3 chiều sử dụng một commit chuyên dụng để liên kết hai lịch sử lại với nhau. Danh pháp xuất phát từ thực tế là Git sử dụng ba lần xác nhận để tạo ra lần xác nhận hợp nhất: hai mẹo nhánh và tổ tiên chung của chúng.
Mặc dù bạn có thể sử dụng một trong hai chiến lược hợp nhất này, nhưng nhiều nhà phát triển thích sử dụng các hợp nhất chuyển tiếp nhanh (được hỗ trợ thông qua rebasing) cho các tính năng nhỏ hoặc sửa lỗi, trong khi dành riêng các hợp nhất 3 chiều để tích hợp các tính năng chạy lâu hơn. Trong trường hợp sau, commit hợp nhất kết quả đóng vai trò là sự kết hợp tượng trưng của hai nhánh.
Ví dụ đầu tiên của chúng ta thể hiện sự hợp nhất chuyển tiếp nhanh. Đoạn mã dưới đây tạo một nhánh mới, thêm hai lần xác nhận vào nhánh đó, sau đó tích hợp nhánh đó vào dòng chính bằng cách hợp nhất chuyển tiếp nhanh.
# Start a new feature
git checkout -b new-feature main
# Edit some files
git add <file>
git commit -m "Start a feature"
# Edit some files
git add <file>
git commit -m "Finish a feature"
# Merge in the new-feature branch
git checkout main
git merge new-feature
git branch -d new-feature
Đây là quy trình công việc phổ biến cho các nhánh chủ đề tồn tại trong thời gian ngắn được sử dụng nhiều hơn dưới dạng phát triển biệt lập hơn là một công cụ tổ chức cho các tính năng hoạt động lâu hơn.
Cũng lưu ý rằng Git không nên phàn nàn về git branch -d
, vì tính năng mới hiện có thể truy cập được từ nhánh chính.
Trong trường hợp bạn yêu cầu một commit hợp nhất trong quá trình hợp nhất chuyển tiếp nhanh cho mục đích lưu giữ hồ sơ, bạn có thể thực hiện git merge
với tùy chọn --no-ff
.
git merge --no-ff <branch>
Lệnh này hợp nhất nhánh đã chỉ định vào nhánh hiện tại, nhưng luôn tạo ra một commit hợp nhất (ngay cả khi đó là hợp nhất chuyển tiếp nhanh). Điều này hữu ích để ghi lại tất cả các hợp nhất xảy ra trong kho lưu trữ của bạn.
Hợp nhất 3 chiều
Ví dụ tiếp theo rất giống, nhưng yêu cầu hợp nhất 3 chiều vì main
xử lý trong khi tính năng đang được tiến hành. Đây là tình huống phổ biến đối với các tính năng lớn hoặc khi nhiều nhà phát triển đang làm việc đồng thời trên một dự án.
Start a new feature
git checkout -b new-feature main
# Edit some files
git add <file>
git commit -m "Start a feature"
# Edit some files
git add <file>
git commit -m "Finish a feature"
# Develop the main branch
git checkout main
# Edit some files
git add <file>
git commit -m "Make some super-stable changes to main"
# Merge in the new-feature branch
git merge new-feature
git branch -d new-feature
Lưu ý rằng Git không thể thực hiện hợp nhất nhanh, vì không có cách nào để di chuyển main
lên new-feature
mà không quay lui.
Đối với hầu hết các quy trình công việc, new-feature
sẽ là một tính năng lớn hơn nhiều và mất nhiều thời gian để phát triển, đó là lý do tại sao các commit mới sẽ xuất hiện trên main
trong thời gian chờ đợi. Nếu nhánh tính năng của bạn thực sự nhỏ như nhánh trong ví dụ trên, thì có lẽ tốt hơn hết là bạn nên khởi động lại nó trên main
và thực hiện hợp nhất nhanh. Điều này ngăn các commit hợp nhất không cần thiết làm lộn xộn lịch sử dự án.
Giải quyết xung đột
Nếu cả hai nhánh bạn đang cố hợp nhất đều thay đổi cùng một phần của cùng một tệp, Git sẽ không thể tìm ra phiên bản nào sẽ sử dụng. Khi tình huống như vậy xảy ra, nó sẽ dừng ngay trước khi hợp nhất commit để bạn có thể giải quyết xung đột theo cách thủ công.
Phần tuyệt vời của quy trình hợp nhất của Git là nó sử dụng quy trình edit/state/commit quen thuộc để giải quyết xung đột hợp nhất. Khi bạn gặp xung đột hợp nhất, chạy lệnh git status
sẽ cho bạn biết tệp nào cần được giải quyết. Ví dụ: nếu cả hai nhánh sửa đổi cùng một phần của hello.py
, bạn sẽ thấy nội dung như sau:
On branch main
Unmerged paths:
(use "git add/rm ..." as appropriate to mark resolution)
both modified: hello.py
Các xung đột được thể hiện như thế nào
Khi Git gặp xung đột trong quá trình hợp nhất, Git sẽ chỉnh sửa nội dung của các tệp bị ảnh hưởng bằng các chỉ báo trực quan đánh dấu cả hai bên của nội dung xung đột. Những điểm đánh dấu trực quan này là: <<<<<<<, ======= và >>>>>>>. Thật hữu ích khi tìm kiếm một dự án cho các chỉ số này trong quá trình hợp nhất để tìm ra nơi xung đột cần được giải quyết.
here is some content not affected by the conflict
<<<<<<< main
this is conflicted text from main
=======
this is conflicted text from feature branch
>>>>>>> feature branch;
Nói chung, nội dung trước điểm đánh dấu =======
là nhánh nhận và phần sau là nhánh hợp nhất.
Khi bạn đã xác định được các phần xung đột, bạn có thể truy cập và sửa lỗi hợp nhất theo ý thích của mình. Khi bạn đã sẵn sàng hoàn tất quá trình hợp nhất, tất cả những gì bạn phải làm là chạy git add
trên (các) tệp bị xung đột để thông báo cho Git rằng chúng đã được giải quyết. Sau đó, bạn chạy bình thường git commit
để tạo commit hợp nhất. Đó chính xác là quy trình giống như thực hiện một ảnh chụp nhanh thông thường, điều đó có nghĩa là các nhà phát triển bình thường dễ dàng quản lý việc hợp nhất của riêng họ.
Lưu ý rằng xung đột hợp nhất sẽ chỉ xảy ra trong trường hợp hợp nhất 3 bên. Không thể có các thay đổi xung đột trong hợp nhất nhanh.
Tổng kết
Bài viết này là tổng quan về lệnh git merge
. Hợp nhất là một quá trình thiết yếu khi làm việc với Git. Chúng ta đã thảo luận về cơ chế bên trong đằng sau việc hợp nhất và sự khác biệt giữa hợp nhất chuyển tiếp nhanh và hợp nhất ba chiều, hợp nhất thực sự. Một số điểm chính là:
- Hợp nhất Git kết hợp các chuỗi xác nhận thành một lịch sử xác nhận thống nhất.
- Có hai cách chính mà Git sẽ hợp nhất: Fast Forward và Three way
- Git có thể tự động hợp nhất các cam kết trừ khi có những thay đổi xung đột trong cả hai chuỗi commit.
Tài liệu này đã tích hợp và tham chiếu các lệnh Git khác như: git branch
, git pull
và git fetch
.
Nguồn: https://www.atlassian.com/git/tutorials/using-branches/git-merge