Git Submodule

Git Submodule 允許在一個 Git 倉庫中嵌入另一個 Git 倉庫,適合管理專案間的共用資源。

前置條件

建立兩個 GitHub Repo:

  • 主 repogit-main-module
  • 子 repoclaude-skill

建立子 repo

$ mkdir claude-skill
$ cd claude-skill
$ git init
Initialized empty Git repository in .../claude-skill/.git/

$ mkdir skill
$ echo "echo 'Hello from claude-skill'" > skill/demo.sh

$ git add .
$ git commit -m "initial commit of claude-skill"
[master (root-commit) b3afa7d] initial commit of claude-skill
 1 file changed, 1 insertion(+)
 create mode 100644 skill/demo.sh

$ git branch -M main
$ git remote add origin git@github.com:TLexYuW/claude-skill.git
$ git push -u origin main

建立主 repo

$ mkdir git-main-module
$ cd git-main-module
$ git init
Initialized empty Git repository in .../git-main-module/.git/

$ echo "hello main module" >> main.txt
$ git add .
$ git commit -m "initial commit of main module"
[main (root-commit) d7d4992] initial commit of main module

$ git branch -M main
$ git remote add origin git@github.com:TLexYuW/git-main-module.git
$ git push -u origin main

在主 repo 加入 Submodule

$ git submodule add git@github.com:TLexYuW/claude-skill.git .claude/skill
Cloning into '.claude/skill'...
remote: Enumerating objects: ...
remote: Counting objects: ...
remote: Compressing objects: ...
remote: Total ... (delta ...)
Resolving deltas: ...

$ git status
On branch main
Changes to be committed:
  new file:   .gitmodules
  new file:   .claude/skill

$ git add .
$ git commit -m "Add claude-skill as submodule"
[main a0c1450] Add claude-skill as submodule
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 .claude/skill
$ git push origin main

主 repo 目錄結構

git-main-module/
│  main.txt
│  .gitmodules
└─ .claude/
    └─ skill/
        └─ demo.sh

.gitmodules 檔案內容

[submodule ".claude/skill"]
    path = .claude/skill
    url = git@github.com:TLexYuW/claude-skill.git

Clone 主 repo 含 Submodule

$ git clone git@github.com:TLexYuW/git-main-module.git
$ cd git-main-module

# 初始化 submodule
$ git submodule update --init --recursive

# 或者直接一次完成
$ git clone --recurse-submodules git@github.com:TLexYuW/git-main-module.git
Clone 後 .claude/skill 目錄會包含子 repo 完整檔案。

更新子 repo

假設在子 repo 新增檔案:

$ cd .claude/skill
$ echo "echo 'New file from submodule'" > new_script.sh
$ git add .
$ git commit -m "Add new script in claude-skill"
$ git push origin main

同步主 repo 指向最新子 repo commit

回到主 repo:

$ cd ../..   # 回到 git-main-module
$ git submodule update --remote --merge
Submodule path '.claude/skill': checked out 'abcd1234'
$ git add .claude/skill
$ git commit -m "Update submodule claude-skill to latest"
$ git push origin main

Submodule 更新需要兩層 commit:

  1. 子 repo commit
  2. 主 repo commit 指向新的子 repo commit hash

修改子 repo 後直接在主 repo 推回

# 修改子 repo
$ cd .claude/skill
$ echo "echo 'Update from main module'" >> update_script.sh
$ git add .
$ git commit -m "Update from main module"
$ git push origin main

# 回到主 repo commit pointer
$ cd ../..
$ git add .claude/skill
$ git commit -m "Update submodule pointer after local change"
$ git push origin main
Submodule 兩層 commit 原則:修改子 repo → push 子 repo → commit pointer in main repo → push main repo

刪除 Submodule

$ git submodule deinit .claude/skill
$ git rm --cached .claude/skill
$ rm -rf .git/modules/.claude/skill
$ git commit -m "Remove submodule claude-skill"
$ rm -rf .claude/skill
$ git push origin main

Submodule 注意事項

優點

  • 精準鎖定子 repo commit
  • 適合明確依賴

缺點

  • Clone 需要額外步驟
  • 更新需要兩層 commit
  • CI/CD 操作需要注意 submodule 初始化

操作重點

操作指令
新增子 repogit submodule add <url> <path>
Clone 或更新git submodule update --init --recursive
拉最新子 repogit submodule update --remote --merge
修改後推送修改子 repo → push → 主 repo commit pointer → push
刪除deinit + rm --cached + rm -rf .git/modules + commit