- vừa được xem lúc

Làm thế nào để clone một Git submodule khổng lồ trong nháy mắt

0 0 9

Người đăng: Bùi Hiếu

Theo Viblo Asia

Những Git repo khổng lồ với kích thước lên đến vài GB hay vài chục GB cũng không phải là chuyện hiếm gặp. Nhất là với những dự án có nhiều file binary lớn như video game chẳng hạn. Bạn sẽ có vài chục GB asset và mỗi commit có thể dễ dàng lên đến vài chục MB. Hoặc có thể đó là một monorepo với vài chục dự án và vài trăm thư viện dùng chung.

Ví dụ như cái này https://github.com/chromium/chromium.

git-clone-chromium.png

Dù đã clone với option --depth=1 nghĩa là chỉ download 1 commit gần nhất thì dung lượng phải tải về đã lên đến hơn 1GB và mất khoảng 3 phút để tải về với tốc độ khoảng 12MB/s. Kích thước của repo sẽ vào khoảng 4GB.

Tât nhiên nếu cả ngày bạn chỉ pull có một lần thì thôi cũng tạm chấp nhận được, trong lúc chờ đợi đi làm ly cafe luôn. Nhưng nếu dự án của bạn sử dụng hệ thống CI nào đó mà mỗi lần build lại phải tải mấy chục GB về thì đúng là dành cả thanh xuân chỉ để pull code ?.

Hơn nữa đây là một monorepo với cả app desktop, iOS, Android và rất nhiều thứ nữa. Khá chắc là khi làm việc bạn sẽ không sử dụng hết cả 4GB code đấy mà chỉ dùng 1 phần thôi.

Những phiên bản gần đây của Git đã có thêm vài tính năng mới để bạn chỉ chọn dowload những phần bạn muốn thay vì toàn bộ mấy chục GB.

Partial clone và sparse checkout

Để sử dụng tính năng partial clone, mình sẽ thêm option --filter=blob:none. Ngoài ra mình sẽ thêm cả --no-checkout để chỉ clone folder .git mà không checkout file.

git clone --filter=blob:none --no-checkout --depth=1 https://github.com/chromium/chromium.git

Vì chỉ cần clone 1 commit và không tải file nào cả nên mình chỉ cần tải 11MB và xong trong nháy mắt.

Xong bước này thì repo mình clone về mới chỉ có mỗi folder .git và chưa có file nào cả. Mình cần checkout để tải file về nữa. Nhưng không phải là toàn bộ 4GB file như ở trên mà chỉ những file mình muốn thôi. Ví dụ mình chỉ làm phần iOS app nên mình chỉ cần folder ios chẳng hạn.

Mình sẽ dùng một tính năng khác của Git là sparse checkout như thế này.

git config core.sparseCheckout true
echo ios > .git/info/sparse-checkout
git read-tree -mu HEAD

2 dòng đầu là để config sparse-checkout. Nếu bạn có phiên bản Git mới (khoảng 2.22.0) thì bạn có thể dùng syntax mới tiện lợi hơn như này.

git sparse-checkout init --cone
git sparse-checkout set ios

Checkout cũng xong trong nháy mắt. Mình chỉ cần tải về khoảng hơn 100MB.

Chỉ có folder iOS và những thứ liên quan được checkout thay vì toàn bộ repo.

Áp dụng cho Submodule

Việc sử dụng package hoặc thư viện của bên thứ ba trong quá trình phát triển phần mềm thì quá là bình thường rồi. Hầu hết các ngôn ngữ lập trình đều cung cấp một kiểu package manager nào đó để người dùng có thể dễ dàng tải các package hoặc chia sẻ package với mọi người. Tuy nhiên có những lúc bạn không thể sử dụng package manager mặc dù source code của package có thể dễ dàng tìm thấy trên Github, Gitlab. Có thể là bạn muốn build package với option khác (với biến SCSS khác chẳng hạn) với bản được cung cấp thông qua package manager. Hoặc đơn giả là tác giả không publish package đấy lên package manager nào cả. Giải pháp là đưa repo chứa package kia vào trong repo của bạn dưới dạng một submodule.

