Sử dụng branch hiệu quả trong Git

Trong bài viết này, tôi sẽ giới thiệu với bạn mô hình sử dụng Git mà tôi đã và đang sử dụng trong các dự án nhiều năm trở lại đây. Đó là một mô hình thực sự hiệu quả, nhưng mãi đến giờ tôi mới có cơ hội để có thể viết về nó và chia sẻ với các bạn. Tôi sẽ không đi vào chi tiết dự án, mà chỉ xoay quanh chiến lược quản lý các nhánh (branch) mà thôi.

Mô hình branch trong git

Tại sao lại chọn Git?

Đã có quá nhiều cuộc chiến nổ ra khi bàn về ưu điểm nhược điểm của Git so với các công cụ quản lý phiên bản source code (source version control) tập trung khác như TFS, SVN.

Là một nhà phát triển, tôi đã sử dụng cả 3 và tôi cảm thấy thích Git nhất. Với Git chúng tôi không còn gặp tình trạng người này lock file, người kia phải chờ hoặc mượn, xung đột mã xảy ra thường xuyên giữa các thành viên trong team, ...

Nhờ đó mà hiệu quả công việc của cả team cũng tăng lên rất nhiều.

Mô hình Git mà tôi sử dụng hoạt động xoay quanh một kho lưu trữ từ xa (remote repository). Các thành viên trong team sẽ có các kho lưu trữ cục bộ (local repository).

Khi một thành viên trong team commit và push, mã của họ sẽ được đẩy lên remote repository và những thành viên khác trong team sẽ thấy những thay đổi này và có thể pull những thay đổi này về máy của họ.

Để làm việc hiệu quả thì nên tạo những loại branch sau cho Git:

  • Branch chính: master và develop.
  • Branch phụ: feature, hotfix, release.

Những branch chính trong Git

Đây là 2 branch chính quan trong nhất trong Git:

  • Branch master: có sẵn sau khi tạo repository, được coi là branch chính chứa phiên bản mã nguồn của môi trường production.
  • Branch develop: đây là nhánh được tách ra từ nhánh master và luôn đi song song với nhánh master, nó phản ánh trạng thái thay đổi mới nhất trong quá trình phát triển, chuẩn bị cho release tiếp theo.
Những branch chính trong Git là master và develop
Những branch chính trong Git là master và develop

Khi source code trên nhánh develop đạt đến một mức độ ổn định nào đó và sẵn sàng để release thì sẽ được merge sang bên nhánh master để release.

Như vậy, theo định nghĩa về nhánh master, chúng ta mặc định hiểu rằng khi có thay đổi được merge vào master thì tức là sẽ có một phiên bản production mới được release. Nhờ đó chúng ta có thể tự động publish lên production server mỗi khi có commit ở master bằng CI/CD.

Những branch phụ trong Git

Bên cạnh hai branch chính là master và develop, mô hình mà tôi đang sử dụng còn có thêm rất nhiều những branch phụ khác để giúp các thành viên trong team có thể làm việc hiệu quả, dễ dàng theo dõi các tính năng, chuẩn bị cho release hoặc fix nhanh các bug trên môi trường production.

Khác với hai branch chính, các branch phụ này chỉ tồn tại trong một khoảng thời gian ngắn, rồi sẽ bị xoá đi.

  • Branch feature: tách ra từ branch develop để code cho các tính năng mới hoặc cập nhật các tính năng cũ trên nhánh develop.
  • Branch release: tách ra từ branch develop để chuẩn bị publish lên môi trường production.
  • Branch hotfix: tách ra từ branch master để fix nhanh các bug xảy ra trên môi trường production.

Về mặt kỹ thuật, không có branch nào là "đặc biệt" hơn so với các branch khác cả. Tất cả chỉ là branch thông thường trong Git, chúng chỉ giúp chúng ta làm việc hiệu quả hơn mà thôi.

