23.4 隐性子模组

我在开发备份工具Gistore[1]时遇到一个棘手的问题,就是隐性子模组的问题。Gistore备份工具的原理是将要备份的目录都挂载(mount)在工作区中,然后执行git add。但是如果有某个目录已经被Git化了,就只会以子模组的方式将该目录添加进来,而不会添加该目录下的文件。对于一个备份工具来说,这就意味着备份没有成功。具体操作过程如下:

(1)例如当前super版本库下有两个子模组:


$cd/path/to/my/workspace/super-clone/

$git submodule status

126b18153583d9bee4562f9af6b9706d2e104016 lib/lib_a(remotes/origin/HEAD)

3b52a710068edc070e3a386a6efcbdf28bf1bed5 lib/lib_b(heads/master)


(2)然后创建一个新目录others,并把该目录用Git初始化,并做一次空的提交。


$mkdir others

$cd others

$git init

$git commit—allow-empty-m initial

[master(root-commit)90364e1]initial


(3)在others目录下创建一个文件newfile。


$date>newfile


(4)回到上一级目录执行git status,看到有一个others目录没有加入版本库控制,这很自然。


$cd..

$git status

On branch master

Untracked files:

(use "git add<file>…" to include in what will be committed)

#

others/

nothing added to commit but untracked files present(use "git add" to track)


(5)但是如果对others目录执行git add后,会发现奇怪的状态。


$git add others

$git status

On branch master

Changes to be committed:

(use "git reset HEAD<file>…" to unstage)

#

new file:others

#

Changed but not updated:

(use "git add<file>…" to update what will be committed)

(use "git checkout—<file>…" to discard changes in working directory)

(commit or discard the untracked or modified content in submodules)

#

modified:others(untracked content)

#


(6)看看others目录的添加方式,就会发现others目录以gitlink的方式添加到版本库中,而没有把该目录下的文件添加到版本库中。


$git diff—cached

diff—git a/others b/others

new file mode 160000

index 0000000..90364e1

—-/dev/null

+++b/others

@@-0,0+1@@

+Subproject commit 90364e1331abc29cc63e994b4d2cfbf7c485e4ad


之所以上面的步骤(5)运行git status命令时others出现了两次,就是因为目录others被当作子模组添加到了父版本库中,而且由于others版本库本身“不干净”,存在尚未加入版本控制的文件,所以又在状态输出中显示了子模组包含改动的提示信息。

接下来执行提交,将others目录提交到版本库中。然后当执行git submoudle status命令时会报错。因为others作为子模组没有在.gitmodules文件中注册。


$git commit-m "add others as submodule."

$git submodule status

126b18153583d9bee4562f9af6b9706d2e104016 lib/lib_a(remotes/origin/HEAD)

3b52a710068edc070e3a386a6efcbdf28bf1bed5 lib/lib_b(heads/master)

No submodule mapping found in.gitmodules for path 'others'


那么如何在不破坏others版本库的前提下,把others目录下的文件加入版本库呢?即避免others以子模组形式添加入库,具体操作过程如下。

(1)先删除以gitlink形式入库的others子模组。


$git rm—cached others

rm 'others'


(2)查看当前状态。


$git status

On branch master

Changes to be committed:

(use "git reset HEAD<file>…" to unstage)

#

deleted:others

#

Untracked files:

(use "git add<file>…" to include in what will be committed)

#

others/


(3)重新添加others目录,注意目录后面的斜线(即路径分隔符)非常重要。


$git add others/


(4)再次查看状态,看到others下的文件被添加到super版本库中。


$git status

On branch master

Changes to be committed:

(use "git reset HEAD<file>…" to unstage)

#

deleted:others

new file:others/newfile

#


(5)执行提交。


$git commit-m "add contents in others/."

[master 1e0c418]add contents in others/.

2 files changed,1 insertions(+),1 deletions(-)

delete mode 160000 others

create mode 100644 others/newfile


在上面的操作过程中,首先删除了库中的others子模组(使用—cached参数执行删除);然后为了添加others目录下的文件使用了"others/"(注意others后面的路径分割符“/”)。现在查看一下子模组的状态,会看到只显示出了之前的两个子模组。


$git submodule status

126b18153583d9bee4562f9af6b9706d2e104016 lib/lib_a(remotes/origin/HEAD)

3b52a710068edc070e3a386a6efcbdf28bf1bed5 lib/lib_b(heads/master)


[1]参见本书第7篇“第37章 Gistore”。