軟體專案的開發往往會依賴 其他第三方的函式庫
當這些依賴 不能透過 套件管理程式 處理 而需要將依賴的原始碼放入專案中
你會如何處理?
假如這個第三方的函式庫 未來沒有機會更新的話
一個簡單的方式是 直接複製貼上第三方的函式庫到你的專案內
如果專案所依賴的第三方原始碼 未來有需要將它更新 甚至你要修改它 將它 backport 的需求
那麼簡單的複製貼上就不敷使用了
git 提供了幾種系統化管理內嵌 其他 git repo (所依賴的第三方程式庫) 的方式
- git submodule
- git subtree
在大部分的情形下 git subtree 比 git submoule 好用
但由於 git submodule 出現的比較早 文件也比較完整 也因此很常會遇到
相比之下 git subtree 的文件說明就比較少
git 的 subtree 的使用 有好幾種不同的方式
第一種是 手動
手動的方式 主要是透過 git 原生的指令來管理 內嵌 第三方 函式庫
其中最常使用到的會是
- merge
- cherry-pick
- read-tree
這種做法 在管理 repo 的 history, branch 上 自由度最高 最靈活的
第二種
git 在 1.7.11 之後 新增了 git-subtree.sh 的 script
讓我們多了幾個指令可以操作
- git subtree pull
- git subtree push
使用 git subtree 會帶來一些限制 像是如何控制 被引入的 git repo 會 git history 的影響 ( 要不要使用 --squash 參數 )
當你需要 backport 時 你不能挑選想要的部分 backport 要不就是 全部相關的 commit backport 要不就是沒有
第三種是
這邊我們將
內嵌 其他依賴函式庫 的函式庫稱為 container 依賴的函式庫稱為 module
git subtree 與 git submodule 一個很大的不同是
- 使用 git submodule 的方案時
- container 與 module 各自都是獨立的 git repo
而在 git subtree 的方案 你就只有一個 git repo
因此 使用時需要注意
由於只有一個 git repo git 並不知道 哪些部份是屬於 subtee 哪些部份不是
git submodle 並不會遇到這個問題 因為各自都是一個獨立的 git repo 有各自的 history
因此關於這個部分的關係 就必須要記錄在 說明文件 裡頭
而只有一個 repo 也有它的優點在 它就會是一個完整可以 build 的專案
而相應於 git submodule 如果相依的依賴 消失的話 專案就無法開發
除非你 本地端 有保留 各子模組的 history 再透過它重新建立新的 remote repo
這一篇文章 非常詳細的介紹 git subtree 內有實際的範例 可以下載進行練習
git subtree 使用情境 (手動使用的方法)
Adding a subtree
git remote add plugin <url>
git fetch plugin
git read-tree \
--prefix=vendor/plugins/demo -u plugin/master
git commit \
-m "Added demo plugin subtree in vendor/plugins/demo"
Grabbing/updating a repo that uses subtrees
就只是 你所熟知的 pull, clone
因為 git subtree 的架構下 就只有一個 git repo
假如採用 git submodule 的方案
協作者需要輸入
git fetch
git submodule sync --recursive
git submodule update --init --recursive
如果 協作者 是 clone 一份新的 需要輸入
git clone --recursive
Getting an update from the subtree’s remote
git fetch plugin
git merge -s subtree --squash \
plugin/master
git commit -m "Updated the plugin"
大部分 git 會知道 哪些是 subtree 哪些不是 如果使用了
git merge -s subtree --squash plugin/master
遇到問題
先 reset the merge 明確使用
git merge -X subtree=vendor/plugins/demo --squash plugin/master
Updating a subtree in-place in the container
Backporting to the subtree’s remote
- Commits touching only the subtree, intended for backport (e.g. fixes);
- Commits touching only container code;
- Commits touching both container and subtree code, the latter part being intended for backport;
- Commits touching only the subtree, in a container-specific way that is not to be backported.
四種可能的情境
- commit 只修改 subtree 的 code 此 commit 要送回給 subtree 上游
- commit 只有 container 的 code
(3) commit 修改 container 與 subtree 的 code 此 commit 中與 subtree 相關的部分 要送回給 subtree 上游
(4) commit 修改 container 與 subtree 的 code 此 commit 中與 subtree 相關的部分 沒有要送回給 subtree 上游 是專門客製化給 container
git checkout -b backport-plugin \
plugin/master
Now let’s cherry-pick the commits we’re interested in (adding a -x into the mix so the commit message has extra lines detailing the source for each cherry pick).
git cherry-pick -x master~3
git cherry-pick -x \
--strategy=subtree master^
git push
Removing a subtree
實際上 git 並不知道 哪個部分是 subtree 哪個部分不是 現在就只有一份 git repo
就把你不要的那部分
用 git rm 移除掉
Turning a directory into a subtree
git checkout -b split-plugin
git filter-branch \
--subdirectory-filter lib/plugins/myown
git remote add myown <url>
git push -u myown split-plugin:master
git subtree git submodule 比較
術語 第三方程式碼 稱為 module 使用第三方程式碼的專案 稱為 container
有時候 module 的程式碼 只能放在 container 專案下 才能測試它是否 可以正常運行
在這個情形下 你就被迫 或你就需要 讓你的 container 專案 與 subtree module 專案 的程式碼一起演化 直到某個時間點 你可以把 對 (subtree) 專案的修改 送回去 上游
而當你對 (subtree) module 針對 container 的專案進行客製化 你不需要 分成兩個 commit ( 當你使用 git submodule 時 不建議你 混合 container 與 module 的改變 一起 commit )
你只需要做一個 commit
而當你想要把 對 (subtree) module 的修改 送回去上游時 git 的指令會自己分辨 哪些是針對 (subtree) module 的部分
git subtree 比 git submodule 一個比較好的地方是 它允許 你對 (subtree) module 針對 container 的專案進行客製化 而不需要將這些改變送回給上游 (而 git submodule 卻需要送回去給上游 而當你的修改是 根據 container 客製化時 卻又不適合貢獻回去 但如果你不 push 回去 其他協作者又拿不到這個改動 這時候你就需要另外再開一個 repo 放至第三方的程式碼 由此可知 git subtree 是不太適合 對專案進行客製化的情境)