Thực ra bài này cũng không hẳn là phổ cập kiến thức về git mà chỉ là 1 bài chia sẻ, cũng như tâm sự trong dự án làm ở ngân hàng M xanh thôi.
Như các bạn cũng biết một project phát triển thì luôn tồn tại 3 nhánh chính:
- master (hoặc có thể đặt là prod)
- pilot
- dev
Giải thích chức năng của từng nhánh:
- Master
Nơi chứa code mà đẩy lên người dùng thực tế và các cấu hình đang chạy để phục vụ người dùng như database, kafka, redis
- Pilot
Khác mỗi là master là code có thể được thử nghiệm trên dữ liệu của người dùng thật. Nghĩa là tính năng chưa hoàn thiện test xong mà đang thử nghiệm trên dữ liệu database trên live xem có lỗi lầm gì không thôi?
- Dev
Một mớ hỗn độn của bao nhiêu ông dev tống hết vào đây )) Và đương nhiên, dữ liệu và truy cập database riêng rồi. Fake hết.
Quy trình git khi phát triển
1. Nhánh phát triển bắt buộc phải checkout từ nhánh master
git checkout master -> git pull git checkout -b feature/abc
Khi checkout xong. Việc phát triển trên nhánh feature/abc này cần phải lấy các cấu hình từ dev. Thường nằm trong file resource.
Ví dụ: application.properties, conf, log4j2.xml, ...
Khi đã code và test thông luồng được, thì cherry-pick qua nhánh Dev phục vụ cho mọi người cùng test trên môi trường Dev thôi. Nếu nhiều commit cùng 1 tính năng thì có thể squash lại trước khi cherry-pick qua.
Cái này không có gì khó khăn cả. Khi code qua Dev thì push lên sẽ có CI/CD hỗ trợ cho mình việc đẩy lên K8s rồi
Để thực thi DevCi thì dự án tôi dùng Jenkins để tạo các pipeline
Clone repository -> Maven build -> Build image -> Push image -> ReDeploy to K8s
Có thể lên kiểm tra Kubernetes thì thông qua Rancher
Cái này thuộc về phạm trù khác rồi. Tuy nhiên mình nhắc tới ở đây để phục vụ việc xem log trên môi trường Dev, và cấu hình ConfigMap (application trên K8s). Nếu không đọc được file cấu hình này thì Spring Boot sẽ đọc application trong /resource.
Cái này còn tùy thuộc đội CI/CD cấu hình.
2. Quy trình merge code
Khi test vào để SIT (System Integration Testing) xong rồi còn pentest nữa (test bảo mật). Qua 77 49 bước viết tài liệu thì đẩy code lên pilot thôi :v.
Đẩy code Pilot
Tạo 1 nhánh từ pilot: gọi là pilot_merge/abc
Lúc này có thể tạo 1 nhánh mới từ feature/abc (nhánh phát triển) để squash tất cả các commit hoặc squash trên nhánh phát triển thành 1 commit.
Khi này chỉ cần cherry-pick commit sau squash này sang pilot_merge/abc và resolve các conflic.
Rồi chạy thử xem nó có bị lỗi gì không?, Sau khi kiểm tra tất rồi thì tạo merge request trên gitlab và gửi cho Dev lead review thôi.
Vẫn phải xem lại nếu thay đổi trong application thì phải cấu hình trên ConfigMap trên pilot nhé. (Mỗi môi trường có 1 application riêng)
Đẩy lên master
Cái này thì dễ hơn nhưng cũng cẩn thận vì đây là môi trường người dùng thật đang sử dụng.
Nhánh feature/abc trước khi tạo request merge vào master trên gitlab. Bạn cần pull lại master và rebase trước nhé.
Vấn đề xảy ra sau đó
Thật ra là chẳng có vấn đề gì cả Nếu như mình không đề xuất 1 phương án khác dễ làm hơn.
Mình đặt ra một giải pháp là ta có thể merge thẳng nhánh phát triển vào 1 nhánh tách ra từ pilot mà nhỉ? (Nhanh chóng tiện lợi, đỡ phải squash tất cả commit rồi lại phải cherry-pick sang nhánh tạo merge vào pilot). Đỡ tốn thời gian. còn giảm bớt được sai sót khi merge code.
Khi mà các hot fix nhanh thì code chỉ có trên nhánh master mà pilot lại không có. Càng ngày càng nhiều thì việc nhánh master càng xa nhánh pilot. Mà theo tư tưởng nhánh pilot phải sát or gần nhất với master để quá trình đẩy test được suôn sẻ phải không nào.
OK. Vậy coi như giải phát của mình được chấp thuận.
Lúc này chỉ có nhờ các Dev lead tác động vào nhánh pilot mà thôi. Vì chuyên viên, OS thì làm gì có quyền (
git checkout pilot
git pull origin pilot
git merge master
Khi bị conflic giữ lại tất cả các code có trên nhánh pilot. Thế là ta đã hoàn thành việc giải lập cho Git hiểu là master và pilot đã chung gốc.
Và từ đó, quy trình phát triển với git cực kỳ đơn giản.
- master -> feature/abc
- feature/abc cherry-pick sang dev
Khi cần đẩy code:
- pilot -> pilot_merge/abc rồi merge feature/abc vào là xong. Tạo merge request trên gitlab
- master thì tạo thẳng merge request từ nhánh feature/abc.
Tuy kết quả thì dễ dàng cho việc phát triển nhưng nó sẽ nảy sinh ra 2 vấn đề mình thấy đó là:
- cây commit trên pilot lúc này nó sẽ xấu hơn. =)) Nhưng cái này cũng chẳng sao. Làm việc hiệu quả chứ xấu thì chỉ xấu 1 lần này thôi.
- 3-WAY MERGE
Vậy 3-WAY MERGE là gì?
Nếu sử dụng IntelliJ thì chẳng sao. Mình có thể hoàn toàn kiểm soát được code mình chuẩn bị push lên. Vì khi đó IDE cũng sẽ diff nhánh local và trên origin mà thôi.
GIẢ ĐỊNH CỤ THỂ VỀ 3-WAY MERGE:
Giả sử ta có:
- O: gốc chung giữa master và pilot
- A (pilot): bạn muốn merge vào
- B (master): nhánh bạn merge từ
Khi nào Git KHÔNG báo conflict nhưng code từ master vẫn chui vào pilot?
- Trường hợp 1: pilot KHÔNG thay đổi gì, master thay đổi → Git tự động lấy code từ master
// O (gốc chung)
function sayHello() { console.log("Hi");
} // master (B): sửa lại
function sayHello() { console.log("Hello from master");
} // pilot (A): không đụng gì
function sayHello() { console.log("Hi");
}
Git sẽ không báo conflict → nó tự động lấy code từ master → code pilot bị thay đổi mà bạn không thấy warning nào.
- Trường hợp 2: Cả 2 bên thay đổi khác nhau, nhưng ở 2 vùng khác nhau trong file
// O
function sayHello() { console.log("Hi"); console.log("Welcome");
} // master (B)
function sayHello() { console.log("Hello from master"); console.log("Welcome");
} // pilot (A)
function sayHello() { console.log("Hi"); console.log("Welcome to pilot");
}
Cả 2 đều thay đổi sayHello(), nhưng khác dòng → Git tự động merge cả 2, không báo conflict → code cuối cùng có thể là "trộn" giữa master và pilot.
- Trường hợp 3: master thêm dòng mới ở nơi pilot không thay đổi
// O
function calculate() { return 10;
} // master
function calculate() { let x = 5; return x * 2;
} // pilot (không đụng gì)
function calculate() { return 10;
}
Git không báo gì và có thể áp dụng thay đổi từ master vào pilot, vì nó thấy không có mâu thuẫn.
Hậu quả:
- Những thay đổi này sẽ tồn tại trong pilot mà không ai để ý, nếu bạn không kiểm tra kỹ git diff sau merge.
- Không có conflict ≠ không có rủi ro. Nếu bạn resolve theo kiểu giữ tất cả (dùng --theirs chẳng hạn), bạn có thể vô tình lấy code từ master.
Các bạn nếu có cách làm hay thì có thể comment ở dưới để mọi người cùng tham khảo nhé. Bài có thể sai sót nên mong nhận được góp ý từ ae.