Giải Thích Về Phân Nhánh (Branching) Trong Git

0
272
Làm chủ phân nhánh Git branch
Làm chủ phân nhánh Git branch

Hầu hết các lập trình viên đều sử dụng git branch hàng ngày, nhưng không phải ai cũng nắm được bản chất thực sự của nó. Trong bài viết này, chúng ta sẽ khám phá những khái niệm về nhánh trong Git.

Phân nhánh trong git
Phân nhánh trong git

Tại Sao Phân Nhánh Trong Git Lại “Nhanh Thần Tốc”?

Hầu hết mọi Hệ thống Quản lý Phiên bản (Version Control System – VCS) đều hỗ trợ phân nhánh (branching). Tuy nhiên, việc tạo nhánh bắt buộc phải copy toàn bộ thư mục mã nguồn, mất nhiều thời gian.

Git thì hoàn toàn khác. Một nhánh trong Git chỉ là một con trỏ “nhẹ ký” và có thể di chuyển. Điều này khuyến khích các quy trình làm việc mà ở đó việc tạo, hợp nhất và xóa nhánh diễn ra thường xuyên, thậm chí nhiều lần trong một ngày.

Nhánh (Branch) Thực Sự Là Gì?

Để bắt đầu, chúng ta cần hiểu cách git lưu dữ liệu. Không giống VCS khác là lưu danh sách thay đổi trên tệp (diffs). Git lưu dữ liệu là một bản snapshot của toàn bộ dự án tại một thời điểm.

Mỗi Commit là một “ảnh chụp” (snapshot) của code tại một thời điểm, kèm thông tin và trỏ về commit trước đó.

Branch (nhánh) trong Git chỉ là một con trỏ trỏ tới commit mới nhất của nhánh đó, không phải bản sao code. Vì vậy tạo nhánh rất nhanh. Mỗi nhánh thực chất là một file nhỏ chứa mã SHA-1 của commit mà nó trỏ tới.

HEAD là con trỏ đặc biệt cho biết bạn đang đứng ở nhánh nào hiện tại.

 Các Thao Tác Cơ Bản: “Du Hành” Giữa Các Nhánh

Có hai lệnh cơ bản để bạn tạo và “du hành” giữa các nhánh.

1. Tạo một nhánh mới: git branch <tên-nhánh>

    ◦ Lệnh này tạo ra một con trỏ mới trỏ đến cùng một commit mà HEAD đang trỏ tới. Nó chỉ tạo ra nhánh chứ chưa chuyển bạn sang nhánh đó.

2. Chuyển sang một nhánh: git checkout <tên-nhánh>

    ◦ Lệnh này thực hiện hai việc:

        1. Di chuyển con trỏ HEAD để trỏ đến nhánh bạn đã chọn.

        2. Cập nhật các tệp trong thư mục làm việc của bạn để khớp với bản ghi nhanh (snapshot) của nhánh đó.

Khi bạn chuyển nhánh bằng git checkout, một điều quan trọng sẽ xảy ra: Git sẽ tự động thay đổi các tệp trong thư mục làm việc của bạn để chúng khớp với bản ghi nhanh (snapshot) của commit cuối cùng trên nhánh đó. Về cơ bản, nó “tua lại” hoặc thay đổi trạng thái dự án của bạn để phản ánh lịch sử của nhánh mới.

Để thuận tiện, bạn có thể kết hợp cả hai bước trên thành một lệnh duy nhất:

• git checkout -b <tên-nhánh>: Lệnh này sẽ tạo một nhánh mới và ngay lập tức chuyển HEAD sang nhánh đó.

Bây giờ, hãy cùng xem những khái niệm này hoạt động như thế nào trong một kịch bản thực tế.

Quy Trình Phân Nhánh Điển Hình trong thực tế

Hãy tưởng tượng bạn đang làm việc trên một trang web. Quy trình làm việc của bạn sẽ trải qua các bước sau:

1. Bắt đầu một tính năng mới (iss53)

Bạn đang ở trên nhánh master và bắt đầu làm việc với yêu cầu số 53 trong hệ thống theo dõi lỗi. Thay vì viết code trực tiếp trên master, bạn tạo một nhánh riêng cho tính năng này.

# Tạo nhánh iss53 và chuyển sang nhánh đó ngay lập tức
$ git checkout -b iss53
Ví dụ phân nhánh
Ví dụ phân nhánh

Lúc này, cả hai con trỏ master và iss53 đều trỏ đến cùng một commit. Nhưng con trỏ HEAD bây giờ đang trỏ đến iss53. Bất kỳ commit mới nào bạn tạo ra sẽ chỉ di chuyển con trỏ iss53 về phía trước, trong khi master vẫn đứng yên.

2. Gián đoạn khẩn cấp: Sửa lỗi nóng (hotfix)

Đột nhiên, bạn nhận được một cuộc gọi khẩn: có một lỗi nghiêm trọng trên trang web sản phẩm và cần được vá ngay lập tức. Đây là lúc sức mạnh của việc phân nhánh cho phép bạn “chuyển đổi bối cảnh” (context-switch) một cách hoàn toàn và nhanh chóng. Bạn không cần phải triển khai tính năng iss53 còn dang dở của mình. Thay vào đó, bạn chỉ cần:

Bước 1. Chuyển về nhánh master, nơi chứa code ổn định đang chạy trên sản phẩm.

