我正在尝试将存储库添加为另一个git模块中的子模块。 添加子模块后,我尝试克隆父项目:
git clone https://...
cd <parent_path>/<submodule_path>
git submodule init
git submodule update
现在,如果我git status
在子模块中,则HEAD已分离:
cd <submodule_path>
git status
HEAD detached at a4709b3
nothing to commit, working tree clean
阅读此answer之后,我尝试签出到子模块的master
:
git checkout master
git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
但是现在,如果我在父目录上git status
,则表明子模块中有新的提交(肯定不是这种情况)
cd <parent_dir_path>
git status
On branch test_submodule
Your branch is up to date with 'origin/test_submodule'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: submodule_name (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
有人可以阐明这一点吗?
编辑:
这是cd <submodule_path> && git show master
的输出:
commit 42309e0e2f48aba11902633173053e2423d4ba62 (HEAD -> master, origin/master, origin/HEAD)
Merge: a4709b3 1c1ae85
Author: Abc
Date: Fri May 31 16:27:52 2019 +0100
Merge pull request #6 in test/testing_submodule from test_integration_with_repos to master
* commit '1c1ae8515716d9c2d5135e86dc9c024c81e4320b':
test
答案 0 :(得分:2)
您的超级项目记住子模块必须位于提交a4709b3
处。但是子模块已更新,现在其master
指向42309e0
。您现在应该做什么取决于您要在超级项目中使用什么代码(提交什么)。最简单的解决方案是检出存储的提交:
cd <submodule_path>
git checkout a4709b3
子模块将处于分离的HEAD状态。不用担心。
另一种可能性是更新子模块:
cd <submodule_path>
git checkout master # reconcile detached HEAD
git pull origin master
,然后更新超级项目:
cd <parent_dir_path>
git add <submodule_path>
git commit -m "Update submodule"
答案 1 :(得分:0)
git status
比较HEAD
提交(当前提交)和索引。区别在于准备提交。然后,它比较索引和工作树。不管有什么不同,都不上演提交。在这种情况下,Git将索引gitlink与子模块中的实际提交哈希ID进行比较。当它说:
modified: submodule_name (new commits)
这仅表示索引中的gitlink与子模块中的HEAD
提交哈希不匹配。这并不意味着子模块中有 个新提交,也不意味着那里有 个新提交;这只是意味着现有索引gitlink哈希ID与现有子模块签出不匹配。
围绕子模块总是有很多困惑。您链接到的问题Why is my GIT Submodule HEAD detached from master?的答案并没有真正成为问题的核心,我认为可以通过以下对话进行总结:
人员::嗨,Git,我想将此另一个Git存储库用作我的Git存储库的子目录。
GIT:好,完成。
人员:太酷了。 [后来:]哦,嘿,为什么当我创建一个新克隆时,我的子模块处于分离的HEAD状态?
GIT:就是这样。
人员::但我希望它位于分支master
上。
GIT:确定。
人员:它仍然是独立的!
GIT:是的。
人员::我想在分支上使用它!
GIT:是的,不。抱歉。
简而言之,这就是它的工作方式。现在,子模块是是一个Git存储库,因此您可以将其添加到分支中。但是 superproject Git会立即从分支中撤回它,因为这就是子模块的工作方式。要理解为什么是这种情况,还需要花点时间。
首先,让我们再次注意,子模块是 Git存储库。 使成为子模块的唯一原因是其他一些Git存储库不时地对其进行控制。我们将另一个Git存储库称为 superproject 。超级项目正在执行控制命令,子模块正在遵循它们。除此之外,它们是两个独立 Git存储库:如果子模块具有master
和develop
以及其他分支,则它们独立于超级项目的master
和develop
以及其他任何内容,子模块和超级项目根本不必具有相同的分支名称集。
还请注意,子模块可以 成为另一个子模块的超级项目。自从现在说“子”子模块或“子”超级项目模棱两可以来,这种情况特别令人困惑。现在有两个超级项目,两个子模块和三个 Git存储库,中间的Git存储库都是(顶部Git的)子模块,超级项目(位于底部)。
设计做出了一个巨大的假设,这是一个完全安全的假设,但可能会令人讨厌。该假设是:子模块是从您无法控制的存储库中克隆的。该其他存储库可能非常频繁地更新,或几乎从未更新过,但是其分支会作为子模块的origin/*
远程跟踪名称复制到您的克隆中,以不必控制的方式进行更改。第一次克隆该子模块存储库时,git clone
将创建一个名为master
的新分支,如果他们这么说,则创建其他名称,而您可以通过检查该名字来获得的提交位于该分支的 控制权,而不是您的。
由于这个原因,超级项目本身不需要(而且几乎从未使用过)子模块的任何分支名称。相反,超级项目记录有关 不能更改的子模块存储库的信息。它无法更改的事实意味着,无论谁控制您的克隆源来对子模块进行操作,他们都不会破坏您对子模块的依赖性。 >
(当然不是100%正确。例如,他们可以完全删除源存储库。或者可以丢弃您依赖的提交或标记,但总的来说,人们不会删除存储库。丢弃提交是稀有,并且丢弃已发布的标签及其相应的提交尤其罕见。)
您的超级项目记录的无法更改的是超级项目将告诉子模块git checkout hash
的提交的原始哈希ID 。记录的信息将进入您的超级项目中的每次提交! (好吧,每次使用子模块的提交。)
git checkout hash
如果您已经使用Git足够长的时间,那么您对此很熟悉。通过其哈希ID来检查所有历史记录提交,然后Git进入分离的HEAD模式。
超级项目通过其不可更改的哈希ID来检查历史提交。因此,子模块最终以分离的HEAD模式运行。这都是方法和原因。超级项目记录哈希ID,并命令子模块:检查其中一个,现在您处于分离的HEAD模式。
这对于只读的历史提交非常有用。超级项目中的 每个提交都会记录该超级项目提交的正确子模块哈希ID。签出该提交时,您告诉Git同步您的子模块,然后正确的子模块提交,以便您可以构建和使用您的项目。
首先,我们都需要就一个定义达成共识:现有的提交与新工作无关。提交是只读的,始终冻结。您不能更改任何现有提交的任何内容。 (这就是子模块的提交的哈希ID对超级项目有用的原因:它被冻结在时间上,并且可能永远永久存在。)每个提交都包含所有文件的快照,以及一些元数据,例如创建者,时间和原因-日志消息。
这样冻结了提交,但我们认为,新工作是一件可取的事情。因此,任何一个Git存储库(除了--bare
之外)都提供了一个您可以执行此操作的地方。那个地方是工作树(或工作树或任何数量的相似拼写)。 Git从提交中将压缩的,只读的,仅Git格式的文件复制到工作树中,以它们的日常形式保存,因此您可以查看和使用文件。
引用子模块的提交通过Git称为 gitlink 的方式进行。 gitlink实际上是mode 160000
的提交文件(普通文件是mode 100644
或mode 100755
),其中文件的“内容”只是超级项目应命令的哈希ID。子模块Git到git checkout
。
因此,提交中的子模块条目gitlink告诉git checkout
:您正在充当超级项目。当您到达工作树中的这个位置时,而不是仅在此处提取一个文件,子模块应该作为单独的HEAD进入此提交。如果您使用git checkout --recurse-submodules
,Git会做到这一点。如果您使用git checkout --no-recurse-submodules
,则Git会坚持这样做-它会留下子模块,而子模块毕竟是一个单独的Git存储库。
现在,Git使 new 提交的不是工作树中的内容,而是 index 中的内容。索引包含每个文件的副本...并且当提交具有gitlink时,索引包含该gitlink的副本。 这是索引中的gitlink条目,它决定您进行下一次提交的内容。因此,下一个 superproject 提交将使用超级项目索引中的内容。
有时您希望子模块在另一个提交上。由于子模块是是一个Git存储库,因此您可以直接进入该子模块并git checkout
喜欢。如果使用分支名称,则现在将附加子模块的HEAD
。就超级项目Git而言,这无关紧要:对超级项目重要的是实际的哈希ID。如果子模块的git rev-parse HEAD
仍产生与超级项目索引的gitlink中相同的哈希ID,则所有内容仍然匹配。如果它产生其他哈希值,则由您自己解决。 由于您想要另一次提交,因此现在应该更新超级项目索引的gitlink。
git checkout
分支 name ?您可以做到这一点。但是这里的假设是,您已让Git从不受控制且不git push
的上游存储库克隆子模块Git。因此,这不是足够,而Git提供的是命令:
git submodule update --remote
在这种情况下,超级项目Git将:
git fetch
origin/master
,等待git fetch
或它在子模块中的更新origin/master
(或其他任何东西)的新哈希ID ,然后再次将子模块Git git checkout
那个哈希ID ...作为独立的HEAD!
如果您确实要控制子模块,并想在其中进行新的提交,您需要cd
插入子模块,而只需{{1} }所需的任何分支名称,然后在此处进行工作。毕竟,该子模块是常规的旧Git存储库。您可以执行所需的任何工作,然后运行git checkout
将所有更新的工作树文件复制到索引中-git add
将使用索引中的内容-然后运行git commit
进行创建新的提交。
然后,完成所有这些操作后,您可以立即将提交推送到上游,也可以等待并git commit
回到超级项目。现在,无论哪种方式,您都可以执行超级项目中所需的任何工作,并cd
修改任何文件和子模块的名称。您不仅要更新超级项目索引中的文件,还需要更新超级项目中的gitlink。现在,您已经完成了所有这些操作,您可以在超级项目中运行git add
,以进行新提交,以存储更新的文件和的gitlink。
现在您有了新的超级项目提交,可以在某个地方git commit
进行新的超级项目提交。 如果您已经在子模块中使用了git push
,那么这就是您需要做的。但是,如果没有,则应该首先git push
子模块。原因很明显,一旦您考虑一下:新的超级项目提交说:进入该子模块时,请阅读此gitlink并提取提交git push
(或它的任何哈希ID)。对于其他人,他们需要a987654...
git fetch
已被git push
编辑的上游子模块,以便获取将a987654...
提交到其子模块Git!
请注意,这与git submodule update --remote
动作匹配:他们将转到其子模块的上游,git fetch
更新后的分支,然后git checkout
进入相应的origin/branch
哈希ID,这种情况a987654...
,作为其子模块的独立HEAD。
这不是有史以来最流畅的过程,但是它就像Git本身所做的那样简单。 git submodule update
还可以做其他几件事,但都是从这种思路开始的:子模块存储库是从其他地方克隆的,主要是其他地方提供了新功能。提交子模块。