Khi thư mục .git bỗng nhiên trở thành ‘gánh nặng’ ổ cứng
Vài tháng trước, hệ thống CI/CD của team mình bất ngờ báo lỗi No space left on device ngay giữa đêm. Kiểm tra nhanh server Jenkins, mình phát hiện ổ cứng đã đầy sạch 100%. Đáng nói là thủ phạm không phải log file hay docker image, mà chính là thư mục .git của một dự án lâu năm đã phình to lên gần 5GB.
Mỗi lần developer thực hiện git fetch, lượng dữ liệu rác tích tụ qua nhiều năm lại tiếp tục nhân bản. Nếu bạn thấy tốc độ clone dự án chậm như rùa hoặc dung lượng .git lớn bất thường, đã đến lúc cần một đợt “tổng vệ sinh” bằng Git GC và Git Prune.
Tại sao thư mục .git lại phình to quá mức?
Về cơ bản, Git lưu trữ toàn bộ lịch sử thay đổi chứ không chỉ code hiện tại. Có ba lý do chính khiến kho lưu trữ này trở nên cồng kềnh:
- Loose Objects (Đối tượng rời rạc): Mỗi lần bạn
git add, Git tạo ra một file nén zlib mới. Qua hàng nghìn commit, số lượng file nhỏ lẻ này sẽ tăng chóng mặt và làm chậm hệ thống. - Dangling Commits (Commit mồ côi): Các thao tác như
rebasehaycommit --amendthường để lại các commit cũ. chúng vẫn nằm trong database nhưng không thuộc về bất kỳ nhánh nào. - Dữ liệu từ Reflog: Cơ chế
reflogghi lại mọi hành động để giúp bạn khôi phục dữ liệu khi lỡ tay. Mặc định, Git sẽ giữ các bản ghi này từ 30 đến 90 ngày.
Thực tế tại team mình, anh em từng lỡ tay commit vài file binary nặng như .jar và ảnh raw. Dù sau đó đã xóa file và commit lại, các file nặng nề này vẫn âm thầm tồn tại trong lịch sử Git, khiến dung lượng repo không thể giảm xuống.
Bước 1: Đo lường “sức khỏe” của Repository
Đầu tiên, hãy kiểm tra xem có bao nhiêu dữ liệu thừa bằng lệnh:
git count-objects -vH
Hãy chú ý thông số count (số lượng loose objects) và size-pack. Nếu count lên đến hàng chục nghìn, đó là dấu hiệu rõ ràng cho thấy repo của bạn đang cực kỳ bừa bộn.
Bước 2: Nén dữ liệu với Git GC (Garbage Collection)
Lệnh git gc giống như một chiếc xe rác chuyên dụng. Nó sẽ gom các loose objects thành packfiles, loại bỏ các đối tượng không còn được tham chiếu và cập nhật lại chỉ mục (index).
Để tối ưu hóa sâu nhất, mình thường sử dụng flag --aggressive:
git gc --aggressive --prune=now
Flag --aggressive giúp Git tìm kiếm các delta (sự khác biệt giữa các file) hiệu quả hơn để nén dung lượng tối đa. Tuy nhiên, hãy cẩn thận với --prune=now. Lệnh này xóa sạch các commit mồ côi ngay lập tức, khiến bạn không thể dùng reflog để cứu dữ liệu nếu lỡ tay xóa nhầm nhánh trước đó.
Bước 3: Dọn dẹp triệt để bằng Git Prune
Đôi khi git gc vẫn chưa thể xóa hết vì vướng cơ chế bảo vệ của reflog. Để thực sự tống khứ những dữ liệu cũ kỹ, mình thường sử dụng bộ lệnh “thanh trừng” mạnh tay sau đây:
# Ép các bản ghi reflog hết hạn ngay lập tức
git reflog expire --expire=now --all
# Xóa các object không còn được tham chiếu
git prune --verbose --progress
# Gom tất cả vào một packfile duy nhất
git repack -ad
Kết quả thật bất ngờ. Sau khi chạy combo này trên dự án 5GB kể trên, dung lượng thư mục .git giảm xuống chỉ còn chưa đầy 800MB. Tốc độ thực hiện lệnh git status cũng nhanh hơn thấy rõ.
Chiến thuật ngăn chặn repo phình to từ sớm
Dọn dẹp chỉ là giải pháp tình thế. Để duy trì một repo sạch sẽ, mình áp dụng hai quy tắc cứng cho team:
1. Kiểm soát file binary chặt chẽ
Tuyệt đối không đưa các file build (node_modules, target), file log hay binary lớn vào Git. Nếu lỡ commit, việc xóa chúng khỏi lịch sử rất phức tạp, thường phải dùng đến các công cụ chuyên sâu như BFG Repo-Cleaner.
2. Bảo trì định kỳ trên CI/CD
Thay vì đợi ổ cứng báo đỏ, mình thêm một script bảo trì chạy vào cuối tuần cho các dự án lớn:
#!/bin/bash
git gc --prune=today --quiet
git remote prune origin
Lệnh git remote prune origin cực kỳ hữu ích. Nó giúp dọn sạch các tham chiếu đến những nhánh đã bị xóa trên server, giữ cho danh sách branch của anh em luôn gọn gàng.
Lời kết
Quản lý Git Repository cũng giống như dọn dẹp nhà cửa. Thực hiện định kỳ sẽ giúp mọi thứ nhẹ nhàng và trơn tru. Nếu repo của bạn đang có dấu hiệu chậm chạp, hãy thử ngay git gc --aggressive. Bạn sẽ ngạc nhiên với lượng dung lượng tiết kiệm được đấy!
Nếu bạn từng gặp rắc rối với những file nặng lỡ tay commit vào Git, hãy chia sẻ trải nghiệm ở phần bình luận nhé!
