繁体   English   中英

如何将更改从我自己的存储库的子模块推送到其他人的存储库(我是合作者)?

[英]How do I push changes to someone else's repository (I'm a collaborator on), from a submodule of my own repository?

所以我有自己的存储库,我们称之为“我的”,它包含一个子模块存储库“他们的”。 我可以访问他们的远程存储库,因此我可以直接将更改推送到它。

我正在尝试将我对“他们的”所做的更改推送回他们的远程 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) 

提交完成后,将其推送到分叉仓库(即“我的”)。 从你的分支在正确的分支中打开一个拉取请求。 拉取请求获得批准并合并后,您可以将更改从上游拉取到本地存储库。

然后更改将被添加到“他们的”存储库中。

你是对的:子模块本身就是一个存储库。 这意味着您可以cdchdir进入子模块并以与使用任何其他 Git 存储库相同的方式开始使用它,包括运行git fetchgit checkoutgit commit等。

使子模块成为子模块的原因是,位于子模块上方某处的其他 Git 正在控制子模块,通常通过在其中运行git checkout hash-id控制子模块。 这会将子模块置于 Git 所谓的分离 HEAD模式。 (控制 Git 是超级项目。)

这种“分离头”模式有点棘手。 可以在这种模式下进行新的提交,但是当您这样做时,大多数普通方法都无法找到它们。 它们暂时很容易通过特殊名称HEAD ,但是这个特殊名称HEAD将被超级项目强行调整,它将chdir进入子模块并再次运行git checkout hash-id ,丢失1你所做的提交.

要将您在子模块 Git 存储库中所做的提交发送到某个其他 Git 存储库,您必须其他Git 存储库中为这些提交指定一个名称 通常,这意味着给他们一个分支名称。 您的存储库中使用分支名称没有硬性要求,因为您可以运行:

git push <remote-or-URL> HEAD:<name>

通过名称HEAD将子模块存储库中标识的提交发送到另一个 Git,并礼貌地要求它创建或更新其名称name以指向该提交。 但是大多数人不喜欢使用/使用分离的 HEAD 模式。

一般来说,这意味着要在充当某些超级项目的子模块的 Git 存储库中工作,您应该使用以下顺序: 2

  1. 进入子模块。 从这里到第 3 步,您只需将它用作常规 Git 存储库即可。

  2. 通过选择分支名称git checkout或创建指向当前提交的分支名称退出分离的 HEAD 模式 需要注意的是,如果你选择一些现有的分行名称,这可能是一个不同于当前提交提交。 或者它可能与当前提交相同

    请记住,超级项目存储库早些时候告诉这个 Git:使用这个原始提交,通过它的哈希 ID进入分离的 HEAD 模式。 我们现在正在退出分离 HEAD 模式,这需要选择或创建分支名称。 如果你选择一些现有的分支名称,你就会被分支名称选择的任何提交所困扰。 但是,如果您正在子模块存储库中开发新的提交,您可能需要一个分支名称来记住它们。

  3. 现在,以通常的方式进行新的提交。 以通常的方式使用git push 提交将或不会以提交执行或不执行的通常方式进入接收存储库。 如果他们确实进入了接收存储库,则会以通常的方式创建或更新该存储库的分支名称

  4. 一切完成后,退出子模块存储库,返回超级项目存储库。 现在是时候在superproject 中进行新的提交了。

我已经多次提到超级项目 Git 一直控制着子模块 Git。 它在子模块 Git 中执行chdir并运行git checkout hash-id 这里的关键来自两部分:

  • 超级项目 Git 什么时候对子模块做这个?
  • 超级项目 Git哪里获取原始哈希 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但是,对于子模块,它:

  • 进入子模块片刻;
  • Git:什么是原始哈希ID为提交你确定HEAD ( git rev-parse HEAD );
  • 将生成的哈希 ID(无论它是什么)填充到超级项目 Git 的索引中,在该子模块的插槽中。

无论子模块是否处于分离的HEAD状态,这都有效,因为git rev-parse HEAD始终返回当前提交的原始哈希 ID。

因此,在git add path/to/submodule选择的(实际上是制作和推送的)提交的哈希 ID 现在记录在超级项目的索引中。 您的下一次提交将记录原始哈希 ID。

假设其他一切都准备好了,您现在可以运行git commit在超级项目中进行新的提交(这大概是,并且一直处于附加 HEAD 状态,在某个分支名称上)。 一旦你提交了这个新的超级项目,你就可以像往常一样git push了。

请注意此处步骤的仔细排序:

  1. 进入子模块。
  2. 为简单起见,在子模块中的分支上进行提交,但是无论您如何进行提交都可以。
  3. 发送这些提交到哪里它是人们从克隆子模块,使Git有他们。 此步骤需要在其他 Git 中设置分支或标签名称。
  4. 现在其他人可以访问子模块的origin存储库中的提交,在 suprerproject 中创建和推送一个提交,该提交引用子模块提交的原始哈希 ID。

由于 Git 的分布式特性,可以在您的子模块中进行提交但将其推送到任何地方,然后在您的超级项目中进行提交推送它。 任何获得这个新提交的人都会得到一个提交,该提交通过原始哈希 ID 指向一个提交,他们不仅没有,而且甚至无法获得. 因此,第 3 步(“使提交对所有人可用”)必须在第 4 步中的push之前发生。(第 4 步中的“进行提交”可以更早发生——注意不要推送它,并使用任何更新的内容重做提交哈希,如果子模块提交出于任何原因必须重做。)


1失去这里的意思是“很难找到”。 提交本身不会立即消失:它们与其他丢失的提交具有相同的宽限期,您可以使用子模块 Git 的HEAD reflog 来查找它们,就像您在任何存储库中查找丢失的提交一样——因为子模块毕竟只是另一个存储库。

2因为 Git 是一组工具,而不是预先打包的解决方案,所以还有很多其他方法可以实现您的目标。 这只是一种非常灵活的方式。

3特别是git submodule update有很多更新方式 使用一些参数,您可以直接git submodule update来检查git submodule update名称,从而首先生成一个附加的 HEAD(而不是分离的 HEAD)! 这是脚注 2 所指内容的一部分。 git submodule update的详细工作非常复杂,所以我试图在这个答案中避免这些变体。

4从技术上讲,它用正确的文件内容写出一个blob 对象——或者如果可能的话,不改变地重用一些现有的 blob 对象——然后将 blob 对象的哈希 ID 写入索引,而不是实际的文件内容。 但是效果就像 Git 将文件复制到索引中一样,只要你不深入到git ls-files --stagegit update-index的级别。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM