在git中,将提交从一个远程转移到另一个远程与另一个作​​者

时间:2016-12-02 18:16:46

标签: git shell

在git中,我们正在使用我们的个人帐户在bitbucket存储库中工作,但我们希望在提交完成后自动从一个bitbucket帐户转移到另一个bitbucket帐户,将所有作者更改为另一个(例如:John Doe)。

 if (url!=null && url.contains("https://m.youtube.com/channel_creation_done"))
             view.setVisibility(View.INVISIBLE);

目前我们已经完成了,但只在第一次工作,我们无法转移下一次提交:

| Remote A |           | Remote B |

Commit 1: Mike         Commit 1: John Doe
Commit 2: James        Commit 2: John Doe
Commit 3: James        Commit 3: John Doe
Commit 4: Mike         Commit 4: John Doe
Commit 5: Lara         Commit 5: John Doe

1 个答案:

答案 0 :(得分:1)

您无法更改提交

提交 - 实际上所有Git对象 - 实际上都是不可变的。原因是对象的名称是其哈希ID。 (任何其他名称,例如指向提交的分支或标记名称,仅仅是" true"哈希名称的别名。实际提交是通过哈希ID找到的,简短的,人类可读的name用于查找真实姓名。)

这意味着git filter-branch 副本提交。事实上,每个Git命令似乎都会改变提交,实际上是复制。因此,git commit --amendgit rebase也可以通过复制提交来工作。新副本是通过像往常一样通过其真实名称获取原始commit-found并根据需要构建新提交而创建的。如果新提交与旧提交一点一点地相同,则它与相同的真实名称相关,即它实际上是旧提交;但是如果作者的任何内容以任何方式被更改,则新提交会有所不同,因此具有不同的名称。

过滤分支因此复制,然后重新映射

正如我们刚刚看到的,这意味着filter-branch实际上复制每次过滤提交。它过滤(副本)的提交是你要求的,例如,--all表示"每个引用可以访问的每个提交" (包括标签引用)。它通过以下方式复制提交,至少虚拟地将其内容提取到临时树中,并将其元数据(作者,提交者,电子邮件地址和时间戳,以及提交消息)复制到临时存储中,将所有指定的过滤器应用于树和元数据,并进行制作(或跳过)使用修改后的数据和元数据的新提交。然后它会更新映射文件,以便它知道每个已过滤的旧名称的 new 名称。

(此过程非常慢,因此如果可能,filter-branch会跳过大部分内容。如果您只使用索引,环境,父级,消息和/或提交过滤器,filter-branch可以避免"提取"步骤并使用索引完成所有工作。在您的情况下,由于您甚至没有触及索引,您可以更快地进行此操作。)

映射是至关重要的,但是filter-branch会把它抛弃......等等

一旦过滤了所有提交,filter-branch命令就会运行它的最后一步:

  • 如果有注释的标签指向过滤的提交,并且您指定了--tag-name-filter,则它也会复制带注释的标记对象,并根据需要进行映射。因为标签用于与分支不同的目的,所以它与标签名称有所不同(这就是为什么这是标签 name 过滤器,而不是标签过滤器):它可以使新的标签使用不同的名称。使用cat作为标记名称过滤器会保留原始名称。
  • 然后,对于每个"肯定参考"在命令行上给出filter-branch更新引用指向新(映射)提交而不是(旧的,预复制操作)提交。当然,如果过滤器什么也没做 - 假设你没有跳过任何提交 - 新的提交旧的提交,这没有任何改变;但是如果过滤器做了某些事情,新的提交是不同的,因此这使得名称找到新分支的新提示。 (对于带注释的标签,它们已经由标签名称过滤器处理。对于轻量级标签,它们的处理方式与分支相同。)

最后,filter-branch命令会丢弃地图

这可能是个错误。除此之外,它意味着如果你有提交注释,那些注释现在只附加到原始(预复制)提交,而不是新提交。如果filter-branch保留了地图,您可以使用git notes copy使它们指向新的提交。

