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”。
