两个 git 仓库意外合并,如何清理

Two git repos accidentally merged, how to clean up

我的一个客户不小心在他的 .git/config 文件中将另一个项目设置为远程项目,然后拉取、提交和推送。

这意味着回购 "A"(主要文件)中的所有文件现在都在回购 "B"(次要文件)中。不幸的是,已经有几个人从 "B".

从 "B" 中删除 "A" 的所有文件、提交和历史记录的最佳方法是什么?

我假设您的存储库看起来像这样,其中 A-C 在正确的存储库中提交,而 1-3 在错误的存储库中提交。 D 是拉动后两者之间的合并点(大概 pull --force 除非你知道有一个变基,否则你不应该这样做)。

... A - B - C - D [master] [origin/master]
               /
... 1 -  2 -  3

解决此问题所需要做的就是将 master 移回 C。由于 Git 中的分支只是指向提交的标签,因此移动它们很便宜。这就是 git reset 所做的(git reset -- 完全做其他事情)。

git checkout master
git reset --hard C

--hard 表示丢弃索引并使工作目录也与提交匹配。现在您的存储库看起来像这样。

... A - B - C [master] - D [origin/master]
                        /
           ... 1 - 2 - 3

现在 git push --force 在远程存储库中进行相同的操作。没有其他引用它,有问题的提交最终将被垃圾收集。

... A - B - C [master] [origin/master]

如果在错误的合并之上进行工作,这会变得有点复杂。

... A - B - C - D - E - F - G [master] [origin/master]
               /
... 1 -  2 -  3

你想做和以前一样的事情,但要抓住 G。你可以通过记下提交 ID 来做到这一点,或者你可以标记它以确保安全。

git tag old-master master

看起来像...

... A - B - C - D - E - F - G [master] [origin/master] (old-master)
               /
... 1 -  2 -  3

像以前一样进行重置...

... A - B - C [master] - D - E - F - G [origin/master] (old-master)
                        /
         ... 1 -  2 -  3

但是现在你想把 E、F 和 G 挂在 C 之外。你可以用变基来做到这一点。这将把 E、F 和 G 作为补丁并将它们一个接一个地应用到 C 上。

git rebase --onto master D old-master

更改将具有新的提交 ID,因为 git 中的每个提交 ID 都取决于其父项的 ID(这就是推送和拉取速度如此之快的原因)。

              E1 - F1 - G1 [master]
             /
... A - B - C - D - E - F - G [origin/master] (old-master)
               /
... 1 -  2 -  3

现在您可以push --force掌握和删除旧的掌握。

... A - B - C - E1 - F1 - G1 [master]

由于存储库从它们下面更改出来,所以已经从存储库中提取的每个人都必须 git pull --force。他们应该 git pull --rebase --force。拉取通常是获取和合并。 --rebase 把它变成一个 fetch 和一个 rebase。使用 rebase 会将用户未推送的工作作为补丁应用到新存储库之上。这可以防止错误的存储库通过 pull/merge.

返回

例如,用户的存储库可能如下所示,其中 H 和 I 是他们自己未推送的更改。我也会把固定的远程存储库放上来进行比较。

[local]
... A - B - C - D - E - F - G [origin/master] - H - I [master]
               /
... 1 -  2 -  3

[remote]
... A - B - C - E1 - F1 - G1 [master]

修复远程存储库后,如果他们拉 Git 将拒绝合并,因为 G 不是 G1 的祖先,他们必须强制。分开显示 fetch 和 merge/rebase 更简单。所以 git fetch origin 将导致...

[local]
              E1 - F1 - G1 [origin/master]
             /
... A - B - C - D - E - F - G - H - I [master]
               /
... 1 -  2 -  3

[remote]
... A - B - C - E1 - F1 - G1 [master]

git merge origin/master,这是 git pull --force 会做的,会导致这个...

               - - - - - - E1 - F1 - G1 [origin/master]
             /                         \
... A - B - C - D - E - F - G - H - I - J [master]
               /
... 1 -  2 -  3

我们不希望这样,它会从错误的存储库中恢复历史记录。 git pull --force --rebase 将改为执行 git rebase origin/master,这会将 H 和 I 置于 origin/master.

之上
[local]
... A - B - C - E1 - F1 - G1 [origin/master] - H1 - I1 [master]

现在他们可以继续正常工作了(不需要立即推送)。