git 子模块使用方法

git 子模块使用方法

前言

本文主要摘抄了Scott Chacon和Ben Straub的Pro Git Book第二版的相关章节,并按照章节自己进行了操作。

这本书写的非常好,还提供了免费的中文版,深入浅出讲解了git的使用和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 fetchgit 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,并修改子模块urlfork后的仓库,然后去维护此仓库。

但如果你一定要这样做,首先你需要进入子仓库中检出一个工作分支,因为在前几个章节的操作后,子模块将属于游离态,不属于任何分支。

$ 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-demandgit将自动尝试推送子模块。

$ 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] 子模块仓库

posted @ 2024-04-24 16:51  Xsuns  阅读(122)  评论(0)    收藏  举报