GIT 支持子模块,所谓 GIT 子模块,即某个项目需要管理的模块数目太多,而各个模块需要不同的人或团队维护,此时就需要在GIT中引入子模块。GIT 引入子模块后,其本身的上游代码提交历史依然可以保存下来,并且避免了在上游代码发生变更时本地的定制代码归并(Merge)困难。
新建带子模块的项目 PyDemo
我们举一个简单的例子说明上述问题:假设你开发了一个项目 PyDemo,PyDemo 项目中使用了Leveldb 的 Python 绑定 cpy-leveldb(https://github.com/forhappy/cpy-leveldb),但是需要在定制 cpy-leveldb 的功能,此时你就需要在PyDemo 项目中新建一个子模块 cpy-leveldb,然后修改本地 cpy-leveldb的实现,此时 PyDemo 把它视作一个子模块,当你不在 cpy-leveldb 目录里时并不记录它的内容,取而代之的是,Git 将它记录成来自那个仓库的一个特殊的提交。当你在那个子目录里修改并提交时,子项目会通知那里的 HEAD 已经发生变更并记录你当前正在工作的那个提交
代码如下:
forhappy@forhappy-lenovo:/tmp$ mkdir PyDemo
forhappy@forhappy
-lenovo:/tmp$ cd PyDemo/
forhappy@forhappy
-lenovo:/tmp/PyDemo$ ls
forhappy@forhappy
-lenovo:/tmp/PyDemo$ git init
Initialized empty Git repository
in /tmp/PyDemo/.git/
forhappy@forhappy
-lenovo:/tmp/PyDemo$ touch README
forhappy@forhappy
-lenovo:/tmp/PyDemo$ vim README
forhappy@forhappy
-lenovo:/tmp/PyDemo$ git add .
forhappy@forhappy
-lenovo:/tmp/PyDemo$ git ci -m "Initial commit."
[master (root
-commit)>
1 file changed, 1 insertion(+)
create mode
100644 README
forhappy@forhappy
-lenovo:/tmp/PyDemo$ git st
# On branch master
nothing to commit (working directory clean)
forhappy@forhappy
-lenovo:/tmp/PyDemo$ git submodule add git@github.com:forhappy/cpy-leveldb.git
Cloning into
'cpy-leveldb'...
remote: Counting objects:
888, done.
remote: Compressing objects:
100% (475/475), done.
建立了 PyDemo 项目的子模块后我们查看一下:
forhappy@forhappy-lenovo:/tmp/PyDemo$ ls
cpy
-leveldb README
forhappy@forhappy
-lenovo:/tmp/PyDemo$ git st
# On branch master
# Changes to be committed:
# (use
"git reset HEAD <file>..." to unstage)
#
# new
file: .gitmodules
# new
file: cpy-leveldb
#
forhappy@forhappy
-lenovo:/tmp/PyDemo$ git add .
forhappy@forhappy
-lenovo:/tmp/PyDemo$ git ci -m "add submodule cpy-leveldb"
[master c02fba3] add submodule cpy
-leveldb
2 files changed, 4 insertions(+)
create mode
100644 .gitmodules
create mode
160000 cpy-leveldb
首先你注意到有一个.gitmodules文件。这是一个配置文件,保存了子项目 URL 和你拉取到的本地子目录
forhappy@forhappy-lenovo:/tmp/PyDemo$ cat .gitmodules
[submodule
"cpy-leveldb"]
path
= cpy-leveldb
url
= git@github.com:forhappy/cpy-leveldb.git
尽管 cpy-leveldb 是你工作目录里的子目录,但 GIT 把它视作一个子模块,当你不在该目录里时并不记录它的内容。注意 cpy-leveldb 条目的 160000 模式,这在 GIT 中是一个特殊模式,基本意思是你将一个提交记录为一个目录项而不是子目录或者文件。
好了,至此一个带子模块的 GIT 仓库就构建完了,你可以在主项目中添加,删除或修改源码,或者在 cpy-leveldb 中定制你自己的功能,两者不会相互影响,各自保持自己的提交记录。
克隆带子模块的项目
当你克隆一个带子项目的 GIT 项目时,你将得到了包含子项目的目录,但里面没有文件:
forhappy@forhappy-lenovo:/tmp/PyDemo-work$ git clone /tmp/PyDemo
Cloning into
'PyDemo'...
done.
forhappy@forhappy
-lenovo:/tmp/PyDemo-work$ ls
PyDemo
forhappy@forhappy
-lenovo:/tmp/PyDemo-work$ cd PyDemo/
forhappy@forhappy
-lenovo:/tmp/PyDemo-work/PyDemo$ ls
cpy
-leveldb README
forhappy@forhappy
-lenovo:/tmp/PyDemo-work/PyDemo$ cd cpy-leveldb/
forhappy@forhappy
-lenovo:/tmp/PyDemo-work/PyDemo/cpy-leveldb$ ls
forhappy@forhappy
-lenovo:/tmp/PyDemo-work/PyDemo/cpy-leveldb$ cd ..
此时你需要执行:git submodule init 来初始化你的本地配置文件,另外你还需要执行:git submodule update来从 cpy-leveldb 项目拉取所有数据并检出你上层项目里所列的合适的提交。
forhappy@forhappy-lenovo:/tmp/PyDemo-work/PyDemo$ git submodule init
Submodule
'cpy-leveldb' (git@github.com:forhappy/cpy-leveldb.git) registered for path 'cpy-leveldb'
forhappy@forhappy
-lenovo:/tmp/PyDemo-work/PyDemo$ git submodule update
Cloning into
'cpy-leveldb'...
remote: Counting objects:
888, done.
remote: Compressing objects:
100% (475/475), done.
一个常见问题是当开发者对子模块做了一个本地的变更但是并没有推送到公共服务器。然后他们提交了一个指向那个非公开状态的指针然后推送上层项目。当其他开发者试图运行git submodule update,那个子模块系统会找不到所引用的提交,因为它只存在于第一个开发者的系统中。如果发生那种情况,你会看到类似这样的错误:
$ git submodule update
fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Unable to checkout
'6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'cpy-leveldb'
子模块问题
使用子模块并非没有任何缺点。首先,你在子模块目录中工作时必须相对小心。当你运行git submodule update,它会检出项目的指定版本,但是不在分支内。这叫做获得一个分离的头——这意味着 HEAD 文件直接指向一次提交,而不是一个符号引用。问题在于你通常并不想在一个分离的头的环境下工作,因为太容易丢失变更了。如果你先执行了一次submodule update,然后在那个子模块目录里不创建分支就进行提交,然后再次从上层项目里运行git submodule update同时不进行提交,Git会毫无提示地覆盖你的变更。技术上讲你不会丢失工作,但是你将失去指向它的分支,因此会很难取到。
为了避免这个问题,当你在子模块目录里工作时应使用 git checkout -b 创建一个分支。当你再次在子模块里更新的时候,它仍然会覆盖你的工作,但是至少你拥有一个可以回溯的指针。
切换带有子模块的分支同样也很有技巧。如果你创建一个新的分支,增加了一个子模块,然后切换回不带该子模块的分支,你仍然会拥有一个未被追踪的子模块的目录。
本文部分段落参考来源
Pro Git:http://github.danmarner.com/section/ch6-6/
运维网声明
1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网 享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com