在開發較複雜的軟體專案時 往往會依賴 其他的函式庫
當這些依賴 不能直接透過 套件管理程式 處理 而需要將依賴的原始碼放入專案中
你會如何處理?
假如這個第三方的函式庫 未來沒有機會更新的話
一個簡單的方式是 直接複製貼上第三方的函式庫到你的專案內
如果專案所依賴的第三方原始碼 未來有需要將它更新 甚至你要修改它 將它 backport 的需求
那麼簡單的複製貼上就不敷使用了
git 提供了幾種系統化管理內嵌 其他 git repo (所依賴的第三方程式庫) 的方式
- git submodule
- git subtree
在大部分的情形下 git subtree 比 git submoule 好用
但由於 git submodule 出現的比較早 文件也比較完整 也因此很常會遇到
使用 git submodule 內嵌其他 git repo 時 實際上 你是放入 另外一個 git repo 它與 container git repo 是兩個獨立的 git repo 因此你在操作 git 指令時 需要注意 你位於哪一個目錄
但在使用 git submodule 時 需要特別謹慎 否則容易會遇到 協作上 會出現不一致的情形
例如當你要
客製化 submodule 時 會需要 兩次 commit 兩次 push
在 submoulde 內 先進行 commit 並在 submodule 內 push 再到 container 進行 commit 並 push
如果少了某一個步驟 其他協作的人 將會拿不到你所進行的改動
而當 別人 發佈的改動 有更改到 submodle 時 ( 如上述的情形 ) 你要取得這些改動時 也需要注意 單只是在 container 輸入 git pull 並不會 更新 submole 的內容
實際上你需要輸入
git pull
git submodule sync --recursive
git submodule update --init --recursive
問題
如果你忘了這麼做 就在 container 進行了一次 commit 並且 push 那麼 其他的人在 submodule 的改動 就會在你這一次的 push 被退回 你的 repo 的 sub module 的狀態 ( 某一個 commit id )
這一篇文章 非常詳細的 介紹 git submodules (因此文章有點長)
什麼時候需要使用 會遇到什麼陷阱 提供實際的範例 可以進行練習
git submodule 常見情境
- 這邊我們將
內嵌 其他依賴函式庫 的函式庫稱為 container
依賴的函式庫稱為 module
Adding
Initial add:
>>> git submodule add <url> <path>
Coloning
Initial container clone:
>>> git clone --recursive <url> [<path>]
Grabbing updates inside a submodule
更新 子模組
1. cd path/to/module
2. git fetch
3. git checkout -q <commit-sha1>
(The -q is only there to spare us Git blabbering about how we’re ending up on a detached head. Usually this would be a healthy reminder, but on this one we know what we’re doing.)
4. cd -
5. git commit -am “Updated submodule X to: blah blah”
Grabbing container updates
更新 (內含 git子模組) git repo
git pull
git submodule sync --recursive
git submodule update --init --recursive
Updating a submodule inside container code
修改 子模組 內容 並發佈至 子模組上游專案 與 container git repo
1. git submodule update --remote --rebase -- path/to/module
2. cd path/to/module
3. Local work, testing, eventually staging
4. git commit -am “Update to central submodule: blah blah”
5. git push
6. cd -
7. git commit -am “Updated submodule X to: blah blah”
Permanently removing a submodule (1.7.8+)
移除 子模組
1. git submodule deinit path/to/module
2. git rm path/to/module
3. git commit -am “Removed submodule X”
git submodule 設定
diff.submodule = log
(so you get clearer container diffs when referenced submodule commits changed).
fetch.recurseSubmodules = on-demand
(so you are confident new referenced commits for known submodules get fetched with container updates).
status.submoduleSummary = true
(so git status gets useful again when a referenced submodule commit changed).
Misc
子模組的 .git 是一個檔案 內容長得像是
gitdir: ../../../.git/modules/vendor/plugins/demo
其內容紀錄 實際的 gitdir 在 container 的 .git/modules
Leftovers
git submodule foreach
git submodule status
git submodule summary
git mv on a 1.7.8+ submodule directory (one with a gitfile) does the right thing