Bối cảnh vấn đề
Hãy tưởng tượng bạn đang tập trung viết code, mọi thứ đang diễn ra suôn sẻ và bạn thực hiện git fetch để cập nhật những thay đổi mới nhất. Thay vì được cập nhật gọn gàng, Git dừng bạn lại ngay lập tức. Nó đưa ra một lỗi khó chịu về việc "khóa tham chiếu" (locking references). Bạn kiểm tra tên các nhánh và chúng có vẻ ổn. Tuy nhiên, Git vẫn từ chối hợp tác.
Điều này xảy ra do sự không tương thích cơ bản giữa Git và macOS. Trong khi Linux coi 'File.txt' và 'file.txt' là hai thực thể riêng biệt, macOS lại không phân biệt hoa thường nhưng vẫn giữ nguyên kiểu chữ (case-insensitive but case-preserving). Nếu một thư mục có tên 'Feature' đã tồn tại, bạn không thể tạo một thư mục khác tên là 'feature' trong cùng thư mục đó. Đối với máy Mac của bạn, chúng là cùng một đường dẫn.
Lỗi cụ thể
error: cannot lock ref 'refs/heads/feature/abc': 'refs/heads/feature/ABC' exists; cannot create 'refs/heads/feature/abc'
Tại sao điều này xảy ra
Git không chỉ là một công cụ; nó là một cơ sở dữ liệu sử dụng hệ thống tệp của bạn để lưu trữ dữ liệu. Theo mặc định, nó lưu các nhánh cục bộ dưới dạng các tệp riêng lẻ bên trong thư mục .git/refs/heads/. Đối với một nhánh có tên feature/ABC, Git sẽ tạo một thư mục tên là feature và một tệp tên là ABC bên trong đó.
Xung đột bắt đầu khi một đồng nghiệp đẩy nhánh feature/abc (viết thường) lên máy chủ. Khi bạn cố gắng pull nó về, Git cố gắng ghi một tệp mới tên là abc. Vì macOS coi ABC và abc là cùng một tệp, Git bị rơi vào một vòng lặp. Nó thấy một tham chiếu đã tồn tại nhưng nhận thấy dữ liệu không khớp, dẫn đến lỗi "cannot lock ref" (không thể khóa tham chiếu).
Cách khắc phục xung đột khóa
1. Loại bỏ các tham chiếu remote cũ (Prune)
Thông thường, phiên bản "cũ" của nhánh đã bị xóa hoặc đổi tên trên máy chủ, nhưng máy cục bộ của bạn vẫn đang bám lấy "bóng ma" của tham chiếu đó. Bạn cần yêu cầu Git đồng bộ hóa bộ nhớ của nó.
git remote prune origin
Lệnh này là một công cụ dọn dẹp nhanh chóng. Nó loại bỏ bất kỳ nhánh theo dõi origin/ nào không còn tồn tại trên remote. Nếu feature/ABC chỉ là một nhánh theo dõi từ xa, việc này thường sẽ giải quyết vấn đề ngay lập tức.
2. Xóa xung đột cục bộ
Nếu việc prune không hiệu quả, nguyên nhân có khả năng là một nhánh đang nằm trên ổ đĩa cục bộ của bạn. Bạn cần tìm tên cụ thể gây ra xung đột.
git branch -a | grep -i "feature/abc"
Nếu bạn thấy cả feature/abc và feature/ABC, một trong hai phải bị xóa. Vì máy Mac của bạn không thể phân biệt giữa chúng, hãy xóa phiên bản không khớp với quy ước đặt tên của nhóm bạn.
# Xóa cưỡng ép nhánh cục bộ gây xung đột
git branch -D feature/ABC
3. Dọn dẹp thủ công trong thư mục .git
Đôi khi hệ thống tệp bị kẹt nghiêm trọng đến mức các lệnh Git tiêu chuẩn không thể dọn sạch tàn dư. Trong những trường hợp hiếm hoi này, bạn có thể xóa tệp gây lỗi một cách thủ công. Hãy cẩn thận khi thực hiện việc này — bạn đang tác động trực tiếp vào cấu trúc nội bộ của Git.
- Mở terminal tại thư mục gốc của dự án.
- Di chuyển đến thư mục refs ẩn:
cd .git/refs/heads/ - Tìm thư mục khớp với tiền tố của bạn (ví dụ:
feature). - Xóa tệp khớp với tên nhánh gây xung đột.
Lệnh ví dụ:
rm .git/refs/heads/feature/ABC
Sau khi tệp đã bị xóa, hãy thử fetch lại: git fetch --prune.
4. Giải pháp triệt để: git pack-refs
Nếu bạn có hàng tá nhánh và lỗi liên tục xuất hiện, hãy sử dụng pack-refs. Lệnh này nén hàng trăm tệp nhánh nhỏ vào một tệp văn bản phẳng duy nhất có tên là packed-refs.
git pack-refs --all
Vì các nhánh giờ đây chỉ là các dòng trong một tệp văn bản thay vì các tệp riêng lẻ trên đĩa của bạn, các giới hạn của hệ thống tệp macOS không còn hiệu lực nữa. Nó giúp vượt qua hoàn toàn vấn đề phân biệt hoa thường.
Xác minh
Để đảm bảo việc sửa lỗi đã thành công, hãy kéo các thay đổi mới nhất:
git pull origin main
git fetch --all
Nếu lệnh kết thúc mà không có phàn nàn gì, bạn đã có thể tiếp tục công việc. Chạy git branch -a một lần cuối để xác nhận rằng chỉ còn lại phiên bản đúng kiểu chữ của nhánh.
Bài học cho tương lai
- Luôn sử dụng chữ thường: Tránh gây rắc rối cho bản thân. Hãy thống nhất với nhóm chỉ sử dụng chữ thường cho tên nhánh (ví dụ:
feature/fix-headerthay vìFeature/Fix-Header). - Tự động Prune: Thiết lập Git để tự động prune bằng cách chạy
git config --global fetch.prune true. Điều này giúp môi trường cục bộ của bạn luôn sạch sẽ mà không tốn thêm công sức. - Nhận thức về môi trường: Máy Mac của bạn rất thân thiện và không phân biệt hoa thường, nhưng các máy chủ sản xuất Linux thì không như vậy. Luôn coi sự khác biệt về kiểu chữ là những lỗi tiềm ẩn cần xử lý.

