Hướng dẫn sử dụng git check-ignore và git check-attr: Debug quy tắc loại trừ file trong Git

Git tutorial - IT technology blog
Git tutorial - IT technology blog

1. Vấn đề thực tế khi làm việc với repository phức tạp

Dự án monorepo công ty mình từng phình to hơn 15 microservices với hàng chục thư mục lồng nhau. Quản lý file khi đó thực sự thành ác mộng. Có hôm, một bạn junior tá hỏa vì file .env.staging vô tình lọt vào commit rồi đẩy thẳng lên remote repo. Ai cũng đinh ninh file này đã được ignore từ trước.

Lần khác, một file icon mới tại packages/ui/assets/icons/logo.svg lại không hiện khi gõ git status. Cả team loay hoay không rõ nguyên nhân. Để kịp tiến độ, anh em thường chọn cách nhanh nhất: gõ git add -f để ép Git nhận file.

Hậu quả sau đó rất phiền toái. Dung lượng repo tăng vọt, xung đột dấu ngắt dòng (CRLF/LF) giữa Windows và macOS nổ ra liên miên trên từng pull request. Việc cấu hình sai ignore và attributes vẫn là bẫy ngầm khó phát hiện nếu chỉ nhìn bằng mắt thường.

2. Vì sao Git không bỏ qua file như bạn mong đợi?

Git không chỉ đọc duy nhất một file .gitignore ở thư mục gốc. Khi xác định một file có bị bỏ qua hay gán thuộc tính hay không, Git duyệt qua nhiều tầng ưu tiên:

  • File .gitignore cục bộ: Các file nằm rải rác trong từng thư mục con sẽ ghi đè quy tắc của thư mục cha.
  • File cấu hình riêng của repo: File .git/info/exclude chỉ có hiệu lực trên máy cá nhân và không được commit lên remote.
  • File cấu hình toàn cục: File global ignore được định nghĩa qua biến core.excludesFile trong ~/.gitconfig.
  • Quy tắc phủ định (Negation !): Nếu thư mục cha đã bị ignore hoàn toàn (ví dụ dist/), Git bỏ qua toàn bộ thư mục đó. Quy tắc phủ định bên trong như !dist/bundle.js sẽ hoàn toàn vô tác dụng.

Cơ chế của .gitattributes cũng tương tự. Thiết lập chuyển đổi dòng (eol=lf), filter Git LFS hay diff driver đều bị ghi đè theo cấp bậc thư mục. Khi repo có hàng chục file cấu hình lồng nhau, tra cứu thủ công là bất khả thi.

3. Các thói quen xử lý sai lầm thường gặp

Cách 1: Lùng sục thủ công bằng Ctrl + F hoặc grep

Mỗi khi file bị ẩn, dev thường mở file .gitignore ở root rồi nhấn Ctrl + F. Tìm không thấy, họ lại lần mò vào từng thư mục con để đọc tiếp.

Điểm yếu: Cách này tốn thời gian và dễ bỏ sót các mẫu glob phức tạp như **/*.log hay build/*/temp. Bạn cũng hoàn toàn mù tịt trước các quy tắc đến từ global config trên máy cá nhân.

Cách 2: Chữa cháy bằng git add -f

Khi cần commit gấp mà Git ngó lơ file, nhiều bạn tiện tay gõ luôn:

git add -f src/services/mailer/templates/welcome.html

Điểm yếu: Lệnh force chỉ giải quyết phần ngọn. Rule sai trong cấu hình vẫn nằm nguyên đó. Đồng nghiệp khác khi pull code về và sửa tiếp file này vẫn sẽ bị ignore. Nguy hiểm hơn, thói quen này rất dễ làm lọt các file chứa secret, credential nhạy cảm lên repo.

Cách 3: Sử dụng git check-ignore và git check-attr

Git tích hợp sẵn hai công cụ chuyên dụng: git check-ignore và git check-attr. Chúng hoạt động như một công cụ debug, chỉ ra chính xác dòng nào trong file cấu hình nào đang chi phối file của bạn.

4. Quy trình debug chuẩn xác trong thực tế

Từ khi đưa hai lệnh này vào checklist xử lý sự cố của team, mọi khúc mắc về ignore và line-ending đều được giải quyết trong vài giây.

Debug quy tắc loại trừ với git check-ignore

Cú pháp hiệu quả nhất là thêm cờ -v (verbose). Git sẽ in ra chi tiết: File cấu hình : Số dòng kích hoạt : Mẫu quy tắc (pattern).

# Kiểm tra file local.json bị ignore bởi dòng nào
git check-ignore -v config/environments/local.json

Kết quả trả về trên terminal:

.gitignore:14:local.*    config/environments/local.json

Output hiển thị rõ ràng: dòng 14 trong file .gitignore gốc với rule local.* chính là nguyên nhân.

Muốn kiểm tra nhiều file cùng lúc, hãy thêm cờ --non-matching (hoặc -n) để xem cả những file không bị ignore:

# Kiểm tra danh sách file
git check-ignore -v -n src/index.ts .env.local docs/setup.pdf

Output mẫu:

::      src/index.ts
.gitignore:3:.env*    .env.local
packages/docs/.gitignore:2:*.pdf    docs/setup.pdf

Ký hiệu :: ở dòng đầu tiên cho biết file src/index.ts hoàn toàn bình thường, không khớp với bất kỳ rule ignore nào.

Xử lý bẫy Negation (!) thường gặp

Một tình huống rất phổ biến: bạn muốn bỏ qua thư mục logs/ nhưng cần giữ lại file logs/important.log.

# Cấu hình sai trong .gitignore:
logs/
!logs/important.log

File important.log vẫn không xuất hiện khi chạy git status. Hãy soi lại bằng git check-ignore:

git check-ignore -v logs/important.log
# Trả về: .gitignore:1:logs/    logs/important.log

Cách sửa chuẩn: Thay vì ignore cả thư mục logs/, bạn chỉ ignore nội dung bên trong để Git vẫn duyệt thư mục này:

# Cấu hình đúng trong .gitignore:
logs/*
!logs/important.log

Kiểm tra thuộc tính file với git check-attr

Trên môi trường cross-platform hoặc khi sử dụng Git LFS, file .gitattributes chịu trách nhiệm định dạng text/binary và quy tắc chuyển đổi CRLF/LF.

Để liệt kê toàn bộ thuộc tính đang áp dụng cho một file:

git check-attr -a src/scripts/deploy.sh

Kết quả hiển thị:

src/scripts/deploy.sh: text: set
src/scripts/deploy.sh: eol: lf
src/scripts/deploy.sh: diff: unspecified

Để kiểm tra xem file thiết kế Photoshop dung lượng 50MB đã ăn cấu hình Git LFS hay chưa:

git check-attr filter diff merge assets/banner.psd

Nếu terminal trả về filter: lfs, cấu hình đã chuẩn. Nếu là unspecified, file .gitattributes đang thiếu rule cho định dạng *.psd.

Bảng lệnh bỏ túi cần nhớ

  • git check-ignore -v <path>: Tìm chính xác file và số dòng đang ignore target.
  • git check-ignore -v -n <paths...>: Kiểm tra trạng thái ignore của nhiều file cùng lúc.
  • git check-attr -a <path>: Xem toàn bộ thuộc tính EOL, diff, LFS gán lên file.

Gặp file bị ẩn hoặc sai format dòng, đừng vội gõ git add -f. Chạy thử hai lệnh trên, bạn sẽ bắt đúng bệnh và sửa tận gốc vấn đề.

Share: