刘勇虎的官方网站
网站内容包含大前端、服务器开发、Python开发、iOS开发、Android开发、网站维护等技术文章。专注于分享技术经验,职业心得体会,IT优秀文章与教程创作。
Stay hungry,Stay foolish,Stay young
经常有这样的事情,当你在一个项目上工作时,你需要在其中使用另外一个项目。也许它是一个第三方开发的库或者是你独立开发和并在多个父项目中使用的。这个场景下一个常见的问题产生了:你想将两个项目单独处理但是又需要在其中一个中使用另外一个。
这里有一个例子。假设你在开发一个网站,为之创建Atom源。你不想编写一个自己的Atom生成代码,而是决定使用一个库。你可能不得不像CPAN install或者Ruby gem一样包含来自共享库的代码,或者将代码拷贝到你的项目树中。如果采用包含库的办法,那么不管用什么办法都很难去定制这个库,部署它就更加困难了,因为你必须确保每个客户都拥有那个库。把代码包含到你自己的项目中带来的问题是,当上游被修改时,任何你进行的定制化的修改都很难归并。
Git 通过子模块处理这个问题。子模块允许你将一个 Git 仓库当作另外一个Git仓库的子目录。这允许你克隆另外一个仓库到你的项目中并且保持你的提交相对独立。
我们将要演示如何在一个被分成一个主项目与几个子项目的项目上开发。
我们首先将一个已存在的 Git 仓库添加为正在工作的仓库的子模块。 你可以通过在 git submodule add 命令后面加上想要跟踪的项目 URL 来添加新的子模块。 在本例中,我们将会添加一个名为 “DbConnector” 的库。
$ git submodule add https://github.com/chaconinc/DbConnector
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
现在你就在项目里的 rack 子目录下有了一个 Rack 项目。你可以进入那个子目录,进行变更,加入你自己的远程可写仓库来推送你的变更,从原始仓库拉取和归并等等。如果你在加入子模块后立刻运行 git status,你会看到下面两项:
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: .gitmodules
# new file: rack
#
首先你注意到有一个 .gitmodules 文件。这是一个配置文件,保存了项目 URL 和你拉取到的本地子目录
$ cat .gitmodules
[submodule "rack"]
path = rack
url = git://github.com/chneukirchen/rack.git
如果你有多个子模块,这个文件里会有多个条目。很重要的一点是这个文件跟其他文件一样也是处于版本控制之下的,就像你的 .gitignore 文件一样。它跟项目里的其他文件一样可以被推送和拉取。这是其他克隆此项目的人获知子模块项目来源的途径。
git status 的输出里所列的另一项目是 rack 。如果你运行在那上面运行 git diff,会发现一些有趣的东西:
$ git diff --cached rack
diff --git a/rack b/rack
new file mode 160000
index 0000000..08d709f
--- /dev/null
+++ b/rack
@@ -0,0 +1 @@
+Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
尽管 rack 是你工作目录里的子目录,但 Git 把它视作一个子模块,当你不在那个目录里时并不记录它的内容。取而代之的是,Git 将它记录成来自那个仓库的一个特殊的提交。当你在那个子目录里修改并提交时,子项目会通知那里的 HEAD 已经发生变更并记录你当前正在工作的那个提交;通过那样的方法,当其他人克隆此项目,他们可以重新创建一致的环境。
这是关于子模块的重要一点:你记录他们当前确切所处的提交。你不能记录一个子模块的 master 或者其他的符号引用。
当你提交时,会看到类似下面的:
$ git commit -m 'first commit with submodule rack'
[master 0550271] first commit with submodule rack
2 files changed, 4 insertions(+), 0 deletions(-)
create mode 100644 .gitmodules
create mode 160000 rack
注意 rack 条目的 160000 模式。这在Git中是一个特殊模式,基本意思是你将一个提交记录为一个目录项而不是子目录或者文件。
你可以将rack目录当作一个独立的项目,保持一个指向子目录的最新提交的指针然后反复地更新上层项目。所有的Git命令都在两个子目录里独立工作:
$ git log -1
commit 0550271328a0038865aad6331e620cd7238601bb
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Apr 9 09:03:56 2009 -0700
first commit with submodule rack
$ cd rack/
$ git log -1
commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433
Author: Christian Neukirchen <chneukirchen@gmail.com>
Date: Wed Mar 25 14:49:04 2009 +0100
Document version change