git - 过去承诺中可见的子模块文件夹
问题描述
我第一次测试 git 以检查它是否可以解决我的版本控制问题。我开始玩子模块,我注意到一个意想不到的行为。
有用的信息:
- 我在操作系统上运行 git
- 我的远程存储库在 Bitbucket 上
- 我使用终端并使用 Sourcetree 检查
这是来自 Sourcetree 的树:
在第四个承诺“我现在有一个图书馆”中添加了子模块“图书馆”。
当我git checkout master
使用我的 ProjectFile.txt 和我的子模块“库”时,我正确地看到了我的存储库:
如果 I ,我希望只找到一个自述文件git checkout <hash 1st commitment>
,但事实并非如此。.gitmodules 消失了,但包含其内容的“库”文件夹仍然存在:
如果我转到 Bitbucket 上的远程存储库并签出相同的第一个承诺,那么我找不到预期的库文件夹:
为什么“库”文件夹在实际添加到我的计算机之前弹出?
请注意,当在 Bitbucket 上创建新存储库时,默认情况下会出现第一个提交,因此不会错误地存在“library”文件夹。
解决方案
理想情况下,如果您克隆存储库及其子模块,但随后检查未使用子模块的提交,Git 将删除子模块目录。但是,这样做有一个 Git 的历史问题。
在下面的讨论中,请记住子模块本身就是一个 Git 存储库。它只是一个 Git 存储库,其当前提交由另一个 Git 控制,我们称之为超级项目. 超级项目 Git 有一个要使用的子模块列表,并且对于每个子模块,该子模块内的提交以在该子模块内签出/切换到。还要记住,Git 存储库的核心是一对数据库。一个数据库,通常是更大的数据库,包含所有内部 Git 对象:提交及其文件以及构成历史记录的其他支持内部对象。另一个数据库保存名称,例如分支和标签名称,以及它们到提交的映射。在这两个数据库之上有一个很大的软件层来进行源代码管理,但是数据库本身对于这个过程是必不可少的:除非提交存在并且可以找到,否则没有任何东西可以去任何地方。
因此,存储库就是这个数据集合,存储在一个.git
目录中。裸存储库是仅包含目录的存储库.git
:这是通常在服务器和托管站点(如 GitHub 或 Bitbucket)上找到的存储库。开发人员的存储库由.git
目录和工作树或工作树组成,Git 可以在其中从某些提交中提取文件以便您可以处理它们,并且您可以在其中处理这些文件并因此进行新的提交。
请注意,.git
顶级(超级项目)存储库的目录通常位于该存储库的工作树中。也就是说,在工作树的顶部,有一个.git
包含存储库的隐藏目录。
在现代 Git 中——“现代”在这里被定义为 Git 版本 2 及更高版本,尽管其中一些也出现在 1.x 后期版本中——存储库及其子模块(如果有的话)的正常存储布局通常存储所有存储超级项目的目录中的子模块存储库.git
。这种机制称为吸收:父超级项目“吸收”子模块的存储库。因此,如果父存储库在path/to/repo/.git
,并且子模块的工作树在path/to/repo/subm/
,则实际存储库for path/to/repo/subm/
is not path/to/repo/subm/.git/
but rather path/to/repo/.git/modules/subm/
.你会发现有一个普通文件名为path/to/repo/subm/.git
; 这个普通文件包含 Git 在子模块中操作时需要的存储库路径信息。
但是,在非常旧的 Git 版本中,其中一些仍在使用中,情况并非如此:子模块的存储库实际上存储在path/to/repo/subm/.git
. 此外,如果您创建一个新的超级项目和子模块对,您可以通过运行:
mkdir path/to/repo
cd path/to/repo
# now get into path/to/repo/subm and create the submodule first
mkdir subm; cd subm; git init
echo "submodule for whatever" > README.txt
git add README.txt
git commit -m initial
git push -u ... # create submodule on the hosting system
# now back to path/to/repo
cd ..
git init
echo "new project" > README.txt
git add README.txt
git commit -m initial
# include the submodule
git submodule add <url> subm
git commit -m "add submodule"
完成此操作后,您还没有将子模块“吸收”到主存储库中。实际的子模块存储库,包括它的所有提交和其他数据,仍然存在于path/to/repo/subm/.git/
. 您必须运行:
git submodule absorbgitdirs
将子模块存储库移动到path/to/repo/.git/modules/
. (如果您的git submodule
命令缺少absorbgitdirs
子命令,那么您的 Git 版本不支持吸收子模块。)
现在,如果你有一个吸收式子模块,并且你曾经git checkout
检查过一个超级项目提交——就像我们在上面的超级项目中的第一个提交——实际上并没有子模块,你的超级项目 Git 可以安全地删除整个path/to/repo/subm/
树,包括.git
文件。实际的子模块,包括您所做但尚未推送的任何提交,都安全地存储在主 Git 存储库中。但是,如果出于任何原因您有一个未吸收的子模块,则删除 path/to/repo/subm/
也会删除 中的所有文件path/to/repo/subm/.git/
,从而删除整个存储库。
这显然很危险,Git 根本不这样做,即使子模块已被吸收。也许 Git 应该为吸收子模块的情况这样做,但它只是没有,至少在当前版本的 Git (2.27) 中是这样。
推荐阅读
- node.js - 保护我的 ExpressJS 服务器路由,它们将通过 cURL 访问
- r - 如何在没有表达式功能的情况下将上标添加到 ggplot 标签?
- java - 将 JSON 记录转换为 LinkedHashMap
使用杰克逊 API - webdav - 实施 WebDAV ACL:我应该修改还是替换访问条目?
- azure - 创建应用服务托管证书失败
- spring-data-rest - Neo4J OGM 中的类顺序扫描错误
- windows - 想要将所有内容(文件/文件夹)压缩到父文件夹中的 4 个不同文件夹中
- c++ - 将视口渲染到纹理会产生重叠视口吗?
- sql - 如何使用来自另一个表的数据对一个 SQL 查询的结果进行排序
- r - 在 RSelenium 的 findElement 函数中传递变量的值