GIT:将2个分支合并,用另一个分支覆盖其中一个分支的内容

6
我遇到了一个麻烦的问题。我的整个项目都在主分支(master)中。之前有个人在这个项目上工作,但是使用的是SVN。他进行了一些更改,我需要将这些更改与我的更改集成。两个项目具有相同的文件夹结构,唯一的区别是版本控制系统的类型。我按以下步骤操作:
  • 下载他的代码并删除.svn文件夹
  • 创建并切换到一个新的分支"code_to_integrate"(指向我的master分支)
  • 复制下载的代码并用它替换我的项目文件夹。(.git文件夹保持不变)

此时,如果我运行git status命令,我可以看到需要与我的主分支集成的更改(文件被标记为已修改)。然后,

  • 在那里提交这些更改。让它的提交ID为c2
  • 检出到我的主分支(提交ID为c1)
  • 将其与“code_to_integrate”分支合并。

结果是我的主分支中的代码被“code_to_integrate”分支中的代码覆盖。我失去了所有的修改。HEAD在c2处,我也可以看到c1。如果我使用

git reset --hard c1,

我将恢复我的更改。现在,就像使用命令

git merge -s theirs

我获得了从合并分支(code_to_integrate)中的所有更改,并且丢失了合并分支(master)中的更改。发生了什么?这应该非常简单吧?任何帮助将不胜感激。提前感谢。


你能否将问题格式化/标记以使其更易读?我的眼睛很难受,我无法正确地标记它(害怕改变您在此帖子中的主要问题)。 - bahrep
1
尝试将你的 code_to_integrate 分支变基到 master 上。然后将 code_to_integrate 分支合并到 master 上。Git 在尝试合并之前会尝试找到一个共同的提交(可能是找到一个没有你所需更改的 master 中的提交)。在合并之前,我的建议是始终变基你的更改到你想要合并的分支上。 - Shyam K
@ShyamK,我认为你也不应该在有冲突的情况下进行任何rebase操作,请参考这个答案。无论如何,rebase和merge的区别并不是这个问题的重点。我甚至不知道在线性历史中尝试rebase会发生什么。它可能会执行快进合并,这不是Shibin想要的。 - Robert Rüger
1
@Shibin,我需要你详细说明“我已经在代码库中创建了我的repo”,这是否意味着您登录到以前的SVN服务器并在SVN存储库基础上执行了git initgit add .git commit操作?我需要知道您的git repo与同事的SVN repo之间的关系。我假设该项目始于SVN,最终转换为git,但是具体是如何完成的呢? - Jarl
@RobertRüger 我之前一直使用合并工作流,但是仅仅合并代码已经导致我多次丢失数据。我的代码会被抛弃,而我所合并的分支会覆盖我的更改。(人们只会进行一次大型提交)没有冲突... 没有任何问题... 只有 Git 的神奇解释,这个需要,这个不需要。到目前为止,我从未遇到过关于 rebase 的问题 :) - Shyam K
显示剩余7条评论
1个回答

1
问题在于您在您的master分支上创建了code_to_integrate分支。所以情况看起来像这样:
... -> oldercommit -> oldcommit -> c1 (master, code_to_integrate)

当您将另一个人的代码提交到此分支时,您将获得线性历史记录:
... -> oldercommit -> oldcommit -> c1 (master) -> c2 (code_to_integrate)

当您将code_to_integrate合并到主分支时,Git会检测到c2c1新,并进行所谓的快进合并,这基本上意味着只将master指向的commit更改为c2。想象一下,code_to_integrate只是您自己创建的一个快速分支,用于做一些工作,例如修复错误。完成后,您将拥有完全相同的历史记录,快进合并正是您想要的。

为了解决问题,您需要告诉Git您和其他人的工作的最新公共祖先是哪个旧提交。基本上,您需要告诉Git在何时其他人从您的master分支分叉。您可以通过检查您和其他人在历史记录中都有的最新提交,然后从那里开始创建code_to_integrate分支来实现此目的。假设其他人从oldercommit分叉,您将执行git checkout oldercommit,然后执行git checkout -b code_to_integrate,应该会得到以下结果。

... -> oldercommit (code_to_integrate) -> oldcommit -> c1 (master)

一旦你提交了另一个人的版本,你会得到以下结果:


             -> c2 (code_to_integrate)
            /
... -> oldercommit -> oldcommit -> c1 (master)

当你将code_to_integrate合并到master中时,Git会发现有一个分歧的历史记录,并进行三方合并(其中oldercommit是合并基础),提示你解决可能出现的冲突等。这就是你想要的!
因此,简而言之,你只是从错误的提交开始了code_to_integrate分支。没有任何版本控制系统可以在不知道合并基础的情况下执行合并。
编辑:如果你自从将代码放入Git后没有与其他人分享任何更改,则你的合并基础是你历史记录中最旧的提交。以下操作应该能满足你的需求。
git checkout $(git rev-list --all | tail -n 1)
git checkout -b code_to_integrate
[ ... put the other guys code into your working directory and commit it ... ]
git checkout master
git merge code_to_integrate

第一个命令检出您历史记录中最古老的提交,这是您和SVN人员共同拥有的最后一个提交。第二个命令在您的最古老提交处创建 code_to_integrate 分支并切换到该分支。然后,将其他人的代码放入您的工作目录并将其提交到 code_to_integrate 分支。最后,切换回您的 master 分支并将另一个分支合并进去。

想象一下,code_to_integrate只是你自己为了做一些工作(例如修复一个bug)而创建的一个快速分支。一旦完成,你将拥有完全相同的历史记录,并且快进合并正是你想要的。但是,如果我的工作目录或暂存区有未提交的更改与我正在检查的分支冲突,那么这种情况就不会发生,对吧?两个分支中的同一文件的同一部分都有更改,因此必然会产生冲突。 - Shibin
"另一个人在旧提交处分支了。他没有从我的主分支分支出来。该项目最初在svn中。我下载了该项目,在codebase中创建了一个仓库,就像在github中创建一样,克隆了该仓库,添加了文件,提交并将其推送回codebase。简而言之,我将一个SVN项目转换为GIT项目。之后,那个人将更多的更改推送到了SVN中的项目。我需要获取那些更改。我不能执行上述相同的过程,因为我的版本(GIT)中也有更改。" - Shibin
他从未执行过 git branch,但实际上当你把代码从 SVN 转移到 git 中时,他已经分支出了你的主分支。这就好像你们两个都在使用 git,并且他已经从你的第一个提交中正确地创建了一个 git 分支一样。由于你的第一个提交似乎是合并基础,所以我将更新我的答案,以提供应该能够获得所需结果的 git 命令。 - Robert Rüger
非常感谢。我得到了我想要的。 你节省了我很多时间。你说的是正确的,我的合并基础是我的第一个提交。当我将“code_to_integrate”分支指向它时,一切都很顺利 :) - Shibin
很高兴听到你成功了!顺便说一下:你可以使用 git log --pretty=oneline --abbrev-commit --graph --decorate --branches --remotes --tags(命令行,你可以将 git graph 作为别名)或 gitk(图形化)将你的存储库可视化为图形。我几乎总是使用这个而不是常规的 git log,因为它使分支之间的关系更加明显。如果你使用这个命令,你可能会发现你要求 git 做的事情是不可能的;-) ... - Robert Rüger
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接