28 điểm bởi GN⁺ 2023-11-24 | 1 bình luận | Chia sẻ qua WhatsApp
  • Nhiều người cho rằng cách hoạt động của Git branch không mang tính trực quan.
  • Bài viết giải thích sự khác biệt giữa mô hình trực quan phổ biến về Git branch và cách branch thực sự được biểu diễn bên trong Git.
  • Bài viết cho thấy mô hình trực quan và cách Git thực sự hoạt động trên thực tế có liên hệ rất chặt chẽ với nhau.
  • Thảo luận về những giới hạn của mô hình trực quan và lý do nó có thể gây ra vấn đề.

Mô hình branch trực quan

  • Nhiều người hình dung branch như 'cành của cây táo'.
  • Trong Git, branch không có khái niệm 'cha', nên khác với cách nghĩ rằng nó được tách ra từ main.

Trong Git, branch là toàn bộ lịch sử

  • Trong Git, branch không chỉ là các commit được tách nhánh mà bao gồm toàn bộ lịch sử của mọi commit trước đó.
  • Thông qua một repository ví dụ, bài viết cho thấy cả mainmybranch đều có 4 commit.

Branch được lưu bằng ID commit

  • Bên trong Git, branch được lưu dưới dạng một tệp văn bản nhỏ chứa ID commit.
  • Commit mới nhất của mỗi branch được ghi trong tệp đó.
  • Vì không có quan hệ cha-con giữa các branch, Git không biết mối quan hệ giữa các branch.

Trực giác của mọi người thường không hẳn là sai

  • Nói rằng trực giác của mọi người về Git là 'sai' thì hơi ngớ ngẩn.
  • Ngay cả một mô hình 'sai' trên thực tế vẫn có thể hữu ích.

Rebase cũng dùng khái niệm branch 'trực quan'

  • Rebase chỉ áp dụng lại lên main các commit thuộc branch 'trực quan'.
  • Kết quả của rebase phù hợp với mô hình trực quan.

Merge cũng dùng khái niệm branch 'trực quan'

  • Merge không sao chép commit, nhưng cần một commit nền tảng dùng chung.
  • Merge base tìm ra commit nơi branch được tách ra dựa trên mô hình trực quan.

Pull request trên GitHub cũng dùng ý tưởng trực quan

  • Khi tạo một pull request để merge mybranch vào main trên GitHub, chỉ các commit thuộc branch trực quan mới được hiển thị.

Trực giác là tốt nhưng có giới hạn

  • Định nghĩa branch theo trực giác khá khớp với công việc thực tế trong Git, nhưng Git không thể tự nhận biết branch nào được tách ra từ main.

Trunk và branch được tách ra

  • Mọi người nhận thức mainmybranch theo cách khác nhau, và điều này ảnh hưởng đến cách họ dùng Git.
  • Git không phân biệt được một branch có phải là 'nhánh tách ra' từ branch khác hay không.

Git có thể rebase theo 'chiều ngược lại'

  • Vì Git không cho biết branch nào là 'nhánh tách ra' của branch khác, người dùng phải tự biết khi nào cần rebase branch nào.
  • Cả git rebase main lẫn kiểu rebase ngược git rebase mybranch đều khả thi. Merge cũng vậy.

Việc thiếu cấu trúc phân cấp giữa các branch trong Git hơi kỳ lạ

  • Câu nói branch main không hề đặc biệt xuất phát từ việc Git không nhận biết được quan hệ giữa các branch.
  • Giữa các branch có quan hệ với nhau, nhưng Git thì không biết gì cả.

UI branch của Git cũng hơi kỳ lạ

  • Khi chỉ muốn xem các commit 'được tách nhánh', cách dùng git loggit diff lại khác nhau.

Trên GitHub, branch mặc định là đặc biệt

  • GitHub có một 'branch mặc định', và branch này đóng vai trò đặc biệt.

Ý kiến của GN⁺