Bước 2. Thư mục làm việc của bạn ngay lập tức được khôi phục về trạng thái của master, như thể bạn chưa từng bắt đầu làm việc trên iss53.

Ví dụ git checkout master
Ví dụ git checkout master

Bước 3. Tạo một nhánh mới cho việc sửa lỗi. git checkout -b hotfix

Tạo branch hotfix
Tạo branch hotfix

Bước 4. Bây giờ, bạn thực hiện sửa lỗi và tạo một commit mới trên nhánh hotfix.

Người dùng thao tác trên nhánh hotfix
Người dùng thao tác trên nhánh hotfix

3. Gộp nhánh và triển khai bản vá (hotfix -> master)

Sau khi sửa lỗi xong và kiểm thử cẩn thận, đã đến lúc đưa bản vá này vào nhánh chính để triển khai.

1. Chuyển về master:

2. Gộp nhánh hotfix vào master:

Bạn sẽ thấy thông báo “Fast-forward”. Đây là một thuật ngữ quan trọng. Nó xảy ra khi commit trên nhánh bạn đang gộp (hotfix) là “con cháu” trực tiếp của commit trên nhánh đích (master). Lịch sử của chúng là một đường thẳng. Trong trường hợp này, Git không cần tạo commit gộp mới; nó chỉ đơn giản là di chuyển con trỏ master về phía trước để trỏ đến cùng commit với hotfix.

Sau khi gộp xong, nhánh hotfix không còn cần thiết nữa—vì giờ đây nhánh master đã trỏ đến cùng một vị trí. Bạn có thể xóa nó một cách an toàn.

$ git branch -d hotfix

4. Quay lại công việc (iss53)

Bản vá khẩn cấp đã được triển khai. Giờ bạn có thể quay lại với công việc của mình.

$ git checkout iss53

Bạn có thể tiếp tục làm việc trên tính năng iss53, hoàn toàn tách biệt với những thay đổi từ hotfix, cho đến khi bạn sẵn sàng để gộp nó vào master.

Câu chuyện trên đã cho chúng ta thấy hai loại hợp nhất (merge) khác nhau. Hãy cùng định nghĩa chúng rõ ràng hơn.

Quay lại công việc iss53
Quay lại công việc iss53

Hợp Nhất Các Nhánh (Merging)

Khi bạn chạy lệnh git merge, Git sẽ thực hiện một trong hai kiểu hợp nhất sau:

Loại MergeKhi Nào Xảy RaKết Quả Là Gì?Ví Dụ Trong Câu Chuyện
Fast-forwardKhi nhánh bạn gộp vào là con cháu trực tiếp của nhánh đích. Lịch sử là một đường thẳng.Git chỉ cần di chuyển con trỏ của nhánh đích về phía trước. Không tạo commit mới.Gộp nhánh hotfix vào master.
Three-way MergeKhi lịch sử của hai nhánh đã rẽ ra từ một điểm chung trong quá khứ.Git tạo ra một “merge commit” mới đặc biệt, có hai commit cha.Gộp nhánh iss53 (sau khi master đã có hotfix) vào master.

Xung đột khi hợp nhất (Merge Conflict) thì sao?

Đôi khi, quá trình hợp nhất không diễn ra suôn sẻ. Nếu bạn thay đổi cùng một phần của cùng một tệp theo những cách khác nhau trên cả hai nhánh, Git sẽ không thể tự động hợp nhất chúng. Đây được gọi là xung đột khi hợp nhất (merge conflict).

Git sẽ tạm dừng quá trình hợp nhất và chèn các dấu hiệu xung đột vào tệp bị ảnh hưởng:

<<<<<<< HEAD
<div id="footer">contact : [email protected]</div>
=======
<div id="footer"> please contact us at [email protected] </div>
>>>>>>> iss53

• Nội dung giữa <<<<<<< HEAD và ======= là phiên bản từ nhánh hiện tại của bạn (HEAD).

• Nội dung giữa ======= và >>>>>>> iss53 là phiên bản từ nhánh bạn đang gộp vào.

Nhiệm vụ của bạn là phải chỉnh sửa tệp này theo cách thủ công để giải quyết xung đột, xóa các dấu hiệu trên, sau đó chạy git add và git commit để hoàn tất việc hợp nhất.

Quy trình giải quyết conflict khi merge
Quy trình giải quyết conflict khi merge

Tổng Kết: Nhánh Là Công Cụ, Hãy Dùng Thường Xuyên!

Thông điệp cốt lõi cần ghi nhớ là: các nhánh trong Git chỉ là những con trỏ nhẹ ký và rẻ tiền.

Chính vì chúng rất dễ tạo và hủy, các lập trình viên được khuyến khích sử dụng chúng một cách thường xuyên cho mọi thứ—từ các tính năng lớn kéo dài hàng tuần đến các bản sửa lỗi nhỏ chỉ trong vài phút. Quy trình làm việc này giúp giữ cho nhánh chính (master) luôn ổn định, đồng thời tổ chức công việc thành các đơn vị logic, độc lập và dễ quản lý. Việc nắm vững cách sử dụng nhánh sẽ thay đổi hoàn toàn cách bạn phát triển phần mềm.

Nắm vững phân nhánh không chỉ là học một lệnh mới; đó là học một cách làm việc thông minh hơn, an toàn hơn và hiệu quả hơn.