Giới thiệu về Git - Phần 2
Sử dụng git trong thực tế
Tại sao lại là git?
Git mang lại cho chúng ta 1 cách tiếp cận mới, nó thay đổi cách nghĩ của người phát triển phần mềm về việc merging và branching. Với những công cụ truyền thống là CVS/Subversion thì nguời phát triển luôn luôn lo lắng, e ngại mỗi lúc mergeing/branching với những confict tiềm tàng ẩn chứa rủi ro.
Nhưng đối với git, việc đó trở nên dễ dàng và an toàn hơn bao giờ hết, việc mergeing và branching đã trở thành những công việc thường ngày mà chúng ta làm trong quá trình phát triển. Trong những ebook hướng dẫn sử dụng với CVS/Subversion thì hướng dẫn branching và merging thường được đề cập ở những chap cuối của sách (phần nâng cao) dành cho những người đã thông thạo, còn với git thì nó lại thuộc phần basic (cơ bản), điều này dễ dàng cho ta thấy việc sử dụng git để merging và branching dễ dàng và an toàn hơn so với các phương thức cổ điển.
Những cải tiến suất sắc trong merging và branching của git so với những công cụ truyền thống đã giúp cho việc quản lý version của source code trở nên đơn giản và nhẹ nhàng hơn rất nhiều, vì vậy mà các nhà phát triển dần dần đã chuyển sang sử dụng git để thay thế các phương pháp truyền thống.
Vậy sử dụng git trong thực tế như thế nào, tổ chức và quản lý các branch, tiến hành mergeing như thế nào là đúng đắn??? Sau đây sẽ là hướng dẫn cụ thể việc sử dụng git trong phát triển phần mềm thực tế, sẽ giúp các bạn nắm rõ hơn cách tổ chức 1 bộ source bằng git trong quá trình phát triển.
Decentralized but centralized (phân tán nhưng tập trung)
Với 1 source được quản lý bởi git, luôn chỉ có duy nhất 1 "central truth repo" (1 repo gốc, sẽ được sử dụng để deploy trong quá trình release hệ thống thật) gọi là "origin" (remote origin) → thuật ngữ rất phổ biến với git users.
Mỗi developer sẽ push và pull lên remote origin → centralized, tuy nhiên ta vẫn có thể tổ chức những nhóm riêng (sub team) sử dụng remote riêng để phục vụ cho quá trình phát triển.
Ví dụ giống như hình trên, mỗi khi phát triển 1 feature lớn, cần có 1 team gồm nhiều thành viên để phát triển thì ta sẽ tiến hành tổ chức các remote khác nhau để hỗ trợ. Các developer sẽ push lên các remote của sub team trước khi push lên remote origin, trong hình trên ta thấy được subteam được tổ chức giữa Alice và Bob, Alice và David, Clair và David. => Alice đơn giản chỉ là khai báo 1 remote, ví dụ tên bob, trỏ vào local repo của Bob.
The main branches (Những branch chính)
Main banrches là những branch sẽ tồn tại mãi mãi, gồm có 2 branch sau: master develop 2 branch này được phát triển vả tồn tại song song với nhau. Và chú ý là source code HEAD của branch origin/master chính là phiên bản sẵn sàng để phát hành ra thực tế. Còn đối với origin/develop là branch mà source code HEAD của nó là phiên bản phát hành cuối cùng để sẵn sàng cho lần release tiếp theo. Ngoài ra nó còn được gọi là "integration branch".
Khi source code trong branch develop đã đạt được mục tiêu của lần release tiếp theo thì code của nó sẽ được merge trở lại vào master, được tag với 1 dãy số release (release version, ví dụ v1.0.1). Về release version ta sẽ đề cập ở phần cuối của bài này.
Mỗi lần ta merge vào master có nghĩa là có 1 sản phẩm mới, 1 phiên bản mới sẽ được phát hành, và sẽ phải rất cẩn thận rong quá trình release này, ta có thể sử dụng 1 đoạn git hook script để tự động build và roll-out source code thành phiên bản thực tế mỗi lần ta commit lên master.
Supporting branches (những branch hỗ trợ)
Sau main branch, developer còn sử dụng rất nhiều những supporting branches để tiến hành phát triển song song giữa các thành viên trong team, dễ dàng theo dõi các features, chuẩn bị cho quá trính release cũng nhưng để fix những bug của sản phẩm. Không giống như main branch, những suporting branch luôn có thời gian tồn tại nhất định. Supporting branches gồm những loại sau:
- Feature branches
- Release branches
- Hotfix branches
Mỗi 1 branch trên sẽ có 1 mục đích sử dụng cụ thể riêng và sẽ có những quy định sử dụng cụ thể đi kèm về: được checkout (branch off) từ branch nào và sẽ được merge vào branch nào. Chúng ta sẽ đi từng branch cụ thể đôi chút.
Hệ thống branch
Feature branches
- branch off từ: develop
- merge vào: develop
- Quy luật đặt tên: bất kể gì trừ master, develop, release-, hay hotfix-
Feature branches(hay topic branches) được sử dụng để phát triển 1 tính năng mới cho sản phẩm và sẽ phát hành trong tương lai. Khi bắt đầu phát triển 1 tính năng, có thể ta không biết được là nó sẽ được phát hành cùng với phiên bản release nào. Bản chất của feature branch là sẽ tồn tại như 1 tính năng trong quá trình phát triển, nhưng sau cùng nó sẽ cũng được merger vào develop hoặc là bị hủy đi, không sử dụng nữa. Feature branches thông thường chỉ tồn tại trên repos của developer, không tồn tại trên origin.
Tạo feature branch:
$ git checkout -b myfeature develop
Switched to a new branch "myfeature"
Kết thúc phát triển sẽ được merge lại vào develop và sẽ được add thêm trong lần release tiếp theo:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop
–no-ff
flag có nghĩa là việc merge sẽ luôn luôn tạo ra 1 đối thượng commit, kể cả nếu việc merge có thể được thực thi với fast-forward. Nó chống lại việc mất thông tin về quá trình phát triển của feature và nhóm lại với nhau, hình sau sẽ giúp ta dễ hình dung hơn:
Với việc sử dụng flag –no-ff
thì những log về commit trong branch cũ vẫn được lưu giữ lại việc này giúp cho chùng ta lần lại được log của tất cả những commit trong quá khứ, xem được những thay đổi trong mỗi lần commit đấy. Sử dụng flag này tránh được rắc rối khi chúng ta kiểm tra lại sự thay đổi của project trong quá trình phát triển, khi mà lúc merge xong thì mọi commit trong branch cũ được kết hợp lại làm một.
Tuy nhiên chưa có cách config nào để biến flag này thành mặc định, nhưng nó sẽ có khả năng có trong tương lai.
Release branches
- Được branch off từ:develop
- Được merge vào develop và master.
- Quy tắc đặt tên: release-*
Release branch hỗ trợ chuẩn bị cho 1 release mới của product. Chúng cho phép đứt quãng quá trình và bỏ qua nó. Hơn nữa, nó cũng cho phép tiến hành hạy song song fix bug và chuẩn bị meta-data cho release (version number, build dates,...).
Khi tạo branch release từ develop thì branch develop lúc đó phải thể hiện rõ (mang đủ những tính năng) của product trong lần release mới này. Tối thiểu tất cả những features của lần release mới này phải được merge vào develop rồi.
Chính xác thì khi strat 1 release branch thì nó sẽ được gán 1 version mới (sự thay đổi version release, ko phải fix bug, → sẽ nói kỹ càng hơn ở phần cuối của mục "sử dụng trong thực tế"). Nó được gọi là "project’s rules on version number bumping" → quy tắc đặt tên version.
Tạo release branch: được tạo từ branch develop. Ví dụ phiên bản hiện tại của product là 1.1.5 và chúng ta đang tiến hành release 1 phiên bản mới. Lúc này phiên bản release mới sẽ là 1.2.0 thay vì 1.1.6 hay 2.0. Ví dụ:
$ git checkout -b release-1.2 develop
→ tạo mới và chuyển sang "release-1.2"
$ ./bump-version.sh 1.2
→ bump (nâng version) new version (config thông tin của product thành version mới)
$ git commit -a -m "Bumped version number to 1.2"
→commit state mới
Sau khi tạo mới release branch và chuyển qua nó, chúng ta tiến hành bump số version của product. bump-version.sh là 1 đoạn shell script mà nó sẽ thay đổi nội dung 1 số file để khớp với số version release mới. Rồi bumped số version mới được commit lên.
Có thể tồn tại những branch khác trong quá trình branch release được tiến hành, cho đến khi sự release co thể bị xoay ngược lại. Trong khoảng thời gian này, việc fix bug có thể được apply vào branch này (nhanh hơn so với develop). Việc thêm những tính năng lớn ở giai đoạn này bị cấm tuyệt đối. Chúng sẽ được merge vào develop và chờ cho tới lần release tiếp theo.
Hoàn thành release branch Khi trạng thái của branch release đã sẵn sàng để thành 1 release thực sự, cần phải cẩn thận một số hành động tại thời điểm này. Đầu tiên, branch release phải được merge vào master (nhớ là khi mọi commit trên master là đã được quy định). Tiếp theo, những commit trên master phải được tag để trong tương lai ta có thể ánh xạ lại được những version cũ của product trong quá khứ. Và cuối cùng là những thay đổi trên release branch phải được merge ngược trở lại vào develop để đảm bảo tương thích (những bug đã được fix hay những tính năng nhỏ khác được tiến hành trên branch release) cho lần release tiếp theo.
2 bước đầu tiên trên git
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
$ git tag -a 1.2
Relese done, và được tag cho lần release này.
Và như nói ở trên, ta phải merge ngược lại vào develop. In Git:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
Bước này có thể bị confict, ta phải fix chúng rồi commit lên develop.
Tiếp theo chúng ta xoá release branch đi nếu như chúng ta không còn dùng đến nó nữa:
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).
Hotfix Branches
- Branch off from: master
- được merge vào: develop and master
- Quy tắc đặt tên: hotfix-*
Branch hotfix có điểm rất giống branch release là nó có ý nghĩa sử dụng trong quá trình chuẩn bị cho lần release mới của product. Branch này xuất hiện khi có 1 thay đổi, sửa lỗi cần thiết trên phiên bản thực tế của product (phiên bản đang được sử dụng trong thực tế) và nó hoàn toàn nằm ngoài thiết kế. Khi có 1 bug nghiêm trọng trong phiên bản thực tế của product thì branch hotfix được branch off từ master và là version của product cũng được nâng lên tại thời điểm này (Ví dụ 1.1.2 → 1.1.3).
Lúc này thì công việc của team vẫn được tiến hành bình thường trên branch develop, sẽ có 1 vài người được chỉ định để fix bug nhanh chóng trên product.
Branch hotfix được tạo từ master. Ví dụ 1.2 là phiên bản hiện tại của product trong thực tế và xuất hiện 1 bug nghiêm trọng cần thiết phải sửa chữa ngay lập tức thì công việc sẽ được hình dung cụ thể như sau:
$ git checkout -b hotfix-1.2.1 master
Tạo mới và chuyển sang branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
version bumped to 1.2.1.
$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
1 files changed, 1 insertions(+), 1 deletions(-)
Đừng bao giờ quên bump the version number sau khi branching off!
Và tiếp theo, tiến hành sửa lỗi và commit lên
$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)
Finishing a hotfix branch
Khi kết thúc sửa lỗi, branch hotfix cần phải merge ngược lại vào cả master và develop.
Đầu tiên, update master và tag phiên bản release.
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2.1
Và merge vào develop
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
Có một ngoại lệ đặc biệt ở đây là khi mà branch release đang tồn tại thì branch hotfix sẽ được merge vào branch release thay vì develop. Vì sau này khi merge branch release và branch develop thì những gì của branch hotfix vẫn sẽ được ốp vào develop.
Bước cuối cùng là xoá branch hotfix:
$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).
Release number
- Phase Number: phụ thuộc kế hoạch phát triển của product (phase number là level lớn nhất)
- Release Number: sau phase number là release number, trong mỗi phase sẽ được chia ra từng giai đoạn release, chính là những quá trình thêm các tính năng lớn vào product. Tương ứng với branch release.
- Fixbug number: mỗi lần fix bug trên master thì lại tăng số này lên.