Git子模块提交/推送/拉取。

14
我想使用git子模块。推送更改到我的项目需要执行以下步骤:
  1. 从子模块目录添加/提交/推送
  2. 从父目录添加/提交/推送
拉取项目更改的步骤如下:
  1. 从父目录运行git pull
  2. 从父目录运行git submodule update
更新子模块原始仓库的步骤如下:
  1. 从子模块目录运行git pull
我担心的是来自http://git-scm.com/book/en/Git-Tools-Submodules的以下摘录:

问题在于通常不希望在分离HEAD的环境下工作,因为容易丢失更改。如果你在子模块目录中进行初始子模块更新、提交而不创建一个工作分支,然后在没有同时提交的情况下再次从超级项目运行git submodule update,(??update/commit/update会丢失更改吗?) Git将在未告知你的情况下覆盖你的更改。从技术上讲,你不会失去工作,但你没有指向它的分支,所以检索起来有些困难。

为避免此问题,在子模块目录中使用git checkout -b work或类似命令创建一个分支。当第二次执行子模块更新时,它仍将还原您的工作,但至少您有一个指针可以返回。

我将修改子模块,不想搞砸,上述文档简要提到了可能丢失更改的情况,我不明白是什么原因导致的损失。

我想知道除了我列出的步骤之外,我需要采取哪些额外措施来防止丢失更改。 特别是一些团队成员修改子模块,他们需要做些什么以避免搞砸?


当然,如果您在没有提交/推送的情况下修改子模块,那么当您进行子模块更新时,它将检出原始子模块头,因此您将丢失您的更改。为了避免这种情况,您应该创建一个分支,修改子模块并提交。如果您进行子模块更新,您将把您的分支留给原始子模块头。不用担心,您现在可以合并您的子模块分支或者只是检出以获取它。 - elhadi dp ıpɐɥןǝ
你可以再具体一些吗?假设两个程序员正在修改团队项目的子模块,他们需要做什么?他们各自创建一个同名分支/修改/提交/在子模块文件夹中推送。然后进行子模块更新,是吗? - eugene
我正在为您准备更深入的答案,因为我正在处理类似的事情。同时,您是否阅读过http://blog.endpoint.com/2010/04/git-submodule-workflow.html和https://dev59.com/IW025IYBdhLWcg3wwY34?第一个链接还涵盖了使用子模块创建分支的内容(如果您想了解原因,请阅读此处的“子模块问题”http://git-scm.com/book/en/Git-Tools-Submodules)。 - Aaron Newton
我还可以建议您查看这篇文章 - https://dev59.com/oWw15IYBdhLWcg3wntAQ。有几种方法可以解决这个问题,我越来越倾向于使用git-slave。 - Aaron Newton
我还需要调查如何避免这个问题,但我理解他们的意思:如果你运行 git submodule update,那么子模块总是会执行分离 HEAD 检出。这是正常行为。如果随后你对该分离 HEAD 进行更改提交,那么在另一个 git submodules update 之后,它将再次将分离 HEAD 分支重置为 那些提交之前。 - Carlo Wood
我现在通过创建git子模块https://github.com/CarloWood/cwm4来解决了我的问题,它可以无缝地将(其他)git子模块集成到我的autotools支持的项目中。 - Carlo Wood
1个回答

26

作为 Visual Studio 解决方案中的外部项目工作者,我希望分享一下自己解决类似问题的经验。我对 Git 相对较新,如果有人能提供建设性的批评意见,我将不胜感激。

