在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
答案 0 :(得分:1)
提交 - 实际上所有Git对象 - 实际上都是不可变的。原因是对象的名称是其哈希ID。 (任何其他名称,例如指向提交的分支或标记名称,仅仅是" true"哈希名称的别名。实际提交是通过哈希ID找到的,简短的,人类可读的name用于查找真实姓名。)
这意味着git filter-branch
副本提交。事实上,每个Git命令似乎都会改变提交,实际上是复制。因此,git commit --amend
和git rebase
也可以通过复制提交来工作。新副本是通过像往常一样通过其真实名称获取原始commit-found并根据需要构建新提交而创建的。如果新提交与旧提交一点一点地相同,则它与相同的真实名称相关,即它实际上是旧提交;但是如果作者的任何内容以任何方式被更改,则新提交会有所不同,因此具有不同的名称。
正如我们刚刚看到的,这意味着filter-branch
实际上复制每次过滤提交。它过滤(副本)的提交是你要求的,例如,--all
表示"每个引用可以访问的每个提交" (包括标签引用)。它通过以下方式复制提交,至少虚拟地将其内容提取到临时树中,并将其元数据(作者,提交者,电子邮件地址和时间戳,以及提交消息)复制到临时存储中,将所有指定的过滤器应用于树和元数据,并进行制作(或跳过)使用修改后的数据和元数据的新提交。然后它会更新映射文件,以便它知道每个已过滤的旧名称的 new 名称。
(此过程非常慢,因此如果可能,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/heads
和refs/original/refs/tags
重命名为refs/saved/heads
和refs/saved/tags
,并丢弃剩余的"原文"引用(无论如何,确实应该没有这样的引用)。这些是已经被复制的提交。他们的副本也已被推送到 B 。
现在您要从 A 中选择新提交。这一步很简单:只需运行git fetch
即可。为方便起见,假设没有"倒退"使用普通的git filter-branch
缓慢进行非定制的完全重新过滤,您甚至可以直接从 A 的refs/heads/*
提取到 F ' 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 发送的提交无关。如果那个不是的情况 - 如果你需要某种双向映射 - 这会变得更加困难。