Sửa lỗi "fatal: shallow update not allowed" Khi Push từ Shallow Clone

intermediate📦 Git2026-06-19| Git 2.x trên Linux, macOS, Windows — thường xảy ra trong CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, CircleCI) khi dùng shallow clone với flag --depth

Error Message

fatal: shallow update not allowed
#git#shallow-clone#push#unshallow#ci-cd

Lỗi Xảy Ra

Bạn (hoặc CI runner) đã clone với flag giới hạn độ sâu, và bây giờ git push thất bại hoàn toàn:

fatal: shallow update not allowed

Lỗi này thường xảy ra nhất trong các bước release. Runner lấy shallow clone để tiết kiệm băng thông, sau đó một job sau lại cố push tag, cập nhật version, hoặc branch chứa build artifact — và Git từ chối thẳng thừng.

Nguyên Nhân

Shallow clone là bản sao không đầy đủ có chủ đích. Git theo dõi điểm cắt trong một file tên .git/shallow, file này liệt kê các commit ranh giới — những commit mà commit cha của chúng chưa bao giờ được tải xuống.

Khi remote nhận push của bạn, nó duyệt qua lịch sử commit để xác minh mọi thứ khớp nhau. Gặp một ranh giới chưa từng thấy, nó dừng lại thay vì mạo hiểm làm hỏng đồ thị lịch sử. Ba tình huống kích hoạt lỗi này:

  • Bạn dùng git clone --depth N ở máy local và đang push lên remote đầy đủ.
  • CI runner của bạn checkout với fetch-depth: 1 (mặc định của actions/checkout) và một step sau đó push tag hoặc commit.
  • Bạn đang push từ một shallow clone sang một shallow clone khác — cả hai phía đều có lịch sử không đầy đủ và không bên nào có thể bù đắp phần thiếu.

Chẩn Đoán Trước

Một lệnh duy nhất cho bạn biết bản sao có phải shallow hay không:

git rev-parse --is-shallow-repository

true nghĩa là có. Bạn cũng có thể kiểm tra ranh giới trực tiếp:

cat .git/shallow

Nếu file tồn tại và có nội dung, bạn đang làm việc với shallow clone. Mỗi dòng là một commit SHA đánh dấu rìa của những gì đã được tải xuống.

Cách Sửa 1 — Unshallow Trước Khi Push (Khuyến Nghị)

Fetch toàn bộ lịch sử, sau đó push:

git fetch --unshallow
git push origin HEAD

Lệnh này kéo về mọi commit còn thiếu và xóa .git/shallow, chuyển đổi repo thành full clone. Từ lúc này, push hoạt động bình thường — không cần thêm bước xử lý nào.

Trên các phiên bản Git cũ hơn không có --unshallow, dùng lệnh này thay thế:

git fetch --depth=2147483647
git push origin HEAD

Con số 2147483647 là INT_MAX. Git hiểu đó là "cho tôi tất cả mọi thứ," cho kết quả tương đương.

Cách Sửa 2 — Thay Đổi Độ Sâu Checkout Trong CI

Với CI pipeline, giải pháp tốt hơn là ngừng shallow clone cho các job có push. Đặt depth thành 0 chỉ cho release và deploy job — các job test và lint vẫn có thể dùng shallow.

GitHub Actions

- uses: actions/checkout@v4
  with:
    fetch-depth: 0   # 0 = toàn bộ lịch sử

GitLab CI

variables:
  GIT_DEPTH: 0

Jenkins (pipeline script)

checkout([
  $class: 'GitSCM',
  extensions: [[$class: 'CloneOption', shallow: false]],
  // ... các tùy chọn khác
])

Cách Sửa 3 — Fetch Có Chọn Lọc Chỉ Cho Tags

Cần push một tag duy nhất nhưng không muốn tải về hàng gigabyte lịch sử? Fetch vừa đủ cho commit của tag:

git fetch --depth=1 origin main
git tag v1.2.3
git push origin v1.2.3

Cách này chỉ hoạt động khi tag trỏ đến một commit mà remote đã biết. Nếu bạn đang push các commit mới mà remote chưa từng thấy, hãy quay lại Cách Sửa 1 hoặc Cách Sửa 2.

Cách Sửa 4 — Khi Remote Repository Là Shallow

Hiếm gặp, nhưng xảy ra khi ai đó tạo bare repo bằng git clone --depth N --bare. Trong trường hợp đó, unshallow phía server:

# Chạy trên server
cd /path/to/remote.git
git fetch --unshallow

Không có quyền truy cập server? Hãy tạo lại remote từ một full clone thay thế.

Xác Minh Sau Khi Sửa

Xác nhận repo không còn là shallow nữa:

git rev-parse --is-shallow-repository
# Kết quả phải là: false

Sau đó thử push lại:

git push origin HEAD
# hoặc với tag:
git push origin v1.2.3

Không có output fatal: nghĩa là mọi thứ đã ổn.

Phòng Ngừa Trong CI/CD

Shallow clone tồn tại vì một lý do duy nhất: tốc độ. Một clone depth-1 trên repo 50.000 commit có thể rút ngắn thời gian checkout từ 30 giây xuống dưới 2 giây. Sự đánh đổi đó hợp lý cho các job compile và test không cần động đến lịch sử.

Nó sẽ gây lỗi ngay khi một job cần làm việc với lịch sử quá khứ — push commit, tạo tag, chạy git describe, tạo changelog, hoặc so sánh diff giữa các release.

Phân chia thực tế: fetch-depth: 1 cho test và lint, fetch-depth: 0 cho release và deploy. Hầu hết các CI system cho phép cấu hình riêng từng job, nên bạn giữ được tốc độ ở nơi cần tốc độ và tính chính xác ở nơi cần độ chính xác.

Với pipeline có nhiều job, hãy xem xét tạo một checkout job chuyên dụng để fetch toàn bộ lịch sử và cache thư mục .git. Các job phía sau restore từ cache thay vì phải clone lại.

Tóm Tắt Nhanh

  • Shallow clone local → remote đầy đủ: git fetch --unshallow && git push
  • CI pipeline với depth: 1: đặt fetch-depth: 0 trong bước checkout
  • Chỉ push một tag: git fetch --depth=1 origin <branch> && git push origin <tag>
  • Remote là shallow: unshallow hoặc tạo lại remote

Related Error Notes