git 子模組使用方法
- git 子模組使用方法
- 什麼情況下使用子模組
- 新增新的子模組
- 克隆含有子模組的倉庫
- 主倉庫更新子模組
- 拉取更新了子模組的主倉庫
- 在子模組上工作
- 參考來源
什麼情況下使用子模組
如果想要在開發的專案中引入另外一個專案。那麼除了直接將專案檔案複製到主倉庫目錄下以外,還可以選擇一種更優雅的方式,即本文所述的git子模組。這種方式可以將引入專案按照其本身的倉庫狀態進行管理。當引入專案有變更,不管是修復bug,還是更新功能,都可以方便的將其變更拉取到本地倉庫中。
新增新的子模組
首先需要在主倉庫中執行新增指令:
$ git submodule add https://github.com/Xsuns/xsuns_git_submodule_sub.git
拉取完畢後,主倉庫將出現.gitmodules
檔案和子倉庫的目錄(直接存入index中)。
$ git diff --cached
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..13fa722
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "xsuns_git_submodule_sub"]
+ path = xsuns_git_submodule_sub
+ url = https://github.com/Xsuns/xsuns_git_submodule_sub.git
diff --git a/xsuns_git_submodule_sub b/xsuns_git_submodule_sub
new file mode 160000
index 0000000..ba5bf6d
--- /dev/null
+++ b/xsuns_git_submodule_sub
@@ -0,0 +1 @@
+Subproject commit ba5bf6dad82173cded0d4fafe5e72c4a1643f611
其中記錄的三行分別為子倉庫的名稱、本地目錄和拉取地址,例子中的名稱就是xsuns_git_submodule_sub
, 本地目錄就是主倉庫根目錄下的./xsuns_git_submodule_sub
。
提交更改後,就完成了子模組的新增。
$ git commit -am '新增:增加子模組'
[main 2896730] 新增:增加子模組
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 xsuns_git_submodule_sub
$ git log -1
commit 28967303375194f32c92122b3e7e43dff2441d67 (HEAD -> main)
Author: Xsuns <lxyxsuns@gmail.com>
Date: Mon Apr 22 20:10:58 2024 +0800
新增:增加子模組
克隆含有子模組的倉庫
對於含有子模組的倉庫,需要使用遞迴克隆命令。
$ git clone --recurse-submodules https://github.com/Xsuns/xsuns_git_submodule_main.git
Cloning into 'xsuns_git_submodule_main'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.
Submodule 'xsuns_git_submodule_sub' (https://github.com/Xsuns/xsuns_git_submodule_sub.git) registered for path 'xsuns_git_submodule_sub'
Cloning into 'xsuns_git_submodule_sub' ...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.
Submodule path 'xsuns_git_submodule_sub': checked out 'ba5bf6dad82173cded0d4fafe5e72c4a1643f611'
如果忘記了,也無需刪除原倉庫,直接使用指令就可以遞迴更新。
ps. --recursive 可以遞迴更新子模組中巢狀的子模組,如果不使用此選項,會導致僅更新直接關聯的子模組。
$ git submodule update --init --recursive
Submodule 'xsuns_git_submodule_sub' (https://github.com/Xsuns/xsuns_git_submodule_sub.git) registered for path 'xsuns_git_submodule_sub'
Cloning into 'xsuns_git_submodule_sub' ...
Submodule path 'xsuns_git_submodule_sub': checked out 'ba5bf6dad82173cded0d4fafe5e72c4a1643f611'
主倉庫更新子模組
如果只想要同步子模組預設的最新分支,那麼只需要以下指令即可。
ps. 如果想要更改預設子模組遠端分支,可以在
.gitmodule
中子模組對應位置增加branch = target
。
$ git submodule update --remote
Submodule path 'xsuns_git_submodule_sub': checked out 'ed54345e540890f5ebd258327757de805f5e028c'
$ git diff --submodule
Submodule xsuns_git_submodule_sub ba5bf6d..ed54345:
> 修改:readme文件標題錯誤
同樣地,如果要將子模組變更儲存到主倉庫中,需要在主倉庫進行提交。
$ git commit -am '新增:更新子模組'
[main c057e0e] 新增:更新子模組
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log -p --submodule -1
commit c057e0e5dea6e369801c2fce168c62438a9e7c69 (HEAD -> main)
Author: Xsuns <lxyxsuns@gmail.com>
Date: Mon Apr 22 20:50:13 2024 +0800
新增:更新子模組
Submodule xsuns_git_submodule_sub ba5bf6d..ed54345:
> 修改:readme文件標題錯誤
如果你想更加自由,也可以進入子模組的目錄中執行git fetch
和git merge
進行拉取。
拉取更新了子模組的主倉庫
對於主倉庫的協作者,當主倉庫更新了子模組,僅僅使用git pull
是不夠的,它僅能修改主倉庫中記錄的子倉庫版本,不能自動檢出對應的版本。
$ git pull
Updating 2896730..c057e0e
Fast-forward
xsuns_git_submodule_sub | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: xsuns_git_submodule_sub (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
因此還需要如下指令。
$ git submodule update --init --recursive
Submodule path 'xsuns_git_submodule_sub': checked out 'ed54345e540890f5ebd258327757de805f5e028c'
ps.
init
可以在主倉庫增加了新的子模組時自動初始化並拉取,recursive
則可以更新子模組的巢狀子模組。
還有一種特殊情況是,主倉庫.gitmodules
中子模組的url發生了改變,你需要更新主倉庫引用,可以在git submodule update
呼叫同步指令。
$ git submodule sync --recursive
Synchronizing submodule url for 'xsuns_git_submodule_sub'
$ git submodule update --init --recursive
ps. 同樣地,如果你在本地修改了
.gitmodules
中的url,也要執行此同步指令
在子模組上工作
個人經驗來說,不建議在開發主倉庫的過程中,同步對子模組進行修改,特別是來自第三方的庫。因為本地子模組改動如果無法推送到子模組倉庫,會導致其他人拉取主倉庫時無法拉取到子模組的修改。這時唯一的解決辦法就只有對子模組倉庫進行fork
,並修改子模組url
為fork
後的倉庫,然後去維護此倉庫。
但如果你一定要這樣做,首先你需要進入子倉庫中檢出一個工作分支,因為在前幾個章節的操作後,子模組將屬於遊離態,不屬於任何分支。
$ git status
HEAD detached at ed54345
nothing to commit, working tree clean
$ git checkout main
Switched to branch 'main'
後續對子模組進行更新,需要加上選項--merge
或者--rebase
,如果忘記的話,子模組會重新變為遊離態,此時只要在子模組目錄下再次檢出工作分支進行手動merge
或者rebase
即可。
$ git submodule update --remote --merge
Updating ba5bf6d..ed54345
Fast-forward
readme.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Submodule path 'xsuns_git_submodule_sub': merged in 'ed54345e540890f5ebd258327757de805f5e028c'
如果你有許可權將子模組推送到對應倉庫,你可以在子模組目錄下直接進行推送,然後在推送主倉庫時使用選項--recurse-submodules
,將其設定為check
,這樣主倉庫推送時會進行檢查,在子倉庫改動未推送時推送失敗。
$ git push --recurse-submodules=check
The following submodule paths contain changes that can
not be found on any remote:
xsuns_git_submodule_sub
Please try
git push --recurse-submodules=on-demand
or cd to the path and use
git push
to push them to a remote.
fatal: Aborting.
推送失敗的提示中也有提到將選項--recurse-submodules
設定為on-demand
,git
將自動嘗試推送子模組。
$ git push --recurse-submodules=on-demand
Pushing submodule 'xsuns_git_submodule_sub'
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 301 bytes | 301.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/Xsuns/xsuns_git_submodule_sub.git
ed54345..2631874 main -> main
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 32 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 271 bytes | 271.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To https://github.com/Xsuns/xsuns_git_submodule_main.git
c057e0e..6718be9 main -> main
如果你在本地修改了子模組,遠端主倉庫的子模組也被人修改了,即子模組歷史出現分叉。此時git pull
拉取主倉庫時將無法順利合併.
$ git pull --recurse-submodules
Fetching submodule xsuns_git_submodule_sub
Failed to merge submodule xsuns_git_submodule_sub
CONFLICT (submodule): Merge conflict in xsuns_git_submodule_sub
Automatic merge failed; fix conflicts and then commit the result.
$ git diff
diff --cc xsuns_git_submodule_sub
index 7ab5f6c,9670894..0000000
--- a/xsuns_git_submodule_sub
+++ b/xsuns_git_submodule_sub
這時就需要進入子倉庫去進行手動合併,首先要根據第二個SHA1
建立一個新的分支去合併。
$ cd xsuns_git_submodule_sub
$ git branch try-merge 9670894
$ git merge try-merge
Auto-merging readme.md
CONFLICT (content): Merge conflict in readme.md
Automatic merge failed; fix conflicts and then commit the result.
此時得到了一個一般的合併衝突,然後就按照常規方式解決即可
$ git commit -am '合併:保留所有更改'
[main b342081] 合併:保留所有更改
$ cd ..
$ git diff
diff --cc xsuns_git_submodule_sub
index 7ab5f6c,9670894..0000000
--- a/xsuns_git_submodule_sub
+++ b/xsuns_git_submodule_sub
@@@ -1,1 -1,1 +1,1 @@@
- Subproject commit 7ab5f6cd6823c28650eb6f4c50c0d41686ba6f7e
-Subproject commit 96708949bbc90b7b721880546ac6e82d500ac80a
++Subproject commit b342081a9666aab88e3141c115bb79c1894446b9
$ git commit -am '合併:上游更改'
[main 7385bba] 合併:上游更改
參考來源
[1] Scott Chacon, Ben Straub. Pro Git Book 2nd Edition
[2] 主倉庫
[3] 子模組倉庫