所以我有自己的存储库,我们称之为“我的”,它包含一个子模块存储库“他们的”。我可以访问他们的远程存储库,因此我可以直接将更改推送到它。
我正在尝试将我对“他们的”所做的更改推送回他们的远程 GitHub 存储库,但我似乎无法让它工作。我可以将更改推送到我在 GitHub 上的远程存储库“我的”,并且可以看到对“他们的”的新提交。但是当我访问他们的 GitHub 时,它没有添加最新的提交。
子模块不应该只是常规存储库,在另一个内部带有一些额外的元数据吗?如果是这样,为什么“他们的”目录中的“git push”不起作用?我试过从“他们的”内部提交和推送,然后从“cd ..”和“我的”内部提交/推送,但它也不起作用。
我已尝试此处列出的步骤:Git submodule push无济于事。我只能假设,因为我正在尝试推送到我不拥有的存储库,所以还需要做其他事情吗?
~/Mine$ git remote -v
origin https://github.com/myUser/Mine.git (fetch)
origin https://github.com/myUser/Mine.git (push)
~/Mine/Their$ git remote -v
origin https://github.com/theirUser/Theirs.git (fetch)
origin https://github.com/theirUser/Theirs.git (push)
你是对的:子模块本身就是一个存储库。这意味着您可以cd
或chdir
进入子模块并以与使用任何其他 Git 存储库相同的方式开始使用它,包括运行git fetch
、git checkout
、git commit
等。
使子模块成为子模块的原因是,位于子模块上方某处的其他 Git 正在控制子模块,通常是通过在子模块中运行。这会将子模块置于 Git 所谓的分离 HEAD模式。(控制 Git 是超级项目。)git checkout hash-id
这种“分离头”模式有点棘手。您可以在这种模式下进行新的提交,但是当您这样做时,大多数普通方法都无法找到它们。它们暂时很容易通过特殊名称找到HEAD
,但是这个特殊名称HEAD
会被超级项目强行调整,它会chdir
进入子模块并再次运行,丢失1你所做的提交。git checkout hash-id
要将您在子模块 Git 存储库中所做的提交发送到某个其他 Git 存储库,您必须在其他Git 存储库中为这些提交指定一个名称。通常,这意味着给他们一个分支名称。在您的存储库中使用分支名称没有硬性要求,因为您可以运行:
git push <remote-or-URL> HEAD:<name>
通过 name 将子模块存储库中标识的提交发送HEAD
到另一个 Git,并礼貌地要求它创建或更新其名称name
以指向该提交。但是大多数人不喜欢使用/使用分离的 HEAD 模式。
一般来说,这意味着要在充当某些超级项目的子模块的 Git 存储库中工作,您应该使用以下顺序:2
进入子模块。从这里到第 3 步,您只需将它用作常规 Git 存储库即可。
通过选择分支名称git checkout
或创建指向当前提交的分支名称来退出分离的 HEAD 模式。需要注意的是,如果你选择一些现有的分行名称,这可能是一个不同的从提交当前提交。或者它可能与当前提交相同。
请记住,超级项目存储库早些时候告诉这个 Git:使用这个原始提交,通过它的哈希 ID进入分离的 HEAD 模式。我们现在正在退出分离 HEAD 模式,这需要选择或创建分支名称。如果你选择一些现有的分支名称,你就会被分支名称选择的任何提交所困扰。但是,如果您正在子模块存储库中开发新的提交,您可能需要一个分支名称来记住它们。
现在,以通常的方式进行新的提交。使用git push
通常的方式。提交将或不会以提交执行或不执行的通常方式进入接收存储库。如果他们确实进入了接收存储库,则会以通常的方式创建或更新该存储库的分支名称。
一切完成后,退出子模块存储库,返回超级项目存储库。现在是时候在superproject 中进行新的提交了。
我已经多次提到超级项目 Git 一直控制着子模块 Git。它chdir
进入子模块 Git 并运行. 这里的关键来自两部分:git checkout hash-id
第一个问题的答案很复杂:git submodule update
是否,但并非总是如此;3 git checkout --recursive
这样做(总是);根据选项和设置,各种其他操作有时也可以做到。也就是说,它不会发生,除非和直到你问这样的事发生,但它并不总是很明显,你是问这样的事发生。我们要做的是确保在第二点再次发生之前解决第二点。
第二个问题的答案——超级项目 Git 从哪里获得原始哈希ID——是它从超级项目中的提交中获取它。但是您已经在子模块中进行了新的提交,并将其提交到上游到其他一些 Git 存储库,所以现在是时候在superproject 中进行新的提交,以记录正确的哈希 ID,即新的哈希 ID提交您在子模块中所做的。
与往常一样,任何提交都不能更改;和往常一样,Git从索引中的任何内容(又名staging area)进行新的提交。当您提取一些现有提交时,Git 会将该提交中的文件读取到索引/暂存区中。如果存储库充当某个子模块的超级项目,则此步骤还会从提交中读取所需的哈希 ID(在该子模块中)到索引中。由于这不再是所需的哈希 ID,您现在的工作是将新的哈希 ID放入索引中。
在超级项目中执行此操作的命令是git add
. 与往常一样,git add
将内容写入索引。对于普通文件,它将该文件的副本写入索引。4但是,对于子模块,它:
HEAD
?( git rev-parse HEAD
);无论子模块是否处于分离HEAD
状态,这git rev-parse HEAD
都有效,因为始终返回当前提交的原始哈希 ID。
因此, after git add path/to/submodule
,您选择的提交(实际上是制作和推送的)的哈希 ID现在记录在超级项目的索引中。您的下一次提交将记录该原始哈希 ID。
假设其他一切都准备好了,您现在可以运行git commit
以在超级项目中进行新的提交(这大概是,并且一直处于附加 HEAD 状态,在某个分支名称上)。一旦你提交了这个新的超级项目,你就可以git push
像往常一样准备好了。
请注意此处步骤的仔细排序:
origin
存储库中的提交,在 suprerproject 中创建和推送一个提交,该提交引用子模块提交的原始哈希 ID。由于 Git 的分布式特性,可以在您的子模块中进行提交但不将其推送到任何地方,然后在您的超级项目中进行提交并推送它。任何获得这个新提交的人都会得到一个提交,该提交通过原始哈希 ID 指向一个提交,他们不仅没有,而且甚至无法获得. 所以第 3 步(“使提交对所有人可用”)必须push
在第 4 步之前发生。(第 4 步中的“进行提交”可以更早发生——注意不要推送它,并用任何更新的提交重做它hash,如果子模块提交因任何原因必须重做。)
1失去这里的意思是“很难找到”。在提交本身不会消失的时候了:它们具有相同的宽限期,其他丢失的commit得到,你可以使用子模块Git的HEAD
引用日志找到他们,以同样的方式找到丢失提交任何资料库,因为子模块是刚毕竟,另一个存储库。
2因为 Git 是一组工具,而不是预先打包的解决方案,所以还有很多其他方法可以实现您的目标。这只是一种非常灵活的方式。
3特别是git submodule update
有很多更新模式。使用一些参数,您可以直接git submodule update
检查子模块中的名称,从而首先生成一个附加的 HEAD(而不是分离的 HEAD)!这是脚注 2 所指内容的一部分。的详细工作git submodule update
非常复杂,所以我试图在这个答案中避免这些变体。
4从技术上讲,它用正确的文件内容写出一个blob 对象——或者如果可能的话,不改变地重用一些现有的 blob 对象——然后将 blob 对象的哈希 ID 写入索引,而不是实际的文件内容。但效果好像Git的复制该文件到索引,只要你不坐下来的水平git ls-files --stage
和git update-index
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句