Với những package đơn giản nằm ở riêng một repo thì đơn giản rồi. Tuy nhiên có những package nằm trong một monorepo khác lớn hơn và cũng tương tự như trên, bạn chỉ quan tâm đến package bạn muốn thôi.

Cách làm cũng tương tự, thêm một chút submodule thôi. Ở đây mình sẽ cần init submodule một cách thủ công thay vì để git clone hộ mình, vì nó sẽ lại tải mấy chục GB về mất.

git submodule init chromium
cd chromium
git init
# Dùng `git config` để lấy submodule clone URL
git remote add origin $(git -C .. config submodule.chromium.url)

Tiếp theo sẽ là dùng git fetch để fetch commit tương ứng với submodule hiện tại. Ở đây hơi khác phần trên một chút, mình không thể dùng git clone vì nó sẽ clone commit mới nhất của branch, và với --depth=1 thì sẽ không có history nên sẽ không có cách nào để quay về commit mình muốn.

# Set config này để dùng được `--filter=blob:none` với git fetch
git config extensions.partialClone origin
# Fetch commit tương ứng với submodule
git fetch --filter=blob:none --depth=1 origin $(git -C .. ls-tree -r HEAD chromium | awk '{print $3}')

Phần tiếp theo thì giống như trên rồi, nếu bạn có git version mới thì bạn cũng có thể dùng syntax mới. Chỗ checkout mình sẽ dùng SHA của commit tương ứng với submodule thay vì HEAD.

git config core.sparseCheckout true
echo ios > .git/info/sparse-checkout
git read-tree -mu $(git -C .. ls-tree -r HEAD chromium | awk '{print $3}')

Cuối cùng thì move folder .git của submodule vào trong .git/modules của repo chính, giống với behavior của git submodule.

git -C .. submodule absorbgitdirs

Đây là toàn bộ script, bạn nên lưu lại thành một file để chạy mỗi lần clone repo.

git submodule init chromium
cd chromium
git init
git remote add origin $(git -C .. config submodule.chromium.url)
git config extensions.partialClone origin
git fetch --filter=blob:none --depth=1 origin $(git -C .. ls-tree -r HEAD chromium | awk '{print $3}')
git config core.sparseCheckout true
echo ios > .git/info/sparse-checkout
git read-tree -mu $(git -C .. ls-tree -r HEAD chromium | awk '{print $3}')
git -C .. submodule absorbgitdirs

Bình luận

Bài viết tương tự

- vừa được xem lúc

Đặt tên commit message sao cho "tình nghĩa anh em chắc chắn bền lâu"????

. Lời mở đầu. .

1 1 701

- vừa được xem lúc

Tập hợp những câu lệnh GIT hữu dụng

Dưới đây là một vài ví dụ về các câu lệnh Git mà tôi thường dùng. git config --global user.name "John Doe". git config --global user.

0 0 55

- vừa được xem lúc

Cấu hình CI/CD với Github (phần 2): Trigger một work flow

Events trigger. Bạn có thể cấu hình cho workflows chạy khi có một sự kiện nào đó xảy ra trên GitHub, theo một lịch có sẵn hoặc cũng có thể là một sự kiện nào đó xảy ra ngoài GitHub.

0 0 70

- vừa được xem lúc

Cấu hình CI/CD với Github (phần 1): Một ít lý thuyết

CI/CD là gì. Về mặt khái niệm là vậy nhưng về mặt triển khai thì CI/CD là quá trình tự động thực hiện các quá trình build, test, release, deploy khi có các trigger như commit/merge code lên một branch định sẵn hoặc có thể là tự động chạy theo một lịch cố định.

0 0 118

- vừa được xem lúc

Giới thiệu về Git LFS

. Git LFS là gì . Git LFS làm điều này bằng cách thay thế các tệp lớn trong repo của bạn bằng một con trỏ nhỏ.

0 0 29

- vừa được xem lúc

Git workflow được Google và Facebook sử dụng có gì hay ho

Với developer thì Git hẳn là công cụ rất quen thuộc và không thể thiếu rồi. Thế nhưng có mấy ai thực sự hiểu được Git.

0 0 66