【问题标题】:How to use git to merge two non-git projects如何使用git合并两个非git项目
【发布时间】:2015-03-03 22:23:47
【问题描述】:

我有两个项目最初来自我不再拥有的共同祖先。他们每个人都在他们身边进化了很长时间。现在,我想做一个合并,我想用 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<--+ 

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

但是我确信有更好的方法来做到这一点。

【问题讨论】:

  • 澄清一下:你说你没有共同的祖先,但你真的有no共同祖先吗,或者这更像是一个有共同祖先的情况?古老的“祖先,在你的两个项目中那个祖先和第一次提交之间有很大的差距?
  • @WillPalmer 好点。我真的有一个共同的祖先,但它太老了,我希望忘记它。此外,该项目托管在 ClearCase 上,速度非常慢。可以提取然后将两个分支 + 共同祖先导入 git。
  • 我的建议是尝试通过共同祖先进行合并。您想要详细说明设置这种合并所涉及的步骤的答案吗? (“重生”)
  • @WillPalmer 我认为下面的答案已经对我进行合并有很大帮助。我仍然必须选择最佳答案。我仍然感到困惑的是如何进行预合并或停止合并以切换回我目前使用git diff --stat branchb 作为减少差异量的指导的预合并状态。我做这个过程的方式特别痛苦,特别是如果我还必须管理共同的祖先。我的步骤是:1.识别差异,2.结帐A,3.全局替换差异,4.结帐A,5.返回(1)。

标签: git version-control merge


【解决方案1】:

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

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

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

  • 除了master之外,从初始提交创建两个分支:A和B

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

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

起泡、冲洗、重复并继续从 A 和 B 获取相同的代码块,将它们放入 master 中,并基于它们进行 rebase。您可以通过小而渐进的步骤来做到这一点。

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

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

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

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

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

我还建议,在您提交您认为要掌握的公共代码块并尝试在 master 的新提交之上重新设置 A 和 B 之后,标记分支 A 和 B,如果其中一个其中一个失败,你可以将另一个分支的tip恢复到之前的标签,去掉master上的新提交,然后再试一次。

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

【讨论】:

  • 你能提供一些关于变基的例子吗?我不熟悉这个 git 功能。在这种特殊情况下如何使用变基?
  • 一个 rebase 将提交重新应用到另一个 git 分支或提交。谷歌搜索“git rebase”将带回大量可以包含在简短评论中的信息。但是,假设您将分支 A 与 master 分开,再提交 1 次,然后您在 master 上并行地进行了另一次提交。如果您回到分支 A,“git rebase HEAD^ --onto master”将尝试在 master 上的最新提交之上应用最后一次提交,这是我的回答所基于的方法。如果发生冲突会发生什么,有很多细节需要学习,所以你应该先练习。
  • ... 继续。我提到的“rebase-squash”指的是交互式 git rebase,您可以在其中选择“压缩”提交,即,接受两个或多个提交并用单个组合提交替换它们。它是交互式 git rebase 中的选项之一。根据您的问题,我假设您熟悉 rebase 过程,并提出了一种使用 git rebase 来做您想做的事情的方法。所以,在我看来,在尝试以这种方式合并这两个项目之前,您需要先复习一下它,并了解它是如何工作的。
【解决方案2】:

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

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

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

【讨论】:

  • 这是一个非常有趣的观点。但是如何暂停合并过程并跟踪我的位置、合并了多少文件以及未合并的文件?
【解决方案3】:

我肯定会建议使用Sam's suggestion 附近的东西来重建历史,更清楚地显示 A 和 B 的共同点。

然而,为了“暂停手动合并,将补丁应用到 B,然后恢复手动合并”的唯一目的,git stash 看起来是您需要的工具:

  1. 如您所说,开始使用git difftool
  2. 当点击一些你想同时应用到 A 和 B 时,将你当前的更改保存在 stash 中:

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

    git checkout BranchB
    # do whatever you want
    git add / git commit
    
  4. 返回BranchA

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

    git stash pop
    
  6. git difftool BranchB ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    • 2019-04-15
    • 2015-04-19
    • 2017-06-08
    • 2011-02-17
    • 1970-01-01
    相关资源
    最近更新 更多