Sổ tay kỹ thuật

Git submodule và subtree – nhúng repo khác vào repo của mình

concept#git· cập nhật 2026-11-06

Dùng khi cần đưa một repo khác (thư viện template, bộ YANG model, role Ansible dùng chung) vào project và phải chọn giữa submodule và subtree.

Sửa trang này
Mục lục

Có hai cách chính để nhúng repo B vào thư mục con của repo A:

  • Submodule: A chỉ lưu con trỏ (URL trong .gitmodules + SHA commit của B). Code B không nằm trong lịch sử A; người clone phải kéo thêm.
  • Subtree: A chứa bản copy thật của code B (thường squash thành 1 commit), merge vào thư mục con. Clone A là đủ, không cần lệnh đặc biệt.

Trước khi chọn, hỏi: có thật cần nhúng không? Nếu B là package Python/collection Ansible thì cài qua package manager (uv add, ansible-galaxy) thường đơn giản hơn cả hai.

Mô hình lưu trữ

flowchart LR A["repo app"] -->|".gitmodules + SHA"| SM["vendor/lib (submodule)"] SM -.->|"clone riêng"| B["repo lib"] A2["repo app"] -->|"commit chứa file thật"| ST["vendor/lib (subtree)"] B -->|"git subtree pull"| ST ST -->|"git subtree push"| B

So sánh

Tiêu chí Submodule Subtree
Repo cha lưu gì URL + SHA cố định Toàn bộ file trong thư mục con
Clone Cần --recurse-submodules hoặc submodule update --init git clone thường là đủ
Ghim version Rõ ràng: đúng 1 SHA Theo commit merge gần nhất (xem log)
Cập nhật từ upstream git submodule update --remote rồi commit SHA mới git subtree pull --prefix=... --squash
Sửa code nhúng và đẩy ngược Commit trong thư mục submodule, push repo con git subtree push / split, chậm khi lịch sử dài
Người dùng cần biết lệnh đặc biệt Có, dễ quên --init Hầu như không
Dung lượng repo cha Nhỏ Tăng theo code nhúng
CI Phải bật checkout submodule, cần quyền đọc repo con Không cần gì thêm
Có sẵn Git core git subtree nằm trong contrib, đa số bản phân phối có sẵn

Lệnh submodule

Mục đích Lệnh
Thêm submodule, theo dõi branch main git submodule add -b main https://github.com/org/lib.git vendor/lib
Clone repo kèm submodule git clone --recurse-submodules URL
Đã clone thường, kéo submodule về git submodule update --init --recursive
Xem SHA đang ghim (+ = lệch, - = chưa init) git submodule status
Nâng submodule lên đầu branch upstream git submodule update --remote vendor/lib → git add vendor/lib && git commit
Xem commit mới của submodule trong diff git diff --submodule=log
Chạy lệnh trong mọi submodule git submodule foreach 'git log -1 --oneline'
pull/switch tự cập nhật submodule git config --global submodule.recurse true
Đổi URL (vd chuyển sang SSH) Sửa .gitmodules → git submodule sync --recursive
Gỡ submodule git submodule deinit -f vendor/lib && git rm -f vendor/lib, sau đó xoá .git/modules/vendor/lib

Submodule sau update ở trạng thái detached HEAD. Muốn sửa code trong đó: cd vendor/lib && git switch main trước, nếu không commit dễ bị "lạc" khi update lần sau.

GitHub Actions: actions/checkout cần with: submodules: recursive (repo con private thì cần thêm token có quyền đọc), xem GitHub Actions cheatsheet.

Lệnh subtree

Mục đích Lệnh
Nhúng repo vào thư mục con (gộp lịch sử thành 1 commit) git subtree add --prefix=vendor/lib https://github.com/org/lib.git main --squash
Kéo bản mới từ upstream git subtree pull --prefix=vendor/lib https://github.com/org/lib.git main --squash
Đẩy thay đổi trong vendor/lib lên branch của repo lib git subtree push --prefix=vendor/lib https://github.com/org/lib.git from-app
Tách lịch sử thư mục con thành branch riêng git subtree split --prefix=vendor/lib -b lib-export

Luôn dùng cùng một kiểu (--squash hoặc không) cho mọi lần add/pull của một prefix. Đặt remote cho gọn: git remote add lib https://github.com/org/lib.git rồi dùng lib main thay cho URL.

Khi nào dùng

  • Submodule: cần ghim chính xác version, repo con lớn hoặc cập nhật độc lập, nhiều repo cha cùng dùng và team quen lệnh submodule. Ví dụ: bộ YANG model của vendor, firmware SDK.
  • Subtree: muốn người clone không phải biết gì thêm, code nhúng nhỏ và ít khi đẩy ngược. Ví dụ: bộ template Jinja2 dùng chung, script tiện ích.
  • Không dùng cả hai: khi B có thể phát hành thành package (PyPI, Ansible collection, Helm chart); khi chỉ cần một vài file thì copy và ghi rõ nguồn.
  • Tách một thư mục trong monorepo ra repo riêng (giữ lịch sử): git subtree split là cách nhanh không cần tool ngoài.

Tham khảo