Cách làm việc với git một cách hiệu quả
Blog này miêu tả quy trình làm việc với git một cách có hiệu quả và chuyên nghiệp áp dụng cho mọi project từ bé đến lớn, team size từ 1 cho tới n. Nó được tao ra với mục đích ban đầu là 1 wiki cho engineers của SotaTek, nhưng nếu bạn chưa dùng git bao giờ hoặc dùng nhiều mà thấy nó loạn quá, khó kiểm soát thì cũng có thể thử follow theo xem hiệu quả đến đâu.
Blog này inspired từ model này, có customize một số điểm mà mình thấy không phù hợp: http://nvie.com/posts/a-successful-git-branching-model/
1. Tại sao dùng git
– Blog này sẽ không bàn nhiều về vấn đề này nữa, vì đơn giản là nó tốt đến mức bây giờ mọi công ty từ lớn đến nhỏ; dù bạn có là team hay single developer thì vẫn phải dính dáng đến nó vì đa số các lib open source đều dùng git. Cho nên bạn không muốn thì vẫn phải dùng và bị dùng.
2 Git client:
– Để làm việc với git thì bạn cần 1 git client, nếu bạn thích có giao diện GUI thì có thể dùng https://www.sourcetreeapp.com/ ưu điểm là rất dễ nhìn, rất thuận tiện cho việc review các thay đổi trước khi commit. Nhược điểm là chậm vì phải index mỗi lần mở app lên. Chưa có phiên bản SourceTree cho Linux, bạn muốn có thể tự đi tìm app tương tự như SmartGit hoặc gì đó.
– Dùng command line, cái này thì ưu điểm là rất nhanh và mạnh, có thể giải quyết được tất cả các vấn đề của git mà đôi khi dùng GUI không thể làm được. Nhất là khi làm việc trên môi trường server không có giao diện GUI thì đây là lựa chọn duy nhất. Built in trong MacOSX rồi, linux thì tuỳ bản distribution mà bạn sẽ phải cài hoặc không, Windows bạn sẽ phải tự cài.
– Tuỳ sở thích cá nhân mà bạn tự chọn git client cho mình nhưng best practice của người viết bài này là dùng kết hợp cả 2, SourceTree để review commit, dùng command line cho toàn bộ những việc còn lại. Toàn bộ bài viết sau đây sẽ chỉ dùng command line.
3. SSH key
– Nếu bạn biết SSH key là gì rồi thì có thể bỏ qua phần này.
– Nó là 1 cách để authenticate account git của bạn với các git hosting website như github.com, bitbucket.org… mỗi khi bạn push, pull mà không phải nhập lại username/password.
– Nếu chưa biết thì bạn sẽ clone 1 git url dạng https://path/to/repo nhưng nếu dùng SSH nó sẽ là dạng git@path/to/rep.git
– Cách tạo SSH key, bạn có thể dùng lệnh dưới đây hoặc tham khảo https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/:
$ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
– Sau khi có ssh key, bạn hãy copy nội dung của file public key thông thường locate ở ~/.ssh/id_rsa.pub add lên account của bạn trên github hoặc bitbucket…
4. Branching model
Đây mới là phần chính mà blog này muốn đề cập tới. Thông thường khi tạo 1 git repo bất kỳ đã có 1 nhánh (branch) mặc định là master, với các bạn thiếu kinh nghiệm thì dù team có bao nhiêu người cũng đều làm việc chung ở 1 branch này và commit conflict loạn cả lên. Hoặc tốt hơn 1 chút là phân ra các nhánh khác nhau cho từng bạn nhưng không có 1 convention hoặc model nào cụ thể cho việc merge các thay đổi giữa các branch lại với nhau. Để giải quyết vấn đề này, ta sẽ cùng nhau định nghĩa ra 1 model như thế.
Chúng ta sẽ định nghĩa ra các branch sau trong một project bất kỳ:
– master
– develop
– feature branches
– hotfix branches
4.1. Branch chính
Trong bất kỳ git repo nào cũng sẽ tồn tại song song 2 branch sau đây, 2 branch này sẽ tồn tại trong suốt dòng đời của của dự án của bạn.
* master
* develop
– Branch master: là branch cho phiên bản code stable nhất, sẵn sàng để deploy lên môi trường production, để build app lên store… nói chung là bản code ổn định nhất đã qua hết các giai đoạn testing.
– Branch develop: là branch cho phiên bản code đang trong giai đoạn development, nó bao gồm tất cả các tính năng, bug fix,… mà sẽ được release trong version tiếp theo của project. Khi branch này đã đủ stable và có quyết định release thì nó sẽ được merge vào branch master.
4.2 Branch features
– Mỗi branch sẽ tương đương với 1 feature (tính năng) trong project. Nó có thể là 1 tính năng mới mà chúng ta muốn thêm vào trong sản phẩm của mình so với phiên bản hiện tại. Hoặc cũng có thể là improvement, bug fix cho 1 tính năng nào đó…
– Nhìn chung mỗi khi định nghĩa ra 1 cái gì đó muốn làm chúng ta sẽ tạo ra 1 feature branch
– Cách đặt tên: feature/a_feature
– Branch từ develop và phải merge vào develop sau khi hoàn thành, các bước cụ thể như sau:
#get the latest code from develop
$ git checkout develop
$ git pull
#create a feature branch from there
$ git checkout -b feature/my_awesome_feature
$ git push
– Sau khi đã hoàn thành việc implementation cũng như pass qua quá trình testing, chúng ta sẽ tạo 1 pull request (bản so sánh các thay đổi giữa 2 branch bất kỳ) giữa branch feature và develop để tiến hành code review. Nói đơn giản là một người có kinh nghiệm sẽ review code của bạn trong feature branch trước khi merge vào develop để chắc chắn rằng bạn không gây ra lỗi nào ảnh hưởng đến sản phẩm.
– Sau khi review xong, chúng ta có thể merge vào develop như sau:
#make sure the branch name is correct
$ git branch
#make sure the branch is up to date because many people contribute to a single feature
$ git pull
#switch to develop branch
$ git checkout develop
#make sure develop is up to date again
$ git pull
#do the merging
$ git merge feature/my_awesome_feature
$ git push
– Trong tài liệu “a successful git branching model” họ khuyến khích nên sử dụng option –no-ff để track lại history của việc merge nhưng mình cũng thấy không cần thiết lắm.
– Ngoài ra nếu bạn muốn git log nó đẹp (linear) thì có thể tìm hiểu về git rebase, tuy nhiên quan điểm cá nhân của mình nó cũng không cần thiêt vì phải resolve conflict 2 lần nên khá risky đặc biệt những thời điểm cận kề release. Ví dụ lần resolve sau lập trình viên không để ý mà resolve khác với lần trước đó mà không check diff lại thì coi như đi tong công test.
4.3 Hotfix branches
– Sản phẩm sau khi release vào 1 ngày nắng đẹp nào đó bỗng nhiên phát hiện ra 1 bug nghiêm trọng và cần phải fix ngay (hotfix), vậy chúng ta sẽ tạo branch như thế nào?
– Branch ra từ master vì develop vào thời điểm đó có thể đang chứa những feature mà mình chưa muốn release ngay.
– Cách dặt tên nhánh (naming convention): hotfix/an_urgent_hotfix
– Cách làm tương tự với feature branch, chỉ khác là branch ra và merge vào master chứ không phải develop.
$ git checkout master
$ git pull
$ git checkout -b hotfix/an_urgent_hotfix
#merge
$ git checkout hotfix/an_urgent_hotfix
$ git pull
$ git checkout master
$ git pull
$ git merge hotfix/an_urgent_hotfix
$ git push
– Sau khi đã merge hotfix vào master, chúng ta cũng đồng thời phải merge ngược lại master vào develop
$ git checkout develop
$ git pull
$ git merge master
$ git push
5. Release
Sau khi hoàn tất quá trình phát triển các feature và merge hết vào develop, chúng ta sẽ thường làm các bước sau:
– Tạo 1 release note trên nhánh develop miêu tả các thay đổi của phiên bản này so với phiên bản trước đó.
– Merge develop vào master phần này tương tự như trên nên sẽ không đề cập nữa.
– Tạo git tag (một mốc đánh dấu release version để các phiên bản sau nhỡ có gì đó không ổn chúng ta chỉ cần đơn giản là revert lại tag trước đó) như sau:
$ git checkout master
$ git pull
$ git merge develop
$ git push
$ git tag -a 1.2 -m "v1.2 release"
$ git push --tags
6. Nếu nhiều feature được develop cùng lúc, làm cách nào để có thể test được vì mỗi feature có 1 branch riêng lẻ
– Đây là câu hỏi rất hay gặp nhưng chúng ta có 1 số rule như sau:
– Không bao giờ merge 1 feature branch này vào 1 feature branch khác vì đơn giản nó cả 2 feature branch đó đều chưa stable, chưa qua testing (nó chỉ đáp ứng đủ điều kiện này sau khi đã merge vào develop).
– Nếu chúng ta cố tình merge 1 branch feature này vào 1 branch feature khác nhỡ đâu 1 ngày giám đốc quyết định cắt 1 feature đi, vậy thì làm cách nào có thể vứt tất cả những commit liên quan đến feature đó ra khỏi feature của mình? bằng tay?
– Cách duy nhất: Những feature nào đã ok rồi thì sẽ merge vào develop, sau đó chúng ta sẽ thường xuyên merge từ develop về nhánh của mình trong suốt quá trình phát triển.
– Nếu chúng ta vẫn muốn test các nhánh chưa stable thì chúng ta có thể tạo 1 nhánh trung gian là hợp của cả 2 hoặc nhiều nhánh feature vào. Nhánh này chỉ phục vụ duy nhất 1 mục đích là test nên merge chỉ có 1 chiều, khi test xong là coi như vứt branch đó đi. Còn lại những feature vẫn được phát triển riêng biệt.
7. Một số case study cơ bản hay gặp:
7.1. Nếu bạn đang sửa gì đó trên branch của mình mà khi pull code mới nhất về hoặc checkout sang branch khác bị git báo là conflict:
– Đừng sợ
– Nếu như các thay đổi của bạn là ok và không có lý do gì để bạn không commit những thay đổi đó lên thì bạn chỉ cần: commit các thay đổi rồi muốn pull hay checkout sang branch nào thì checkout.
– Nếu như bạn đang dang dở và chưa thể commit các thay đổi đó được, thì hãy sử dụng git stash — nó sinh ra nhằm mục đích cho bạn vứt hết các thay đổi vào 1 cái stack để sau này bạn pop lại các thay đổi đó ra khi cần như sau:
# stash the changes
$ git stash
#continue with the
$ git pull
# apply the changes again
$ git stash pop
Khi apply những thay đổi từ stash pop, rất có thể những thay đổi đó sẽ bị conflict với code mới nhất mà bạn vừa pull về, điều này là bình thường và hợp logic. Lúc này bạn cần resolve tất cả conflict đó và làm việc tiếp.
7.2. Sau khi bạn commit xong, push lên thì bị báo là không push được do quên chưa pull trước khi commit
– Đừng sợ
– Bạn phải pull về và merge vào code của mình rồi mới push được
– Nên dùng lệnh sau để cho không tạo thêm merge commit.
$ git pull --rebase
– Bản chất của việc rebase là merge và đặt commit của bạn lên trên top của nhánh. Dù pull thường hay pull –rebase vẫn có thể xảy ra conflict, mỗi lần như vậy thì lại resolve và add, commit phần resolve conflict đó lên như thường.
Nếu các bạn có đủ kiên nhẫn để đọc đến đây thì mình xin cảm ơn và hy vọng các bạn đã có 1 cách nhìn khác về cách mà “người ta” vẫn hay làm việc.