Branch feature trong Git

  • Tách ra từ branch: develop
  • Merge vào branch: develop
  • Quy tắc đặt tên: feature-*, tasknumber-*, ... Tốt nhất là thống nhất dùng chung một tiền tố bắt đầu để sau này dễ xóa mấy branch này.
Các branch phụ trong Git
Các branch phụ trong Git

Branch feature (hay còn gọi là topic branch) được sử dụng để phát triển các tính năng mới hoặc cập nhật tính năng cũ phục vụ cho release sau này.

Khi bắt đầu phát triển một chức năng, có thể chưa rõ được thời điểm chức năng đó được tích hợp vào hệ thống và release. Branch feature sẽ tồn tại trong quá trình chức năng được phát triển, cuối cùng sẽ được merge lại vào branch develop (khi quyết định lần release tới bao gồm chức năng đó) hoặc bị bỏ đi (khi thấy chức năng không còn cần thiết).

Tạo branch feature:

git checkout -b myfeature develop

Merge vào branch develop

git checkout develop
git merge --no--ff myfeature
git branch -d myfeature
git push origin develop

Tùy chọn --no-ff giúp thao tác merge luôn tạo ra một commit mới, ngay cả khi có thể merge theo fast-forward. Tùy chọn này giúp chúng ta không bị mất thông tin liên quan đến lịch sử các commit của branch feature.

Branch feature trong git
Branch feature trong git

Ở trường hợp bên phải, không thể nhận biết được những commit nào phát triển cùng chức năng nếu không ngồi đọc log message của từng commit.

Khi đó, nếu muốn revert lại cả feature (phải revert nhiều commit liên quan) thì thực sự là đau đầu. Và đó là lý do mà tùy chọn --no-ff được sử dụng.

Đương nhiên, nó sẽ tạo ra thêm vài commit, nhưng chả có vấn đề gì cả.

Branch release trong Git

  • Tách ra từ branch: develop
  • Merge vào branch: develop và master
  • Quy tắc đặt tên: release-*

Branch release được sử dụng để chuẩn bị cho release phiên bản mới lên production. Tất cả các công việc cuối cùng trước khi release sẽ được thực hiện ở đây, ngoài ra còn để fix nốt các bug lẻ tẻ, chuẩn bị metadata (version number, build dates, etc..).

Nhờ việc tách nhánh ra khỏi develop, chúng ta có thể tiếp tục phát triển các feature cho đợt release khác một cách bình thường.

Thời điểm được lựa chọn để tách nhánh từ develop là khi develop phản ánh được trạng thái mong muốn cho việc release mới. Ít nhất lúc đó tất cả các feature dành cho đợt release phải được merge vào develop rồi. Những feature nhắm đến các lần release sau thì chưa được merge vào, phải đợi sau khi tách nhánh.

Chúng ta sẽ tiến hành đánh version theo quy định của dự án ngay sau khi tạo branch release.

git checkout -b release-1.2 develop
./bump-version.sh 1.2
git commit -a -m "Bumped version number to 1.2"

Ở ví dụ trên, bump-version.sh chỉ tượng trưng cho một script thay đổi một vài file trong source code để phản ánh version mới. Sau khi tạo branch mới và chuyển sang branch đó, chúng ta sẽ thực hiện nâng version, rồi commit thao tác đó.

Branch mới này sẽ tồn tại cho đến khi việc release được thực hiện gọn ghẽ. Trong khoảng thời gian đó, có thể thực hiện fix bug ở branch này, tuy nhiên nghiêm cấm việc bổ sung feature mới lên đó. Tốt nhất nếu có features mới thì hãy merge vào develop, và đợi đợt release sau.

Khi source code trên branch release sẵn sàng để release, đầu tiên, phải merge vào branch master, sau khi release xong phải được merge lại vào branch develop để những lần release sau cũng chứa những thay đổi ở lần này.

git checkout master
git merge --no-ff release-1.2
git tag -a 1.2

Vậy là source code đã được release lên master, và đã được tag để tiện sau này tham chiếu.

git checkout develop
git merge --no-ff release-1.2

