如何使用 git 合并两个非 git 项目

How to use git to merge two non-git projects

我有两个项目最初来自一个我不再拥有的共同祖先。他们每个人都在很长一段时间内朝着自己的方向发展。现在,我想做一个合并,我想使用 git 来做到这一点。

我创建了这个存储库:

 *   Project A [BranchA]
 | * Project B [BranchB]
 |/ 
 * Initial init (Project A)

首先我想找出不同的文件:

 git checkout BranchA
 git diff --stat BranchB

我可以通过

轻松地执行从 BranchB 到 BranchA 的手动合并
 git difftool BranchA

并将修改保存到正确的文件中。但是我有时想修改 BranchA 和 BranchB 的两个文件。例如,如果我注意到 BranchA 上的变量名更改,我可以停止合并,替换 BranchB 中的变量名,提交 BranchB 上的更改并继续手动合并。

此时我会迷路,因为我中断了合并并且没有保留我正在处理哪个文件的任何痕迹。 git diff --stat BranchB 对我没有帮助,因为即使我成功地将更改从 BranchB 导入到 BranchA,它仍然会存在一些差异。

值得一提的是,我无法使用 git merge,因为 git 上没有可用的共同祖先,因为我在一个空的 git 存储库中导入了两个截然不同的项目。在这种情况下 git merge 会做得很糟糕。所以我正在寻找更好的解决方案,以便:

  1. 使用 difftool 浏览两个分支中的每个文件,推送并获取差异,直到两个文件完全相同。
  2. 使用 git 跟踪我的更改,并在需要时帮助我回滚到以前的版本。
  3. 使用git跟踪与git diff --stat的合并过程。

我找到的唯一解决方案是初始化一个包含 BranchA 和 BranchB 的裸存储库。然后我将它克隆到两个本地目录中。他们每个人都指向不同的分支。

                              +-->[ ]<--+ Bare repository
                              |         |
                    Branch A [ ]       [ ] Branch B
                              |         |
                              +->diff<--+ 

由此我可以使用我最喜欢的 merge/diff 工具在两个分支上手动修改我的文件。我可以随时 commitpushpull 我的工作。

不过我确信有更好的方法。

听起来您对存储库的初始提交是项目 A 的副本,然后您从该项目分支并在分支上替换为 B。您是否尝试过让父提交只是一个空提交?我认为这会给 git 一个更好的正确合并的机会,因为它不是假设项目 B 中的所有更改都是对项目 A 的编辑的结果。所以你的 git 树看起来像这个:

*   A
| * B
|/
*   Initial Commit (empty)

借助变基的魔力,您甚至可以开始将一些真正通用的代码放入父级中,这也可能有所帮助。

您是否需要尽可能多地保留项目 A 和 B 的历史记录?

如果没有,我建议采用以下方法:

  • 创建一个 repo 和一个初始的空提交。

  • 除了 master,在初始提交后创建两个分支:A 和 B

  • 将项目 A 和 B 的快照放入各自的分支。

此时,使用您知道如何使用的现有工具来找到两个分支的相同部分,并将相同部分提交给 master。然后,您应该能够将分支 A 和 B 变基到 master 上的新提交上。

起泡、漂洗、重复,并继续从 A 和 B 中获取大块相同的代码,将它们放入 master 中,并基于它们进行变基。您可以逐步执行此操作。

在这个过程中的每一步,您都应该在 A 和 B 分支上进行一次提交,这是从 master 分支上分叉出来的。

在某些时候,您将到达需要查看差异的步骤。让我们以分支A和B上相同变量的不同名称的假设情况为例

好吧,将一个分支上的变量重命名为另一个分支的名称,然后对其进行 rebase-squash,这样您仍然可以在 A 和 B 分支上进行一次提交。这应该最终导致两个分支上的更多代码变得相同,您可以将其拉出,提交到 master 上,然后在其上重新设置 A 和 B 分支的基线。

当你达到 A 和 B 之间没有任何共同点的地步时,它们都与 master 相差一个提交,并且 none 它们改变了重叠的部分,那么你应该能够将一个分支合并到另一个分支;可能会解决一些小的冲突。

在此过程的每一步,您的更改都应该相当小,您不需要使用复杂的差异或合并工具来完成它。

我还建议,在您将您认为是公共代码块提交给 master 并尝试在 master 的新提交之上对 A 和 B 进行变基后,标记分支 A 和 B,如果其中一个失败了,你可以将另一个分支的提示恢复到之前的标签,去掉master上的新提交,然后再试一次。

这个过程的另一个改进是,在创建初始 repo 之后,而不是使用两个分支 + master,克隆 repo 目录两次,然后使用两个新的 repos,本质上是分支:隔离相同的代码后,提交该块到父回购,重新拉动子回购中的父回购,并尝试重新设置子回购中的分支。

我肯定会建议一些接近 的东西来重建历史,更清楚地显示 A 和 B 的共同点。

然而,"pausing a manual merge, applying a patch to B, and resume the manual merge" 的唯一目的是,git stash 看起来像您需要的工具:

  1. 开始使用你提到的 git difftool
  2. 当点击一些您想同时应用于 A 和 B 的内容时,将您当前的更改保存在存储中:

    git stash
    
  3. 转到 BranchB,应用您的更改:

    git checkout BranchB
    # do whatever you want
    git add / git commit
    
  4. return 到 BranchA

    git checkout BranchA
    
  5. 从存储中检索您的更改

    git stash pop
    
  6. git difftool BranchB ...