请注意,filter-branch 会保留所有旧提交。您可以使用refs/original/名称空间找到这些提交:filter-branch在开始提交复制之前将所有现有refs/引用复制到refs/original/。这有一些副作用,至少是学术/理论兴趣:它意味着我们可以重新计算从旧到新的映射,只要我们可以重新创建任何"跳过&# 34;从一个复杂的提交过滤器(只要它的确定性,我们当然可以那样做。)

在您的特定情况下,您不会跳过任何提交,因此这变得更容易:有一个一对一的映射可供所有"有趣的"通过refs/original/名称空间提交。为简单起见,我还假设您没有注释,或者打算在已过滤的存储库的(导出版本)中放弃它们。

如何得到你想要的东西

我没有时间详细描述这一点,或者编写代码来执行此操作,但是一旦您理解了上述所有内容,它就会非常明显。您必须停止使用--all,而是使用--branches--tags(并决定是否还需要--tag-name-filter;这取决于您是否以及如何使用标记) 。您可能希望编写自己的自定义脚本,而不是使用git filter-branch脚本,因此可以轻松复制和自定义)。

您的原始存储库 - 您描述为"远程A"上面的所有原始提交都包含所有原始名称。您想要的存储库"远程B"应该具有所有(且仅有)映射提交及其新名称。但是你希望能够更新"远程B"偶尔,同时仍然使用"远程A"。这意味着您必须在某处保留一个中间工作存储库。让我们将此存储库 F 称为过滤器,并将另外两个 - 两个外部远程存储库 - 引用为 A B

在内部存储库 F 中,您将获得来自 A 的早期快照的所有原始提交所有已过滤的远程提交您已送达 B 。您可以将refs/original/refs/headsrefs/original/refs/tags重命名为refs/saved/headsrefs/saved/tags,并丢弃剩余的"原文"引用(无论如何,确实应该没有这样的引用)。这些是已经被复制的提交。他们的副本也已被推送到 B

现在您要从 A 中选择提交。这一步很简单:只需运行git fetch即可。为方便起见,假设没有&#34;倒退&#34;使用普通的git filter-branch缓慢进行非定制的完全重新过滤,您甚至可以直接从 A refs/heads/*提取到 F &#39; refs/heads/* A refs/tags/*。对于自定义解决方案,请获取中间名称空间,例如refs/todo/,即refs/todo/heads/refs/todo/tags。 (要使用自定义解决方案处理倒带,计算倒带并将其应用于过滤后的副本;这将导致您稍后需要强制推送。这将有点棘手,所以我将忽略该问题。)< / p>

现在您只需要过滤所有提交。如果使用git filter-branch并直接提取到refs/heads/refs/tags/,您只需要使用--branches --tags作为&#34;过滤&#34;的内容的参数。这将再次重新过滤所有过滤的提交。我们依赖于使用相同过滤器重新过滤原始提交 H orig 这一事实,产生新的副本 H 使用相同的复制ID 复制 ,以便存储库 F 中的新副本附加到现有的已过滤存储库。

(因为那太慢了 - 因为你不会改变树 - 这里建议使用自定义的过滤器分支,但这需要我做更多的工作。请注意提交到然后,过滤器将来自git rev-list $(git for-each-ref --format='%(refname)' refs/todo --not $(git for-each-ref --format='%(refname)' refs/saved)。以适当的拓扑顺序获取这些内容,并附加到现有的过滤链中,这些链的提示位于常规refs/heads/refs/tags名称空间中。您可以弄清楚通过初始化refs/saved/heads/*refs/heads/*的映射,加上refs/saved/tags/*refs/tags/*,自动附加&#34;这就是回卷变得棘手的地方。)< / p>

过滤完成后,您的refs/heads/*refs/tags/*名称会更新,您可以准备推送所有新过滤的提交。您只需根据refs/saved/*中的名称更新refs/original/refs/*名称,然后照常从 F 推送到 B (如果和只有有倒带的时候。)

买者

这一切都假设存储库 B 从未添加任何提交,而与首先通过 A 发送的提交无关。如果那个不是的情况 - 如果你需要某种双向映射 - 这会变得更加困难。