Git 如果子模块特性使用跟踪分支,它是如何在内部实现的

Git 如果子模块特性使用跟踪分支,它是如何在内部实现的,git,Git,我正在阅读子模块,其中解释了子模块作为一种特殊类型的文件保存在主存储库提交树中: [/tmp/git/super(master)]$ git ls-files --stage 100644 831cdc0dc1b88e69aa9943cf09907ae1bcd031fc 0 .gitmodules 160000 85ab8ba4edf9168ab051ded7ddbbe20861b71528 0 ProjectA <-------- 100644 16f5c2d3aa96

我正在阅读子模块,其中解释了子模块作为一种特殊类型的文件保存在主存储库提交树中:

[/tmp/git/super(master)]$ git ls-files --stage 
100644 831cdc0dc1b88e69aa9943cf09907ae1bcd031fc 0   .gitmodules
160000 85ab8ba4edf9168ab051ded7ddbbe20861b71528 0   ProjectA     <--------
100644 16f5c2d3aa9656fc424352e4cfaa2523c809778b 0   super.txt
它是如何在内部工作的

据我了解:

跟踪分支设置在子模块克隆存储库中 跟踪分支被添加到.gitmodules 但是引用提交的特殊文件会发生什么情况?它是否仍然保存来自子模块的提交哈希

跟踪分支功能是否仅影响我运行时的行为

git submodule update --remote 
它检查子模块跟踪分支,检查新提交并更新主存储库的索引?

Background请跳到下一节 关于子模块,需要记住的主要一点是,在子模块连接到超级项目的地方,始终涉及两个Git

超级项目至少有三条信息。两个在.git模块中:

完整路径,例如,如果子模块位于某一组子目录中,则为path/to/ProjectA;如果子模块位于顶层,则为ProjectA;和 URL,以便Git可以运行Git克隆。。。在克隆超级项目(而不是其子模块)后获取子模块。 git ls files-stage的索引包含两条信息:

完整路径,例如路径/to/projectA,必须与.gitmodules条目匹配;和 所需的提交哈希,例如85ab8ba4edf9168ab051ded7ddbbe20861b71528。 由于这些重叠,你很明显会失去同步:如果你改变路径,事情会变得有点奇怪

不过,子模块本身是一个Git存储库。这意味着除了提交之外,它还有头、分支、标记等等。HEAD的内容表示当前提交。最初,当超级项目控制所有内容时,子模块的Git被告知:通过哈希ID签出一个特定的提交来分离头,例如85ab8ba4edf9168ab051ded7ddbbe20861b71528

但是,您可以进入子模块,检查分支名称或其他标记。您可以运行git fetch从该子模块git的USStream获取提交,这与超级项目无关。简而言之,您可以在任何旧Git存储库中执行任何操作

但一旦您这样做了,事情就不同步了:HEAD解析到的提交ID可能与超级项目中存储的提交ID不匹配

将分支名称与子模块一起使用 。。。跟踪分支被添加到.gitmodules

对。它坐在那里,等待您的下一个命令:

它从那里或其他地方捕获它,并使处理子模块的Git运行:

git fetch [potential additional options]
然后是git merge、git rebase或git checkout,具体取决于更多标志、选项和设置。传递给下一个命令的参数还取决于更多的标志、选项和设置

完成后,子模块本身可能会签出其他提交。也就是说,在子模块中运行的git rev parse HEAD命名的不是85ab8ba。。。。所以现在您的超级项目和子项目不同步了:您的超级项目调用commit 85ab8ba。。。具体来说,但您的子模块不在该提交上

现在,您的工作是确保超级项目在新的子模块散列就绪的情况下正常工作。如果是这样,您可以在超级项目中在子模块的路径上运行git add。这将更新特殊索引项,保持路径完整,但会向其中写入新的提交散列

现在,您可以在超级项目中进行git提交。索引的内容通常决定新提交的内容。提交将记录新的哈希ID。包含分支名称的.gitmodules的内容没有更改,因此新提交中记录的.gitmodules版本与旧提交中记录的.gitmodules版本相同。超级项目中新散列ID的唯一标志是,存储在提交中的散列ID将在超级项目存储库中提交的git签出时复制回索引,该散列ID是此更新的散列ID

1此时使用的分支取自.gitmodules,除非$GIT_DIR/config中有子模块..分支设置。配置设置覆盖.gitmodules设置。这部分是超级项目中的当前分支名称。所有这些不同的东西都需要限定,因为我们同时考虑两个Git:超级项目Git repo和子模块Git repo


现有的Git文档似乎不太适合在这里保持清晰的区别。

谢谢,很好的回答一如既往:。因此,如果在子模块中使用分支名称,我似乎可以手动执行git所做的操作—进入子模块存储库并运行git fetch和git checkout。对吗?在这两种情况下,我都必须手动执行git add path/to/submodule,对吗?还有两个后续问题:跟踪分支是否在su中
B模块相关?这个配置子模块..分支适用于所有模块?它的目的是将所有子模块与超级项目中的当前分支同步吗?还有一个稍微离题的问题-您使用过git子树吗?我现在正在考虑拆分相当大的项目,并考虑使用子模块或子树。我看过比较,我对你的看法很感兴趣。谢谢你先回答最后一个问题,我真的不喜欢任何选择。子模块本身在某种设计上是有意义的,但在实践中证明是痛苦的。子树在某种意义上似乎更实用,但仍然是一种痛苦。我在实践中使用的只是多个回购协议,松散地同步,在重要的时候在所有回购协议中使用一个通用标记。也就是说,从糟糕的旧git 1.5-1.7天开始,子模块的支持似乎有了很大的改进,也许值得再试一次。至于其余的:是的,你可以手动完成新选项自动化的操作,这就是我们过去的做法。。。。当您在子模块内工作时,子模块内的任何上游设置都很重要,但当您不在子模块内工作时,就没有那么重要了。我在实践中没有使用过这种新颖的模式。我真的不确定子模块..分支的东西是用来做什么的;再一次,这些都说明了我在使用老式子模块时的糟糕经历。
git submodule add -b master [URL to Git repo];
git submodule update --remote
git fetch [potential additional options]