Điều quan trọng nhất trong bài này là hiểu được sự khác biệt giữa cách mọi người trực giác hóa Git branch và cách Git thực sự hoạt động. Bài viết sẽ giúp các kỹ sư phần mềm mới vào nghề hiểu rõ hơn về khái niệm Git branch và sử dụng nó hiệu quả hơn. Việc tìm hiểu mô hình trực quan của Git branch khớp với công việc thực tế như thế nào, cũng như cách Git không xử lý quan hệ giữa các branch, vừa thú vị vừa hữu ích.

1 bình luận

 
GN⁺ 2023-11-24
Ý kiến trên Hacker News
  • Branch là con trỏ trỏ tới một commit, và mỗi khi tạo commit mới thì con trỏ này sẽ được cập nhật. Có thể xem branch như một cái tên di động giống tag. Vì bản thân commit trỏ tới commit cha của nó, branch thực chất là một chuỗi các commit liên quan với một điểm vào đã được đặt tên. Khi xóa branch, nhãn có tên đó sẽ biến mất, và chỉ còn lại chuỗi các commit liên quan.
  • Sẽ dễ hiểu hơn nếu nghĩ phả hệ của commit là các con trỏ trỏ "ngược về sau" thay vì "tiến về phía trước". Vì branch là một ID commit, nên nếu lần theo các liên kết tới commit cha thì có thể tìm ra toàn bộ lịch sử của branch đó. "Điểm phân nhánh" là nơi hai chuỗi commit gặp nhau, còn merge commit thì đặc biệt vì nó cho thấy hai lịch sử đã được hợp nhất thành một.
  • Trong các dự án cá nhân, bạn bè tôi thường nổi giận khi thấy tôi dùng git reset --hardgit stash để thao tác thay đổi và con trỏ branch. Để hủy một lần merge sai, tôi dùng git reset --hard <commit cuối cùng trước khi merge>, và để áp dụng những chỉnh sửa nhỏ trên branch cục bộ sang branch chính, tôi dùng git stash, sau đó checkout sang branch chính rồi dùng git stash apply.
  • Git không có khái niệm "main là đặc biệt", nhưng các công cụ như GitLab có thể cung cấp tính năng protected branch để giảm sai sót. Khái niệm branch "cha" và "con" thực ra có thể khá thú vị, và để hỗ trợ các branch hỗ trợ dài hạn thì cần hỗ trợ nhiều branch "cha".
  • Khi merge, rebase hoặc tạo pull request, bạn phải chỉ định tường minh branch khác là branch nào. Git không biết branch nào là branch nền tảng mà người dùng đang nghĩ tới. Đôi khi bạn muốn merge một feature branch vào một feature branch khác, nên cần chỉ rõ branch nào sẽ được merge vào branch nào.
  • Ngay cả khi trực giác của mọi người có phần sai về mặt kỹ thuật, việc họ có trực giác như vậy vẫn có lý do chính đáng.
  • Có một hướng dẫn tương tác dành cho những người đã biết dùng git addgit commit. Hướng dẫn này trực quan hóa branch để giúp người đọc dễ hiểu hơn.
  • Nếu nhớ rằng khi thực hiện lệnh Git, bạn "luôn" sửa branch hiện tại, thì sẽ "dễ" hiểu cú pháp của Git hơn. Ví dụ, git merge my-branch là merge my-branch vào branch hiện tại, còn git rebase my-branch là rebase branch hiện tại lên trên my-branch.
  • Sẽ hay hơn nếu branch (head) có một "đuôi" trỏ tới commit nền tảng nơi branch đó bắt đầu. Vì branch thường xuyên bị rebase, đôi khi bạn cần biết nó bắt đầu từ đâu. Sẽ tiện hơn nếu Git cho biết commit nền tảng đó thuộc về main.
  • Khi gửi "patch" lên mailing list, bạn có thể tùy chọn kèm theo commit nền tảng. Lý do là có thể không rõ thay đổi đó dựa trên bản phát hành mới nhất, branch phát triển chính hay branch tích hợp. Khi dùng git range-diff, bạn cũng cần lưu ý tới nền tảng. Công cụ này so sánh hai phạm vi như main..previousmain..current.
  • Khi đọc lại quan điểm cá nhân của mình về branch, tôi đã học lại được vài điều mà trước đây mình từng quên.