复制git存储库后,如何找回共享历史记录?

时间:2018-08-09 02:13:48

标签: git azure-devops rebase

很久很久以前,有人在一个遥远的办公室里复制了一个github存储库,并将其上传到Visual Studio Team Services(VSTS)。我们开发人员愉快地进行了编码,开发了功能并修复了VSTS中的错误。现在是时候将我们的代码重新发布到开源社区的热爱者了……

不幸的是,我们的VSTS存储库与github存储库没有共享历史记录,因为它是副本,而不是克隆。虽然我们可以将github存储库添加为远程存储库,但是将我们的代码合并回主要分支中却是令人讨厌的冲突。整个文件夹结构已被移动或重命名,并且开源开发人员已对github存储库中的这些文件进行了更改。

有没有办法将我们的分支挂回到它们的来源?像将整个分支树重新放置到复制存储库时github上的最后一次提交上一样?

我想出的最好的办法是将VSTS中的每个CL都挑选到github上,这听起来像是一些认真的侦探工作,弄清楚了在哪里插入重命名。

2 个答案:

答案 0 :(得分:2)

通常很难将非克隆与实际克隆相结合。

让我们写一个理论示例,使用git://github.com/repo作为原始示例。假设ssh://example.com/copy.git将代表您使用以下命令序列设置的存储库:

<download tarball or zip file from github.com/repo>
<extract tarball or zip file into directory D>
$ cd D
$ git init
$ git add .
$ git commit -m initial -m "" -m "imported from github.com/repo.git"

之后,您从该独立存储库中创建了一个位于--bare的{​​{1}}存储库。

现在已经过了一段时间,您已经意识到您想使用ssh://example.com/repo.git的实际克隆。 las,您的github.com/repo.gitssh://example.com/repo.git没有共享历史,也没有共同的提交。正在运行:

git://github.com/repo.git

让您获得所有公共提交,但是尝试将$ git clone ssh://example.com/repo.git combine $ cd combine $ git remote add public git://github.com/repo.git $ git fetch public 与您自己的私有public/master合并是很麻烦的。

在某些非常特殊的情况下,解决这个问题实际上并不难。诀窍在于比较master可以访问的combine存储库中的 root commit master信息库中所有可以访问的所有提交combine远程跟踪名称。如果幸运的话,一个提交的public/*与您自己的根提交的tree完全匹配,因为您得到的tarball或zip文件生成了一棵相同的树。

如果您很幸运,则不会进行此类提交。在这种情况下,您也许可以找到“足够接近”的提交。但是,假设您 did 找到了一个tree可以到达的,与您自己的根提交完全匹配的提交:

public/master

在这里,大写字母A--B--...--o--o <-- master (HEAD), origin/master \ ... (there may be other branches) C--...--R--...--o <-- public/master 代表您自己的根提交(从下载的tarball或zip文件中创建的根提交)的实际哈希ID,而A是紧随其后的提交一。 B代表可以从C到达的(或某些)根落实,并且主要在图中仅用于说明:我们可以肯定的是,至少还有一个这样的根(无父母)落实。 字母public/master代表与您的提交R完全匹配的提交,这是当前最有趣的提交。

我们现在想做的是假装 第二最有趣的提交A的父级是提交B而不是提交R。我们做得到! Git有一个名为A的工具。 git replace的作用是在进行一些更改的同时复制一个对象。在我们的例子中,我们想要的是将提交git replace复制到看起来与B几乎完全一样但有一个改变的新提交B':其父项。我们希望B列出提交A的哈希ID,而不是将提交B'的哈希ID作为B'的父项。

换句话说,我们将拥有:

R

现在我们要做的就是说服Git,当它查找提交A---------B--...--o--o <-- master (HEAD), origin/master B' / C--...--R--...--o <-- public/master 时,它应该注意到有一个 replacement 提交B,并迅速避免了它的提交。从B'注视着B。这就是B'的其余工作。因此,找到提交git replaceR后,我们运行:

B

现在Git 假装该图显示为:

git replace --graft <hash-of-B> <hash-of-R>

(好吧,除非我们运行 B'-...--o--o <-- master (HEAD), origin/master / C--...--R--...--o <-- public/master 来观察现实,否则Git会假装这个。)

最大的缺点,也许很小的缺点

除了查找提交git --no-replace-objects相当艰巨的工作之外,查找RA非常容易,它们是B列出的最后两个哈希ID。 git rev-list --topo-order master技巧有缺陷。替换提交git replace现在存在于我们的存储库中,但通过特殊名称B'中定位,其中 refs/replace/hash 是原始提交hash的哈希ID。默认情况下,此替换对象(及其名称)不会发送到新的克隆

您可以制作具有替换对象及其名称的克隆,并对其进行处理,一切正常。但这意味着每次有人克隆您的B存储库时,他们都必须运行:

combine

或类似的命令(此特定规则只是将克隆的git config --add remote.origin.fetch '+refs/replace/*:refs/replace/*' 命名空间从属refs/replace/的命名空间,这很粗糙,但有效)。

或者,您可以声明flag day并运行origin或类似方法以将替换固定在适当的位置。我已经在其他地方对此进行了描述,尽管目前我能找到的最好的答案是我对How can I attach an orphan branch to master "as-is"?的回答,实际上,您创建了一个 new 存储库,其中包含git filter-branch而不是{{1 }},没有B',并且具有作为B 的后代的每个提交的新副本(除了父哈希ID以外,其他内容相同)。然后,所有用户都从旧的A切换到新的用户。这很痛苦,但是只有一次。

如果您不打算长时间使用组合存储库,则可能没有关系。

除上述内容外,您还可以使用嫁接的历史记录进行合并-Git命令通常会跟随替换项-之后,您可能不需要替换嫁接项。在这种情况下,缺点是短暂的:它只会持续到合并代码为止。

答案 1 :(得分:0)

假设VSTS仓库是Git仓库,您可以:

  • 克隆您的GitHub存储库
  • 通过正确的提交创建新分支
  • 使用VSTS分支的第一次提交的镜像副本覆盖工作树内容(以避免任何冲突解决)。然后添加并提交。
  • VSTS的
  • git cherry-pick(作为远程添加并提取),将VSTS master分支的所有提交提交到新的本地分支(没有冲突)
  • 将新分支推回GitHub存储库