Hệ thống quản lý mã nguồn Git – phần 3: bản chất

Trong phần 3 này, chúng ta sẽ đi sâu vào giải thích cơ chế hoạt động của Git, cách nó báo cho chúng ta biết các thay đổi của code đồng thời giải thích sự khác nhau cơ bản của git mergegit rebase. Ngoài ra cũng sẽ có một số mẹo nhỏ đi kèm ;).

Các nguyên lý hoạt động cơ bản

Cấu trúc dữ liệu của Git là một kho lưu trữ (repository) các đối tượng khoá-giá trị (key-value object) nơi mà các đối tượng được đánh dấu (indexed) bảo mật. Tất cả các dấu mốc trạng thái (commits), tập tin (files), các file hệ thống đều được lưu dưới các dạng các đối tượng khác nhau trang kho lưu trữ này.

Các dấu mốc trạng thái (commits) thật ra là các “phiên bản” code được lưu lại của dự án. Mỗi commit không đơn thuần chỉ lưu trữ sự thay đổi như cảm giác mà ta có khi thực hiện câu lệnhgit status hay khi xem cây commit (commit tree) của dự án. Tất cả các code change được hiện thỉ bằng cách so sánh thời gian thực khi chúng ta gọi các câu lệnh status hay diff. Tuy nhiên, Git cố gắng tối ưu cách lưu trữ để giảm thiểu tối đa sự dư thừa dữ liệu khi lưu rất nhiều phiên bản code trong một project bằng cách chỉ lưu mã nhận dạng của các file không thay đổi thay vì tạo hẳn một file mới.

Làm thế nào Git phát hiện ra sự thay đổi trong code

Git dùng sự so sánh từng dòng giữa 2 phiên bản để phát hiện những thay đổi trong code. Trường hợp có file mới hay cũ thì nó không nằm trong object tree của repository nên rất dễ để nhận định. Còn trường hợp chúng ta edit file cũ thì khi chạy git status hay git diff, Git duyệt qua tất cả các file để tìm ra những dòng có sự thay đổi so với nhau.

Cách so sánh này nhìn chung khá thủ công và là một trong những điểm yếu của Git. Đôi khi trong lúc chúng ta merge hay rebase, rất dễ có những trường hợp chỉ thêm một dòng trống hay cùng thêm 2 hàm khác nhau trong cùng một file lại có sự xung đột code (conflict) mà Git nhận ra và bắt chúng ta phải giải quyết (sửa file rồi add vào danh sách file sẵn sàng để commit)

tình trạng ban đầu

Merge

Đây là cách đơn giản nhất để tích hợp code của 2 nhánh khác nhau.

Nguyên lý:

Git dùng 2 phiên bản code tại commit cuối cùng của từng nhánh rồi tích hợp trực thẳng vào nhau. Tất cả các thay đổi giữa 2 nhánh sẽ được add vào 1 commit dưới dạng merge branch feature_abc with stable_branch ....

Các commit của 2 nhánh sẽ được sắp xếp dựa theo thời gian tạo commit.

merge flow

Rebase

Đây là cách tích hợp code giúp cho việc review code trở nên dễ dàng hơn, sẽ không có những commit có sự thay đổi lớn merge branch feature_abc with stable_branch ... do câu lệnh merge gây ra nhằm chỉnh sửa conflict hay sử dụng lại (reuse) các method, bugfix từ stable_branch.

Nguyên lý:

Lấy code mới nhất từ stable_branch, áp từng phiên bản commit từ nhánh feature_abclên bộ code mới đó và tạo commit mới tương ứng cùng tên sau khi đã giải quyết các conflict (nếu có).

Các commit của nhánh hiện tại được tạo mới sẽ nằm liền mạch sau commit cuối cùng của stable_branch

Khi nào nên dùng merge/rebase

Từ 2 khái niệm trên, mỗi cách đều có ưu nhược điểm riêng nên để trả lời câu hỏi này, chúng ta sẽ dựa vào đặc điểm của dự án hay cụ thể hơn là tình trạng hiện tại của dự án, của tính năng:

Nên dùng merge khi:

  • Giữa 2 nhánh có conflict ngay từ những commit đầu, nếu rebase sẽ phải chỉnh conflict cho từng commit tại branch hiện tại. Merge trong trường hợp này giúp giảm công sức chỉnh conflict lập đi lập lại
  • Không có nhiều thời gian để review và chỉnh conflict cho từng commit khi muốn tích hợp 2 nhánh. Merge giúp việc tích hợp code diễn ra nhanh chóng

Nên dùng rebase khi:

  • Muốn tạo commit tree đẹp, sạch sẽ, tiện cho việc quản lí code và tránh lỗi merge
  • Giữa 2 nhánh không có nhiều conflict từ những commit đầu, nếu không sẽ rất tốn công sức và thời gian sửa conflict một cách lập đi lập lại
  • Có thời gian để review, chỉnh conflict cho từng commit

Quyết định dùng merge hay rebase còn tuỳ thuộc khá nhiều vào thời gian cho phép và chất lượng code giữa 2 nhánh.

Các thủ thuật thực tế liên quan

  • Tránh mất code từ git merge:

Một trong những lưu ý quan trọng nhất khi dùng merge đó là nó có thể gây ra sự thay đổi code không mong muốn, ảnh hưởng đến sự toàn vẹn của code.

Tình huống: nhánh update_product_view dùng 1 helper sẵn có tên show_company_name, trong khi nhánh clean_up_helper_and_refactor lại delete đi helper đó vì hiện tại nó không đang được dùng trong toàn dự án.

Như vậy, khi đứng tại nhánh clean_up_helper_and_refactor và merge với update_product_view, helper show_company_name sẽ tự động bị xoá gây ảnh hưởng với những phần cần sử dụng nó. Trong những tình huống như vậy Git không nhận định được đó là một tình huống conflict cần phải giải quyết và tự động thực hiện việc merge tự động (fast-forward merge).

Để tránh sự cố này, với những trường hợp quan trọng, tiềm ẩn nhiều lỗi (bug), chúng ta nên dùng option --no-ff khi merge

  • Khi xảy ra conflict và muốn đơn giản giữ lại tất cả code change của nhánh hiện tại (của mình) hoặc giữ lại tất cả code change của nhánh được merge (của người ta):

Giữ lại của mình:

Giữ lại của người ta:

Kết luận

Cơ chế làm việc của Git nhìn chung khá đơn giản nhưng hiệu suất tốt và đáp ứng đủ nhu cầu của các lập trình viên. Với cách so sánh code theo từng dòng, chúng ta có khuôn mẫu sử dụng (best practise) là chỉ nên lưu giữ trong Git repository các file code, tránh các file thiên về lưu trữ như file word, csv, … mà nên cấu hình để lưu chúng ở bên dịch vụ thứ 3 hay ở bộ lưu trữ trực tuyến.

Với những tiện ích của mình, Git đang là hệ thống quản lí mã nguồn mở thông dụng nhất mặc cho vẫn còn đó một vài điểm yếu. Hy vọng bài viết giúp mọi người hiểu hơn về cơ chế hoạt động của git và có thể sử dụng Git tốt hơn trong công việc.

Mong nhận được nhiều góp ý, mọi đóng góp đều được hoan nghênh và ghi nhận. :)

Một số nguồn tham khảo hay:

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s