Ở bước này, rất có thể sẽ có xung đột code (conflict), nên hãy fix nó rồi commit nhé.

Bây giờ thì việc release đã hoàn thành, và chúng ta không cần đến branch này nữa và có thể xóa branch này bằng lệnh sau:

git branch -d release-1.2

Branch hotfix trong Git

  • Tách từ branch: master
  • Merge vào branch: develop và master
  • Quy tắc đặt tên: hotfix-*
Branch hotfix trong Git
Branch hotfix trong Git

Branch hotfix cũng giống branch release ở chỗ được sử dụng để chuẩn bị cho việc release lên production, chỉ khác ở chỗ là không có kế hoạch từ trước.

Khi có một bug nghiêm trọng trên bản production cần được giải quyết ngay lập tức, một nhánh hotfix sẽ được tách ra từ master và được đánh version để nhận biết.

Ưu điểm của việc tách nhánh này ở chỗ các thành viên khác của team có thể tiếp tục công việc ở nhánh develop trong khi những người khác có thể tập trung vào fix bug của production.

Branch hotfix được tạo ra từ branch master. Ví dụ hiện tại version 1.2 là phiên bản production đang chạy và xuất hiện lỗi nghiêm trọng. Tuy nhiên source code trên develop vẫn chưa ổn định, vì thế chúng ta phải tách nhánh hotfix và tiến hành sửa lỗi.

git checkout -b hotfix-1.2.1 master
./bump-version.sh 1.2.1
git commit -a -m "Bumped version number to 1.2.1"

Sau khi tách nhánh phải tiến hành up version luôn nhé!

Sau khi sửa lỗi, hãy thực hiện commit.

git commit -m "Fixed severe production problem"

Sau khi kết thúc sửa lỗi, những thay đổi phải được merge vào lại branch master, đồng thời cũng phải merge vào branch develop để ngăn lỗi xảy ra ở những lần release sau. Nghe rất giống với xử lý trên branch release phải không.

git checkout master
git merge --no-ff hotfix-1.2.1
git tag -a 1.2.1

git checkout develop
git merge --no-ff hotfix-1.2.1

Tuy nhiên, có một điểm cần lưu ý rằng: khi đang tồn tại một branch release thì cần phải merge hotfix vào branch release đó, thay vì merge vào branch develop.

Khi branch release được merge vào develop thì cuối cùng những thay đổi trong hotfix cũng được merge vào branch develop, nên không có vấn đề gì cả.

Trừ khi thực sự công việc ở develop cần phần hotfix ngay lập tức và ko thể đợi  branch release được merge, thì cần cẩn thận merge branch hotfix vào branch develop.

Cuối cùng, chúng ta cũng ko cần đến branch này nữa và có thể xóa bằng lệnh sau:

git branch -d hotfix-1.2.1

Phần kết luận

Qua hướng dẫn này mình hy vọng rằng bạn sẽ tìm ra được một cách làm việc hiệu quả hơn với Git.

Git
Bài Viết Liên Quan:
Cách giải quyết xung đột trong Git
Trung Nguyen 26/03/2021
Cách giải quyết xung đột trong Git

Bài viết này sẽ giới thiệu tới bạn: làm thế nào và khi nào các xung đột thường xảy ra, chúng ta sẽ làm gì để giải quyết chúng.

Git Cheat Sheet - 50 lệnh Git bạn nên biết
Trung Nguyen 26/03/2021
Git Cheat Sheet - 50 lệnh Git bạn nên biết

Git có nhiều lệnh khác nhau mà bạn có thể sử dụng. Hướng dẫn này giới thiệu với bạn 50 lệnh Git được mọi người sử dụng thường xuyên nhất.

Git là gì? Hướng dẫn cho Người mới bắt đầu về Git
Trung Nguyen 25/03/2021
Git là gì? Hướng dẫn cho Người mới bắt đầu về Git

Git là một hệ thống kiểm soát phiên bản (version control) mà các nhà phát triển sử dụng trên toàn thế giới.