如果你在使用 Visual Studio,Git 源代码控制提供程序扩展是免费的(http://visualstudiogallery.msdn.microsoft.com/63a7e40d-4d71-4fbb-a23b-d262124b8f4c),而且测试时似乎会递归提交子模块。

但是,我在家里开发时使用的是 VS Web Developer Express,所以我不想依赖扩展程序(我也认为了解底层发生的事情是很重要的)。因此,我被迫研究这些命令,并在下面添加了一些注释。

注释

如果您还没有这样做,请仔细阅读http://git-scm.com/book/en/Git-Tools-Submodules。有很多注意事项,我会参考这个页面。如果您在没有阅读此页的情况下使用子模块进行工作,您很快就会让自己头痛。

我的方法遵循这篇教程,并添加了一些额外的内容:

一旦您初始化了超级项目(例如 git init && git remote add origin ...),就可以像这样开始添加子模块:

git submodule add git://github.com/you/extension1.git extension
git submodule init
git submodule update

检查您的.gitmodules文件是否反映了此添加,例如:

[submodule "extension1"]
        path = extension
        url = git://github.com/you/extension1.git

进入子模块目录(即 cd extension)。运行:

Switch to your submodule directory (i.e. cd extension). Run:


git fetch #I use fetch here - maybe you can use pull?
git checkout -b somebranchname #See the Git-Tools-Submodules link above for an explanation of why you need to branch

我在 README.txt 文件中做了修改,以便提交它(也为了记录我在此次提交中所做的工作),然后将模块提交以应用该分支(仍在子模块目录中):

git add .
git commit -a -m "Branching for extension submodule"

现在进入超级项目(即 cd ..)。您还需要在此处进行提交(如果您查看我提到的git子模块页面,它会解释为什么这是必需的):

现在进入超级项目(即 cd ..)。您还需要在此处提交更改(如果您查看我提到的git子模块页面,它会解释为什么这是必要的):

git status #will show you that your submodule has been modified
git commit -a -m "Commiting submodule changes from superproject"

现在,如果需要,我们可以递归地推送我们的项目,如下所示:

git push --recurse-submodules=on-demand

你需要按照上述步骤为所有子模块运行一次。

在为所有子模块完成此操作并开始进行更改以进行提交和推送后,您可以使用:

git submodule foreach 'git add .' #recursively add files in submodules

不幸的是,我还没有找到一种递归提交的方法,而不使用像git-slave这样的工具(有人用过吗?),因此您需要进入每个子模块目录并对刚添加的文件运行常规提交。在超级项目中执行:

git status #tells you that `extension` submodule has been modified
cd extension
git commit -a -m "Commiting extension changes in superproject edit session"

一旦子模块提交了,您还需要再次提交超级项目:

所以执行以下操作:

cd ..
git add .
git commit -a -m "Altered extension submodule"
git status #should now show 'working directory clean', otherwise commit other submodules in the same manner

这可能会有点烦人(因为你最终需要提交两次),但一旦你意识到它,实际上并不那么糟糕(因为它强制你检查每个项目中的提交内容)。这只是我的意见 - 如果你已经将一些超级项目的功能隔离成子模块,那么它应该可以独立于其余项目工作(因此,尽管有点烦人,但在不同时间提交它们并不是世界末日)。

现在我们可以再次推送...

git push --recurse-submodules=on-demand
如果你进入子模块并再次尝试推送,你会发现它不会执行任何操作,因为最新提交已经被推送了。
对于超级项目的克隆(或使用远程源),可能也会非常令人困惑 - 例如需要在运行git submodule init之后两次运行git submodule update。阅读http://git-scm.com/book/en/Git-Tools-Submodules中的“使用子模块克隆项目”一节。
在克隆我的超级项目时让我犯了一个错误,就是如何获取子模块的最新更改。参考Easy way pull latest of all submodules
我的变体是为已检出的子模块使用“开发”分支(但您可以将其命名为任何名称),然后在超级项目中使用它。
git submodule foreach git pull origin development

我设置这个时,还会切换到要提交更改的子模块所在的分支,方法如下:

cd extension
git checkout -b development #This will tell you this is a new branch, but I believe this means a new branch of the local git repository - this will get pushed to the 'development' branch
#Make your changes, commit etc.

我可以确认,当我按照上述步骤进行操作时,在克隆/远程源项目中对子模块所做的更改(在推送时)会显示在同一项目的其他克隆/远程源中(不要忘记最后的子模块拉取命令)。

希望这对你有所帮助。


感谢您详细的回答,我决定暂时搁置这个子模块的事情。 - eugene
我明白。我有点像Ubuntu迷,所以我对Windows安装程序中附带的BASH shell感到非常熟悉,并一直在使用它。如果您将与某种IDE项目设置一起使用它(例如我为Visual Studio所描述的),那么最好为您选择的IDE查找一个不错的插件。 - Aaron Newton
4
要一次性提交所有子模块,可以使用以下命令:git submodule foreach 'git commit -a -m "Altered submodule" || :'。该命令会循环遍历每个子模块并执行提交操作。 - Cel
1
我觉得这并没有真正回答问题(我也有同样的问题)。它当然没有解释为什么文档警告会丢失提交,也没有展示如何防止同事搞砸。当然,如果你按照你描述的工作流程进行操作,就不会有任何东西丢失,但是首先也不会出现搞砸的情况。我认为一个正确的答案应该解释哪些脚本和钩子可以防止上述的错误。 - Carlo Wood
我有一个包含子模块的代码库,我使用以下命令 git submodule update --init --recursive --remote --merge path/to/submodule。然后我运行 git addgit commit -mgit push。现在其他开发人员递归地克隆了我的代码库,然后运行 git pull,但是子模块的更改在拉取后出现为未暂存的更改,那么从上游代码库拉取带有更改的子模块的正确方法是什么? - CMCDragonkai
这表明出现了合并冲突 - 你检查过了吗?自从我写这篇文章以来,我开始使用SourceTree GUI。我可以看到它使用三个单独的命令进行拉取 - 一个用于获取(fetch),一个用于拉取(pull),一个用于子模块更新(submodule update)。获取(fetch):git -c diff.mnemonicprefix=false -c core.quotepath=false fetch origin,拉取(pull):git -c diff.mnemonicprefix=false -c core.quotepath=false pull origin authentication(对于名为“authentication”的分支),子模块更新(submodule update):git -c diff.mnemonicprefix=false -c core.quotepath=false submodule update --init --recursive - Aaron Newton

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