使用 Git 管理原始碼,進行子模組操作時,此文可作為參考(Lookup Cheat-Sheet)
合適的場景:
與其使用 submodules,不如使用 subtrees 的場景:
Submodules | Subtrees |
---|---|
更復雜 (尤其對初學者) | 更簡單 |
另一個倉儲的一個提交參照連結 | 程式碼提交會合併到器皿專案的提交歷史中 |
能夠單獨被存取(存在於中心伺服器中的獨立倉儲) | 去中心化(直接通過器皿專案存取) |
需要更多的步驟來操作 | 克隆,拉取,推播都與之前相同(當然也有針對子樹的命令) |
不佔用器皿專案倉儲大小 | 直接佔用器皿專案倉儲容量 |
# 新增子模組並跟蹤名稱為 branch_name 的分支,不加 -b 將會對後續子模組的跟蹤產生影響
git submodule add -b branch_name URL_to_Git_repo optional_directory_path
為子模組設定分支。除了 git submodule add
時使用 -b
引數設定以外,還可以通過修改 .gitmodules 檔案更改。
設計好倉儲內子模組的存放路徑。除了 git submodule add
時通過 optional_directory_name
設定以外,還可以通過修改 .gitmodules 檔案更改。
注:除了直接用文字編輯器修改 .gitmodules 檔案以外,還可以通過命令進行修改(參考如下範例)。
# 檢視現有的 submodule 屬性
git config --file=.gitmodules -l
# 修改 submodule 屬性
git config --file=.gitmodules submodule.modulename.branch branch_name
git config --file=.gitmodules submodule.modulename.path path/to/submodule
git config --file=.gitmodules submodule.modulename.url URL_to_Git_repo
# 克隆倉儲時,同時克隆倉儲的子模組,相當於進行了子模組的初始化和更新
git clone --recursive URL_to_Git_repo
# 克隆倉儲後,初始化並更新子模組,子模組就會參照子模組倉儲預設分支下最新一次提交
git submodule update --init
# 子模組設定變更、子模組變更(新增、更名、刪除等)
git pull
git submodule sync
git submodule update --init
# 簽出分支,使子模組具備拉取最新修改並直接連通父倉儲推播的能力
git submodule foreach "git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo main)"
# 子模組遠端有更新,需要獲取。以下命令等同於進入子模組目錄執行 `git pull`。如果本地有未提交的修改,均需要合併操作,有衝突無法合併則獲取會失敗。
git submodule update --remote --merge
# 推播程式碼到遠端分支
git push --recurse-submodules=on-demand
使用 detached HEAD,子模組連結的是提交 commit 的雜湊值 hash。無法拉取或提交程式碼,只能通過更新倉儲的子模組版本,再通過 git submodule update
命令更新子模組。
# 從 .git/config 中刪除子模組
git submodule deinit -f path/to/submodule
# 從器皿專案(父專案)的 .git/modules 目錄中刪除子模組資料夾
rm -rf .git/modules/path/to/submodule
# 從 .gitmodules 中去掉子模組入口並刪除子模組所在物理路徑 path/to/submodule 下的所有檔案
git rm -f path/to/submodule
# 為所有子模組執行 git 命令,-q 引數靜默模式,--recursive 迴圈操作所有子模組(包括子模組的子模組參照)
git submodule foreach "git command"
# 更新子模組
# --remote 從子模組遠端分支更新(最新),不加此引數則從父倉儲的子模組版本連結更新(對應提交雜湊值)
# --recursive 迴圈操作所有子模組(包括子模組的子模組參照)
# --merge,合併遠端分支的修改,有衝突需要解決衝突。不會 detach HEAD。
# --rebase,復原本地提交,臨時儲存並保留本地修改,應用遠端修改,再將本地修改合併,合併時同樣需要解決衝突。不會 detach HEAD。
git submodule update
注:對於 --remote 而言
- remote 使用
submodule.<name>.branch=branch_name
來確認分支名稱,若未設定則使用遠端倉儲設定的預設分支。再通過讀取子模組branch.branch_name.remote=origin
來確認從遠端分支獲取的地址和 HEAD,預設為 detached HEAD,對應remotes/origin/HEAD
,獲取該分支最新提交。- 如果本地子模組簽出了某個分支,則 HEAD attached 到對應分支,即
branch.branch_name.merge=refs/heads/branch_name
。- 如果本地子模組當前的 HEAD commit hash 和
submodule.<name>.branch
中的值不一致的話,會導致子模組被 detach HEAD,不再追蹤某個分支,而是對映到submodule.<name>.branch
最新的提交上。以上通常存在兩種情況,第一種是 HEAD 和submodule.<name>.branch
分支本身就不同。第二種是本地子模組的提交落後於submodule.<name>.branch
最新的提交。通過新增 --merge 引數可以避免 detachment。- 如果子模組有本地提交併與遠端提交產生了衝突,需要用 --rebase 引數來解決。
- 子模組內使用
git pull
直接使用的是對應分支的 HEAD,為branch.branch_name.merge=refs/heads/branch_name
。對應上述第2點,和一個簽出 branch_name 分支後 HEAD attach 到 heads/branch_name 的子模組的 HEAD 是一致的。
# 檢視現有 git 設定
git config --global -l
# 修改現有 git 設定
git config --global status.submoduleSummary true
git config --global diff.submodule log
git config --global alias.spush "push --recurse-submodules=on-demand"
[status]
submoduleSummary = true
[diff]
submodule = log
[alias]
sdiff = "!git diff && git submodule foreach 'git diff'"
spull = "!git pull && git submodule sync && git submodule update --init"
smerge = "submodule update --remote --merge"
srebase = "submodule update --remote --rebase"
scheckout = "submodule foreach 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo main) && git pull'"
spush = "push --recurse-submodules=on-demand"