git 的 submodule
做爲一個獨立的 repo
, 其擁有普通 repo
所有的功能, 咱們能夠徹底按照普通的 repo
管理命令來進入 submodule
中進行手動管理. 不過若是存在多個 submodule
位於同一 superproject
下時, 掌握一些 git submodule ...
命令就變得尤其重要了.git
本文列出了經常使用的一些 git submodule
管理命令, 並舉出實際應用中遇到的問題及解決方案.github
在 git 倉庫 superproject
的目錄中使用 git submodule add https://github/HanleyLee/C
便可將 https://github/HanleyLee/C 做爲一個 submodule
被 superproject
依賴與管理.bash
當 submodule
被修改時咱們能夠在 superproject
中獲得通知:markdown
在 submodule
中添加了新文件:oop
修改了 submodule
中的內容 (沒有 new commit
):fetch
在 submodule 中產生了 new commit
:ui
在 repo Test
做爲 submodule
被 superproject
管理後:url
repo Test
的目錄中對相關內容進行修改, 而後經過經常使用的 git 命令進行操做.superproject
下能夠經過 git submodule ***
命令來管理其下的全部子倉庫, 使其與遠程庫保持同步或推送到遠程庫.添加 git submodule
的方法很簡單, 使用 git submodule add <repo url>
便可. 添加完以後, 在 superproject
的目錄下會產生一個 .gitmodule
文件, 文件的結構以下:spa
[submodule "C"]
path = C
url = git@github.com:HanleyLee/C.git
[submodule "Cpp"]
path = Cpp
url = git@github.com:HanleyLee/Cpp.git
複製代碼
能夠看到, .gitmodule
文件中標記了每個 submodule
的 path
與 url
.指針
而後咱們進入 ./C
:
$ cd ./C
$ ls -l
$ ls -lhia
35107110 drwxr-xr-x 9 hanley staff 288B May 7 21:15 .
35107043 drwxr-xr-x 14 hanley staff 448B May 7 20:45 ..
35107127 -rw-r--r-- 1 hanley staff 26B May 7 19:59 .git
$ cat .git
gitdir: ../.git/modules/C
複製代碼
咱們發現 ./C/.git
居然是一個文件 (常規 git 目錄中的 .git 是文件夾), 而後其內容指向了另外一個文件夾 (相似於指針), 咱們再去到那個文件夾:
$ cd ../git/modules/C
$ ls -lhia
35107128 drwxr-xr-x 16 hanley staff 512B May 7 21:17 .
35107126 drwxr-xr-x 9 hanley staff 288B May 7 19:59 ..
35112722 -rw-r--r-- 1 hanley staff 17B May 7 21:17 COMMIT_EDITMSG
35109064 -rw-r--r-- 1 hanley staff 0B May 7 21:44 FETCH_HEAD
35109063 -rw-r--r-- 1 hanley staff 16B May 7 21:44 FETCH_LOG
35112529 -rw-r--r-- 1 hanley staff 21B May 7 20:25 HEAD
35116056 -rw-r--r-- 1 hanley staff 41B May 7 21:15 ORIG_HEAD
35114647 -rw-r--r-- 1 hanley staff 319B May 7 20:47 config
35107131 -rw-r--r-- 1 hanley staff 73B May 7 19:59 description
35107132 drwxr-xr-x 15 hanley staff 480B May 7 19:59 hooks
35116224 -rw-r--r-- 1 hanley staff 1.7K May 7 21:17 index
35107129 drwxr-xr-x 3 hanley staff 96B May 7 19:59 info
35107178 drwxr-xr-x 4 hanley staff 128B May 7 19:59 logs
35107159 drwxr-xr-x 9 hanley staff 288B May 7 21:40 objects
35107174 -rw-r--r-- 1 hanley staff 112B May 7 19:59 packed-refs
35107146 drwxr-xr-x 5 hanley staff 160B May 7 19:59 refs
複製代碼
咱們發現這個文件夾纔是 submodule
的真實 .git
文件夾, 咱們對於 submodule
的所作的 commit 信息也都保存在這裏.
git submodule
: 顯示全部 submodule
, 等同於git submodule status
添加 submodule 到現有項目
git submodule add -b <branch> --name <name> <repo-url> <local dir>
從當前項目移除 submodule
git submodule deinit -f <submodule_path>
rm -rf .git/modules/<submodule_path>
git rm -f <submodule_path>
複製含 submodule 項目到本地
git submodule init
to init the submodulesgit submodule update
to have the submodules on a detached HEAD或者直接執行 git clone --recurse-submodules <repo-url>
git submodule init
: 將本項目所依賴的 submodule
進行初始化
git submodule update
: 將本項目所依賴的 submodule
更新到本地最新版本
git submodule update --init
: 前面兩個命令的合併
git submodule update --init --recursive
: 前面三個命令的合集, --recursive
是爲了保證即便 submodule
又嵌套了 sub-submodule
, 也能夠被執行到. 這個命令比較全面, 會常用
git submodule update
: 更新 submodule 爲 superproject
本次 commit 所記錄的版本 (本地版本爲舊版本的話那麼就與舊版本保持同步!)
git submodule update --remote
: 更新 submodule 爲遠程項目的最新版本 (更爲經常使用!)
git submodule update --remote <submodule-name>
: 更新指定的 submodule 爲遠程的最新版本
git push --recurse-submodules=
check
: 檢查 submodule
是否有提交未推送, 若是有, 則使本次提交失敗on-demand
: 先推送 submodule 的更新, 而後推送主項目的更新(若是 submodule 推送失敗, 那麼推送任務直接終止)while
: 全部的 submodule
會被依次推送到遠端, 可是 superproject
將不會被推送no
: 與 while
相反, 只推送 superproject
, 不推送其餘 submodule
git pull --recurse-submodules
: 拉取全部子倉庫 (fetch) 並 merge 到所跟蹤的分支上
git diff --submodule
: 查看 submodule 全部改變
git submodule foreach '<arbitrary-command-to-run>'
: 對全部 submodule 執行命令, 很是有用, 如 git submodule foreach 'git checkout main'
superproject
在 git pull
以後 submodule
沒有切到最新節點?默認狀況下, git pull 命令會遞歸地抓取子模塊的更改 (fetch), 然而, 它不會將 submodule merge 到所跟蹤的分支上. 所以咱們還須要執行 git submodule update
.
若是咱們想一句話解決, 那麼可使用 git pull --recurse-submodule
, 這個能夠在拉取完 submodule 後再將其 merge 到所跟蹤的分支上.
若是咱們想讓 Git 老是以 --recurse-submodules
拉取, 能夠將配置選項 submodule.recurse
設置爲 true
. 具體命令爲 git config --global submodule.recurse true
. 此選項會讓 Git 爲全部支持 --recurse-submodules
的命令使用該選項 (除 clone 之外).
git pull --recurse-submodule
會讓咱們的pull命令遞歸地用於全部submodule
, 若是你的submodule
數量過多的話可能會等較長時間. 這時可使用git pull && git submodule update --init --recursive
一句話命令來只拉取更新了的submodule
並更新到最新commit
, 使用git config --global alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
爲此命令設置alias
submodule
沒有提交commit
的狀況下推送 superproject
的commit
若是咱們在主項目中提交併推送但並不推送子模塊上的改動, 其餘嘗試檢出咱們修改的人會遇到麻煩, 由於他們沒法獲得依賴的子模塊改動. 那些改動只存在於咱們本地的拷貝中.
爲了確保這不會發生, 咱們可讓 Git 在推送到主項目前檢查全部 submodule
是否已推送. git push
命令接受能夠設置爲 check
或 on-demand
的 --recurse-submodules
參數. 若是任何提交的 submodule
改動沒有推送那麼 check
選項會直接使 push 操做失敗.(此外還有 demand
, while
, no
選項, 參考前節命令列表進行理解)
爲了之後方便, 咱們能夠設置默認檢查 git config --global push.recurseSubmodules check
不少人用了 git submodule 後, 都發現每次 update 以後, submodule 中的 HEAD
都是 detached
狀態的, 即便本次 git checkout master
後, 下次更新仍然恢復原樣, 難道就沒有辦法使其固定在某個 branch
上嗎? 通過研究, 參考 stackoverflow 的答案, 我發現是能夠解決的.
問題的關鍵在於 .gitmodule
的配置:
[submodule "C"]
path = C
url = git@github.com:HanleyLee/C.git
update = rebase
[submodule "Cpp"]
path = Cpp
url = git@github.com:HanleyLee/Cpp.git
update = rebase
複製代碼
咱們須要添加 update = rebase
這行, 根據 git official 的說明
checkout
the commit recorded in the superproject will be checked out in the submodule on a detached HEAD.
If --force is specified, the submodule will be checked out (using git checkout --force), even if the commit specified in the index of the containing repository already matches the commit checked out in the submodule.
rebase
the current branch of the submodule will be rebased onto the commit recorded in the superproject.
merge
the commit recorded in the superproject will be merged into the current branch in the submodule.
複製代碼
submodule
的 update 有多種選擇, 默認狀況下是 checkout
, 其會根據 superproject
所記錄的 submodule
的 commit
進行 checkout. 相似於 git checkout 4eda5fgrd
, 這必然致使 submodule
的 HEAD
處於 detached
狀態. 解決辦法就是使用 rebase
(merge
也能夠), 這樣當咱們對 submodule
設置了一個初始的 branch
後, 其之後都只會在這個 branch 上對遠程的最新 commit
進行 rebase
, 不會致使 detached
狀態的產生.
以 submodule
的目錄爲 ./C/
爲例. 具體的解決步驟以下:
$ cd C/
$ git checkout main
$ cd ..
$ git config -f .gitmodules submodule.C.update rebase
複製代碼
此時, 之後再使用 git submodule update
就不會有 detached
狀態的產生了
另外, 在
.gitmodule
文件中也能夠指定 branch, 這裏指定的 branch 表示跟蹤的遠程倉庫的分支, 若是不指定, 則默認爲跟蹤遠程的HEAD
所指向的branch