Git 子模块混乱:如何在不熟悉 Git 的开发人员中使用 Git 子模块?

7

我对使用git的子模块功能感到非常沮丧。要么我还是没有弄懂它,要么它并没有像我期望的那样工作。以下是给出的项目情况:

Project
  | .git
  | projsrc
  | source (submodule)
  | proj.sln

在这种情况下,“source”指向另一个存储库,其中包含我们所有项目的共同源数据。在“source”下发生了大量开发,也在“projsrc”下进行了许多开发。不幸的是,“Project”指向源子模块的某个提交,而不是它的实际HEAD。据我所知,这是Git的常规行为。
我已经发现了:
git submodule update

只需获取与主项目一起提交的子模块版本。然而,我真的很想始终了解子模块的开发情况,但是我不知道如何正确地做到这一点。因此我的问题是:
是否可能将项目附加到子模块的HEAD, 而不管这是否会破坏项目的编译。 我只是不想总是进入子模块目录并在那里执行git pull。 因为我认为我可能会失去在子模块目录中所做的更改, 因为它只是附加到一个提交而不是任何分支。
请考虑以下限制: - 我们组中的开发人员并不熟悉所有VCS。我们过去使用非常庞大的svn存储库,根本没有任何外部存储库功能。 - 我们正在Windows上工作 - 最好是一种点击即忘的解决方案,因为大多数项目成员都害怕使用命令行界面 :)

1
请参阅 https://dev59.com/mXI-5IYBdhLWcg3wMFMf#1979194 和 https://dev59.com/PnA75IYBdhLWcg3wv7wj#3132221。 - VonC
我可能会失去在子模块目录中所做的更改,因为它只是附加到一个提交而不是真正的任何分支。这不是真的!总有一个分支。只有在你处于分离头状态时才会失去更改,直到你提交为止。 - Vanuan
3个回答

8

子模块指向特定版本的原因非常重要。如果您将其指向HEAD,构建将会不可重复。也就是说,如果您检出昨天的项目版本,您永远不知道昨天源代码的确切版本@HEAD是哪个。

这就是为什么它始终存储特定版本sha的原因。

要拉取所有子模块,您可以使用轻松拉取所有子模块的最新方法


我完全同意它们会被破坏,但是我对这种情况感到满意。我只是烦恼于解释每个不熟悉Git的开发人员在我的项目上合作时子模块源代码不按他们期望的方式工作。他们总是在源代码上进行任何更改的提交/推送。更重要的是,如果他们只是在项目中执行“git submodule update”,则会丢失所有对“source”所做的更改,因为它们会被该特定版本覆盖。 - cgart
“submodule update” 的作用不是删除任何更改,据我所知,它只是检出引用的版本。已经做出的更改仍然在仓库中,您可以检出它们,然后更新 Project 以引用该版本,并将子模块版本引用提交到主项目。 - kan
实际上,如果您尚未提交和推送更改(也没有推送父级中的更改),Git 将删除您的更改。由于 source/ 是带分离 HEAD 的子模块(默认行为),因此 Git 子模块更新始终会将 source/ 目录置于父存储库指定的状态。在我看来,这是 Git 的非常糟糕的行为,因为它甚至不会通知我我的本地更改被覆盖了(git 1.7.7)。 - cgart
如果更改没有提交,submodule update 不会改变任何东西(至少在我的电脑上是这样的)。如果更改已经提交,它们不会丢失,即使您检出了其他内容,也可以检出它们。此外,你所说的 submodule update 是用来做什么的? - kan
你仍然可以cd到源代码并检出head。此外,如果您提交更改到源代码,则需要将其推送到origin。您应该将源代码视为一个简单的git存储库,嵌套在projsrc中。这就是子模块的真正含义。唯一的区别是projsrc具有关于源代码子模块和特定提交的来源信息。如果您希望您的公共源始终与其他项目的版本相同,则应将所有项目放在与common相同的存储库中。 - Vanuan

2

我不擅长Git和子模块。但我认为一些简单的规则会非常有帮助。

  1. 从子目录提交和推送。
  2. 返回到项目的根目录,检查状态是否需要再次提交和推送。

当拉取时,可以尝试使用脚本将“拉取/子模块更新”捆绑在一起。并且只在项目的根目录执行。


2
考虑以下情况:
1.源指向HEAD(正常情况下)。
2.在项目中更改源代码(提交但不推送更改)。
3.现在你有两个HEAD:一个在你的项目源中,另一个在公共源中。
当你更新子模块时,你希望哪一个出现在你的项目中?
在你的情况下,git的问题(也是主要特点)是你认为提交和推送是原子操作。但事实并非如此。Git是分散式的。没有共同的HEAD。你可能有多个具有不同HEAD的存储库。
考虑以下情况:
1.你有三个开发人员(A、B和C)与一个git项目。
2.他们都拉取了项目的HEAD。
3.每个开发人员都对项目进行了更改。
4.现在每个人都有3个HEAD:A HEAD、B HEAD和C HEAD。
你认为哪个HEAD是“真正”的HEAD?
因此,回答你的问题:如果你想让公共源子模块始终与中央存储库同步,那么git不是你的选择。也许没有VCS能帮助你做到这一点。
你应该将git子模块视为第三方库,需要手动更新以下两个步骤:
1.拉取你的子模块(“下载第三方库”)。
2.提交已更新的子模块项目(“将新版本的第三方库放入你的项目中”)。
如果你想更改子模块,你应该按相反的顺序执行以下操作:
1.提交你的子模块(“对第三方库进行更改”)。
2.推送你的子模块(“将你的更改发送给第三方库维护者”)。
3.提交已更新的子模块项目(“将新版本的第三方库放入你的项目中”)。

感谢Vanuan。是的,我知道Git的想法了。你可能是对的,它可能不是正确的选择。问题是,相比之前使用的SVN,我花了很长时间才理解Git实际在做什么。我已经非常担心向我的共同开发者解释这一点...尽管如此,我认为我已经找到了自己使用Git的工作流程。 - cgart
不要害怕解释。Git 就是分布式版本控制工具的极简实现。你的合作开发者需要理解的一件事是,每个 Git 仓库副本都是一个自包含的本地 "svn-server"。同步不同 "服务器" 之间的需求是某些操作在 Git 中无法实现的原因。 - Vanuan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接