本文首发于我的个人Blog阿西BUG,欢迎大家批评指正
最近项目出现了新的需求,需要在一个Creator框架工程中,集成多个子游戏。
优点:所有游戏集中管理,避免出现一旦框架更改,所有子游戏工程需要全部改一遍
痛点:如果子游戏数量过多,会导致项目工程巨大,打开编译等速度过慢
目的:框架自身和各子游戏需要能够独立更新,互不影响
引用一段《Git权威指南》的话:
项目的版本库在某些情况虾需要引用其他版本库中的文件,例如公司积累了一套常用的函数库,被多个项目调用,显然这个函数库的代码不能直接放到某个项目的代码中,而是要独立为一个代码库,那么其他项目要调用公共函数库该如何处理呢?分别把公共函数库的文件拷贝到各自的项目中会造成冗余,丢弃了公共函数库的维护历史,这显然不是好的方法。
Git Submodule功能刚刚开始学习可能觉得有点怪异,所以本文会把每一步的操作的命令和结果都用代码的形式展现给大家,以便更好的理解。
说明:本例采用两个项目以及两个公共类库演示对submodule的操作。
C:\Users\TAL-YINGYU\Desktop
λ mkdir repo
C:\Users\TAL-YINGYU\Desktop
λ cd repo\
创建本地仓库:
C:\Users\TAL-YINGYU\Desktop\repo
λ git --git-dir=lib1.git init --bare
Initialized empty Git repository in C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/
C:\Users\TAL-YINGYU\Desktop\repo
λ git --git-dir=lib2.git init --bare
Initialized empty Git repository in C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/
C:\Users\TAL-YINGYU\Desktop\repo
λ git --git-dir=project1.git init --bare
Initialized empty Git repository in C:/Users/TAL-YINGYU/Desktop/repo/project1.git/
C:\Users\TAL-YINGYU\Desktop\repo
λ git --git-dir=project2.git init --bare
Initialized empty Git repository in C:/Users/TAL-YINGYU/Desktop/repo/project2.git/
初始化工作区:
C:\Users\TAL-YINGYU\Desktop
λ mkdir workSpaceLocal
C:\Users\TAL-YINGYU\Desktop
λ cd workSpaceLocal\
初始化project1:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ git clone ..\repo\project
project1.git\ project2.git\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ git clone ..\repo\project1.git\
Cloning into 'project1'...
warning: You appear to have cloned an empty repository.
done.
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ cd project1\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ echo "project1" > project1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ ls
project1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git add project1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git commit -m "init project1"
[master (root-commit) 89fcc18] init project1
1 file changed, 1 insertion(+)
create mode 100644 project1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 227 bytes | 227.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project1.git\
* [new branch] master -> master
初始化project2:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ git clone ..\repo\project2.git\
Cloning into 'project2'...
warning: You appear to have cloned an empty repository.
done.
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ cd project2\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ echo "project2" > project2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ ls
project2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git add project2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git commit -m "init project2"
[master (root-commit) fdefcbe] init project2
1 file changed, 1 insertion(+)
create mode 100644 project2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 229 bytes | 229.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project2.git\
* [new branch] master -> master
初始化lib1:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ git clone ..\repo\lib1.git\
Cloning into 'lib1'...
warning: You appear to have cloned an empty repository.
done.
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ cd lib1\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib1 (master -> origin)
λ echo "I'm lib1." > lib1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib1 (master -> origin)
λ git add lib1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib1 (master -> origin)
λ git commit -m "init lib1"
[master (root-commit) 1ffa3a9] init lib1
1 file changed, 1 insertion(+)
create mode 100644 lib1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib1 (master -> origin)
λ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 222 bytes | 222.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\lib1.git\
* [new branch] master -> master
初始化lib2:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib1 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ git clone ..\repo\lib2.git\
Cloning into 'lib2'...
warning: You appear to have cloned an empty repository.
done.
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ cd lib2\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib2 (master -> origin)
λ echo "I'm lib 2" > lib2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib2 (master -> origin)
λ git add lib2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib2 (master -> origin)
λ git commit -m "init lib2"
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib2 (master -> origin)
λ git commit -m "init lib2"
[master (root-commit) 1f05672] init lib2
1 file changed, 1 insertion(+)
create mode 100644 lib2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib2 (master -> origin)
λ git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 222 bytes | 222.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\lib2.git\
* [new branch] master -> master
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\lib2 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ cd project1\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git submodule add C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/project1/lib1'...
done.
warning: LF will be replaced by CRLF in .gitmodules.
The file will have its original line endings in your working directory
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git submodule add C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/project1/lib2'...
done.
warning: LF will be replaced by CRLF in .gitmodules.
The file will have its original line endings in your working directory
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ ls
lib1/ lib2/ project1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: .gitmodules
new file: lib1
new file: lib2
#看一看公共类库内容是不是刚才我们初始化的
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cat lib1\
.git lib1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cat lib1\lib1-infos.md
"I'm lib1."
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cat lib2\lib2-infos.md
"I'm lib 2"
到目前为止,我们已经使用git submodule add
命令为project1添加了两个公共类库,通过查看当前状态发现添加了一个新的文件(.gitmodules)和两个问题件夹(lib1, lib2),那么.gitmodules文件是做什么用的呢?我们来看一下内容:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cat .gitmodules
[submodule "lib1"]
path = lib1
url = C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/
[submodule "lib2"]
path = lib2
url = C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/
原来这里面存储的都是每个submodule的相关信息,知道在当前项目的位置以及远程仓库地址
ok,现在我们把project1的更改提交到远程仓库
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git commit -m "add submodules[lib1,lib2] to project1"
[master b0389b8] add submodules[lib1,lib2] to project1
3 files changed, 8 insertions(+)
create mode 100644 .gitmodules
create mode 160000 lib1
create mode 160000 lib2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 12 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 449 bytes | 449.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project1.git\
89fcc18..b0389b8 master -> master
那么问题来了,如果一个项目中已经引入了公共类库,项目组其他成员如何clone这个项目呢?
模拟组员m:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ git clone ..\repo\project1.git project1-b
Cloning into 'project1-b'...
done.
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ ls
lib1/ lib2/ project1/ project1-b/ project2/
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ cd project1-b\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git submodule
-1ffa3a92c147a2d09fadbc5129389cc6fd1b95cb lib1
-1f05672f7ff92395fcbbe325ab2cda7405da40e8 lib2
可以看得出submodule的状态是hash码和文件目录,但是前面有一个 - 号,这表示这个子模块还没有被检出
ok,我们检出project1-b的submodule
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git submodule init
Submodule 'lib1' (C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/) registered for path 'lib1'
Submodule 'lib2' (C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/) registered for path 'lib2'
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git submodule update
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/project1-b/lib1'...
done.
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/project1-b/lib2'...
done.
Submodule path 'lib1': checked out '1ffa3a92c147a2d09fadbc5129389cc6fd1b95cb'
Submodule path 'lib2': checked out '1f05672f7ff92395fcbbe325ab2cda7405da40e8'
我们来看下.git/config
文件的内容
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ cat .git/config
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[remote "origin"]
url = C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\\repo\\project1.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[submodule "lib1"]
active = true
url = C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/
[submodule "lib2"]
active = true
url = C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/
可以看到里面有最新的submodule信息
我们来验证下本地项目中公共类库是否存在:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ cat lib1\lib1-infos.md lib2\lib2-infos.md
"I'm lib1."
"I'm lib 2"
继续模拟组员m修改submodule的内容
先看下当前submodule的状态
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ cd lib1\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\lib1 (HEAD detached at 1ffa3a9 -> origin)
λ git status
HEAD detached at 1ffa3a9
nothing to commit, working tree clean
这个时候HEAD直接指向一个commit,Git会警告我们出现了所谓的detached HEAD现象。表示当前git状态不属于任何一个分支。
那么为什么会出现这个情况呢? 继续往下看:
git对于submodule有特殊的处理方式,在一个主项目中引入submoudle的时候,其实git做了3件事:
在project1中push了之后,其实就是执行了更新引用的commit id,然后project1-b在clone的时候获取到了submodule的commit id,然后执行git submodule update的时候,git就根据gitlink获取submodule的commit id,最后获取submodule的文件,所以clone之后不在任何分支上,但是master分支的commit id和HEAD保持一致。
现在我们要先切换到master分支:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\lib1 (HEAD detached at 1ffa3a9 -> origin)
λ git checkout master
Previous HEAD position was 1ffa3a9 init lib1
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
组员m修改lib1:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\lib1 (master -> origin)
λ cat lib1-infos.md
"I'm lib1."
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\lib1 (master -> origin)
λ echo "modified by developer m" > lib1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\lib1 (master -> origin)
λ cat lib1-infos.md
"modified by developer m"
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\lib1 (master -> origin)
λ git commit -a -m "modify lib1-infos.md by developer m"
[master f220249] modify lib1-infos.md by developer m
1 file changed, 1 insertion(+), 1 deletion(-)
在主项目中将刚刚修改的submodule提交会稍微繁琐一些。
在push之前,我们先看看project1-b的状态
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\lib1 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib1 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
lib1(new commits)表示lib1有新的提交,这个比较特殊。
再看看project1-b主项目的变化
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git diff
diff --git a/lib1 b/lib1
index 1ffa3a9..f220249 160000
--- a/lib1
+++ b/lib1
@@ -1 +1 @@
-Subproject commit 1ffa3a92c147a2d09fadbc5129389cc6fd1b95cb
+Subproject commit f220249eb39266a4fdc8e46a1164613a953ac3a4
从diff的结果可以看出,lib1的commit id从原来的1ffa3a92c147a2d09fadbc5129389cc6fd1b95cb更新为f220249eb39266a4fdc8e46a1164613a953ac3a4
注意: 这个时候,如果我们执行了git submodule update
操作,那么lib1会还原到1ffa3a92c147a2d09fadbc5129389cc6fd1b95cb
这样的话,我们刚刚的修改是不是就丢失了呢?
其实并不会,因为刚刚我们已经提交到了master分支,只需要再执行git checkout master
就可以了
现在我们把lib1的修改提交到仓库
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ cd lib1\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\lib1 (master -> origin)
λ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 291 bytes | 291.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/
dd43aec..f220249 master -> master
千万不要以为到这里就结束了,后面还有很重要的一步,提交project1-b引用的submodule的commit id:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git add .
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git commit -m "update lib1 to lastest commit id"
[master 3a6818e] update lib1 to lastest commit id
1 file changed, 1 insertion(+), 1 deletion(-)
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 253 bytes | 253.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project1.git
b0389b8..3a6818e master -> master
OK,现在大功告成,我们已经成功的在项目project1-b中修改了公共仓库lib1,并且把最新的lib1的commit id提交到了仓库。
接下来我们来看看project1怎么获取submodule。
首先进入project1的目录
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ cd ..\project1\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git pull
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 2 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
From C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project1.git\
b0389b8..3a6818e master -> origin/master
Fetching submodule lib1
From C:/Users/TAL-YINGYU/Desktop/repo/lib1
1ffa3a9..f220249 master -> origin/master
Updating b0389b8..3a6818e
Fast-forward
lib1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib1 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
可以看出,我运行了git pull
和git status
获取了远程仓库最新的源码,并且看到本地的状态为modified,这是为什么呢?
我们来比较一下
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git diff
diff --git a/lib1 b/lib1
index f220249..1ffa3a9 160000
--- a/lib1
+++ b/lib1
@@ -1 +1 @@
-Subproject commit f220249eb39266a4fdc8e46a1164613a953ac3a4
+Subproject commit 1ffa3a92c147a2d09fadbc5129389cc6fd1b95cb
从比较结果来看,是因为submodule的commit id更改了,我们前面提到,主项目更新submodule的内容时,首先要提交submodule的内容,然后再更新主项目中引用的submodule的commit id。
现在我们看到的不同,就是因为刚才更改了project1-b的submodule commit id。
ok,我们来更新一下project1的submodules:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git submodule update
Submodule path 'lib1': checked out 'f220249eb39266a4fdc8e46a1164613a953ac3a4'
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
话不多说,直接上代码:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cd ..\project2\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git submodule add C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/project2/lib1'...
done.
warning: LF will be replaced by CRLF in .gitmodules.
The file will have its original line endings in your working directory
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git submodule add C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/project2/lib2'...
done.
warning: LF will be replaced by CRLF in .gitmodules.
The file will have its original line endings in your working directory
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ ls
lib1/ lib2/ project2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git submodule init
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: .gitmodules
new file: lib1
new file: lib2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git commit -a -m "add submodules(lib1 and lib2)"
[master 12516ae] add submodules(lib1 and lib2)
3 files changed, 8 insertions(+)
create mode 100644 .gitmodules
create mode 160000 lib1
create mode 160000 lib2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 12 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 440 bytes | 440.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project2.git\
fdefcbe..12516ae master -> master
相信上面的代码都很熟悉了,就不过多展开了。
我们假如,组员n同时负责peoject1和project2,有可能出现peoject1的某个时候发现lib1或者lib2有个bug,并且这个bug可能会影响很多需求模块,那么我们应该怎么办呢?
现在假设需求如下:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ cd lib1\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib1 (master -> origin)
λ echo "lib1 README" > README
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib1 (master -> origin)
λ ls
lib1-infos.md README
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib1 (master -> origin)
λ git add README
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib1 (master -> origin)
λ git commit -m "add file README"
[master 139f263] add file README
1 file changed, 1 insertion(+)
create mode 100644 README
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib1 (master -> origin)
λ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 297 bytes | 297.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/
f220249..139f263 master -> master
前面的文章提到,这个时候我们只完成了第一步,还有很重要的更新lib1的commit id 一步:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib1 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib1 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git add lib1
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git commit -m "update lib1 to lastest commit id"
[master a29cc13] update lib1 to lastest commit id
1 file changed, 1 insertion(+), 1 deletion(-)
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 251 bytes | 251.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project2.git\
12516ae..a29cc13 master -> master
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ cd lib2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib2 (master -> origin)
λ echo "学习git submodule的修改,同步功能" >> lib2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib2 (master -> origin)
λ git add lib2-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib2 (master -> origin)
λ git commit -m "add descript sentence"
[master 5d24031] add descript sentence
1 file changed, 1 insertion(+)
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib2 (master -> origin)
λ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 307 bytes | 307.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/
1f05672..5d24031 master -> master
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2\lib2 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib2 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git add lib2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git commit -m "update lib2 to lastest commit id"
[master 1f0f0f4] update lib2 to lastest commit id
1 file changed, 1 insertion(+), 1 deletion(-)
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 253 bytes | 253.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project2.git\
a29cc13..1f0f0f4 master -> master
现在project2里lib1和lib2的更改已经提交了,我们来更新project1的submodules(实际情况可能会有很多项目需要更新):
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ cd ..\project1
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git pull
Already up to date.
什么情况???明明在project2中lib1和lib2已经更新了呀!为什么project1执行git pull
得不到任何改动呢?
我们来分析一下,首先来看下project1和project2的submodule状态:
#project1
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git submodule
f220249eb39266a4fdc8e46a1164613a953ac3a4 lib1 (remotes/origin/HEAD)
1f05672f7ff92395fcbbe325ab2cda7405da40e8 lib2 (heads/master)
#project2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project2 (master -> origin)
λ git submodule
139f263f4c444d140d3ca3efe7d949f4b2a5b77b lib1 (heads/master)
5d2403124046f72b364b0355717013c8221d1fbd lib2 (heads/master)
区别:
还记得刚才在project2中修改的时候,我们都是在master分支上操作的,那么目前project1中的lib1不在任何分支,所以我们先切换到master分支:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cd lib1
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1\lib1 (HEAD detached at f220249 -> origin)
λ git checkout master
Previous HEAD position was f220249 modify lib1-infos.md by developer m
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
果然切换分支就发现了我们当前已经落后了两个commit( 我们刚刚在project2中push了 2 次 )了!
那么还等什么?赶紧更新啊
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1\lib1 (master -> origin)
λ git pull
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From C:/Users/TAL-YINGYU/Desktop/repo/lib1
f220249..139f263 master -> origin/master
Updating 1ffa3a9..139f263
Fast-forward
README | 1 +
lib1-infos.md | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 README
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1\lib1 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib1 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git diff
diff --git a/lib1 b/lib1
index f220249..139f263 160000
--- a/lib1
+++ b/lib1
@@ -1 +1 @@
-Subproject commit f220249eb39266a4fdc8e46a1164613a953ac3a4
+Subproject commit 139f263f4c444d140d3ca3efe7d949f4b2a5b77b
ok,可以看得出lib1的commit id已经随着pull的操作和project2目前的状态一致,说明已经同步到了。
那么我们用同样的办法更新lib2吧:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cd lib2\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1\lib2 (master -> origin)
λ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From C:/Users/TAL-YINGYU/Desktop/repo/lib2
1f05672..5d24031 master -> origin/master
Updating 1f05672..5d24031
Fast-forward
lib2-infos.md | 1 +
1 file changed, 1 insertion(+)
在1.7.1中,我们更新了project1中lib1和lib2的最新版本,现在要把最新的commit id 保存在project1中,以保持最新的引用
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1\lib2 (master -> origin)
λ cd ..
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib1 (new commits)
modified: lib2 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git commit -a -m "update lib1 and lib2 commit id to lastest version"
[master b5208b3] update lib
2 files changed, 2 insertions(+), 2 deletions(-)
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 326 bytes | 326.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project1.git\
3a6818e..b5208b3 master -> master
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1 (master -> origin)
λ cd C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git pull
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 2 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
From C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project1
3a6818e..b5208b3 master -> origin/master
Fetching submodule lib1
From C:/Users/TAL-YINGYU/Desktop/repo/lib1
f220249..139f263 master -> origin/master
Fetching submodule lib2
From C:/Users/TAL-YINGYU/Desktop/repo/lib2
1f05672..5d24031 master -> origin/master
Updating 3a6818e..b5208b3
Fast-forward
lib1 | 2 +-
lib2 | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib1 (new commits)
modified: lib2 (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
可以看到,git判断出lib1和lib2有更新,这就是根据submodule的commit id引用
那么现在我们怎么更新呢?难道还像刚才一样按照子模块一个一个的git checkout master
,然后git pull
?
这里只有两个子模块,如果多了呢?
实际上有一个更简便的命令可以替代上述操作:
git submodule foreach git pull
循环
.gitsubmodules
文件里子模块的路径,依次进行子模块更新
同理我们可以模仿上述命令
git submodule foreach ls -l
可以列出每个子模块的文件列表
git submodule foreach git checkout master
每个子模块切换到master
先后执行下面两条语句即可
切换到master
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git submodule foreach git checkout master
Entering 'lib1'
Switched to branch 'master'
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
Entering 'lib2'
Switched to branch 'master'
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
拉取
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-b (master -> origin)
λ git submodule foreach git pull
Entering 'lib1'
Updating f220249..139f263
Fast-forward
README | 1 +
1 file changed, 1 insertion(+)
create mode 100644 README
Entering 'lib2'
Updating 1f05672..5d24031
Fast-forward
lib2-infos.md | 1 +
1 file changed, 1 insertion(+)
git clone /path/to/repo/xxx.git
git submodule init
git submodule update
这时候新员工心里肯定嘀咕:这是什么操作??这么麻烦!!
我们可以一行代码搞定
git clone --recursive /path/to/repo/xxx.git
解释一下:
--recursive
:可以在clone项目的同时clone关联的submodules
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ git clone --recursive ..\repo\project2.git\ prject2-auto-clone-submodules
Cloning into 'prject2-auto-clone-submodules'...
done.
Submodule 'lib1' (C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/) registered for path 'lib1'
Submodule 'lib2' (C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/) registered for path 'lib2'
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/prject2-auto-clone-submodules/lib1'...
done.
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/prject2-auto-clone-submodules/lib2'...
done.
Submodule path 'lib1': checked out '139f263f4c444d140d3ca3efe7d949f4b2a5b77b'
Submodule path 'lib2': checked out '5d2403124046f72b364b0355717013c8221d1fbd'
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ ls
lib1/ lib2/ prject2-auto-clone-submodules/ project1/ project1-b/ project2/
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ ls prject2-auto-clone-submodules\
lib1/ lib2/ project2-infos.md
是不是爽歪歪~~~
重新clone一个项目来练习移除submodule
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ git clone --recursive ..\repo\project1.git\ project1-remove-submodules
Cloning into 'project1-remove-submodules'...
done.
Submodule 'lib1' (C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/) registered for path 'lib1'
Submodule 'lib2' (C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/) registered for path 'lib2'
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/project1-remove-submodules/lib1'...
done.
Cloning into 'C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/project1-remove-submodules/lib2'...
done.
Submodule path 'lib1': checked out '139f263f4c444d140d3ca3efe7d949f4b2a5b77b'
Submodule path 'lib2': checked out '5d2403124046f72b364b0355717013c8221d1fbd'
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal
λ cd project1-remove-submodules\
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ git rm -f --cached lib1
rm 'lib1'
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ git rm -f --cached lib2
rm 'lib2'
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ rm -f lib1
rm: cannot remove 'lib1': Is a directory
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ rm -rf lib1
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ rm -rf lib2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ ls
project1-infos.md
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ rm -rf .gitmodules
因为这里只有两个子模块,所以直接删除了.gitmodules
,如果只需要删除某一个子模块,则在文件中删除对应的配置即可。
.git/config
的submodule配置源文件:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ cat .git\config
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[submodule]
active = .
[remote "origin"]
url = C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\\repo\\project1.git\\
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[submodule "lib1"]
url = C:/Users/TAL-YINGYU/Desktop/repo/lib1.git/
[submodule "lib2"]
url = C:/Users/TAL-YINGYU/Desktop/repo/lib2.git/
删除后:
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ cat .git\config
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[remote "origin"]
url = C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\\repo\\project1.git\\
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)
deleted: lib1
deleted: lib2
Changes not staged for commit:
(use "git add/rm ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
deleted: .gitmodules
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ git add .gitmodules
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)
deleted: .gitmodules
deleted: lib1
deleted: lib2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ git commit -m "delete submodules"
[master b26add0] delete submodules
3 files changed, 8 deletions(-)
delete mode 100644 .gitmodules
delete mode 160000 lib1
delete mode 160000 lib2
C:\Users\TAL-YINGYU\Desktop\workSpaceLocal\project1-remove-submodules (master -> origin)
λ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (1/1), done.
Writing objects: 100% (2/2), 243 bytes | 243.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To C:/Users/TAL-YINGYU/Desktop/workSpaceLocal/..\repo\project1.git\
b5208b3..b26add0 master -> master
零零碎碎总算写完了,写的有点啰嗦。
不过实际上,确实需要经过自己练习,才能熟练掌握git submodule的使用技巧。
如果你有更好的方法,希望能留下宝贵的意见。
git submodule foreach xxx
可真是个万金油拉取所有子模块
git submodule foreach git pull
git submodule foreach --recursive git submodule init
git submodule foreach --recursive git submodule update