如何检索已删除的代码但仍保留新代码?

时间:2016-11-04 01:11:23

标签: git

所以,我有这个合并提交案例:

           --D--E--
          /        \
--A--B--C-          ---H---
          \        /
           --F--G--

其中:

  • 提交A,B和C位于develop分支
  • 提交D,E在feature分支
  • 提交F,G在develop分支中,从另一个功能分支合并
  • 提交H合并featuredevelop

问题是,当我将feature合并到develop时,我们丢失了在分支之前引入的一些代码,例如:

在分支(A,B,C之前)之前,在文件Z中我们有:

Code 1
Code 2

feature分支(D,E)中,提交者已移除Code 2,他还添加了Code 3,因此文件变为:

Code 1
Code 3

在开发分支(F,G)中,添加Code 4,因此文件变为:

Code 1
Code 2
Code 4

现在在合并(H)之后,文件变为:

Code 1
Code 3
Code 4

但合并已删除Code 2,因为它已在feature中删除。

我想要的是什么:

Code 1
Code 2
Code 3
Code 4

合并没有显示此文件的任何冲突。

那么如何在保留新代码的同时保留旧代码呢?

需要注意的一点是,我已将此合并推送到远程仓库。

1 个答案:

答案 0 :(得分:2)

在我了解下面的所有详细信息之前,如果您只是询问如何轻松地(相对)找到已删除的代码,您可以在合并基础上运行git diff和两个提交。这是git merge合并的同一组差异。要查找合并基础,请使用git merge-base

$ merge=1234567...   # or some other way to locate the merge commit
$ git merge-base --all ${merge}^1 ${merge}^2

理想情况下,这会打印出一个提交ID,即合并基础。然后,您可以git diff针对${merge}^1(合并的第一个父级)和${merge}^2(合并的第二个父级)的哈希ID进行 A <-- branch1 / ...--* \ B <-- branch2

作为Tim Biegeleisen noted in a comment,您的图表似乎与您的文字不匹配。幸运的是,从文本中我们可以描述一个更简单的情况,其中问题来自单独的文件&#39;互动。 效果是相同的,但设置更容易:

A

这里我们在branch1上有一个提交B,在branch2上有一个不同的提交*,它们都来自一个公共基本分支,其提示为{{1} }。 (对于即将到来的合并,我们只需要关心提交*。)

假设在合并库中,prepare()中定义的函数shared.py实际上并未在任何地方使用。用于branch1意味着,现在branch1的{​​{1}}使用了。但work.py中提交B的作者决定删除 branch2,因为它从未被使用过。

您现在希望将提交prepare()A合并,或许作为B上的新提交:

branch1

Git将提交$ git checkout branch1 $ git merge branch2 与提交*进行比较:除了其他内容之外,还要说明&#34;在文件A&#34;中添加对prepare的调用。对于Git来说这没问题,因为提交work.py甚至没有触摸文件B

Git然后将提交work.py与提交*进行比较:除了其他事项之外,这说明&#34;从文件B删除对prepare的调用&#34; 。这也不是Git的问题,因为提交shared.py不会触及A的这一部分(甚至可能根本不触及shared.py。)

结果是新的合并提交:

shared.py

Git是一种工具(双关语可能不是意图),而不是解决方案

你的工作,作为合并的人,告诉Git是否正确合并。 Git会得到这个合并错误的,部分原因是谁写了提交 A---C <-- branch1 / / ...--* / \ / B <-- branch2 做错了。如果您只是运行B并推送结果,因为合并成功,那么您的错误就在那里,您将需要从中恢复。我们马上就会谈到这一点,但首先,让我们看看如何尽早避免或纠正错误。

彻底测试

这并不完美,但具有通过计算机轻松完成的优势。如果有好的测试,您可以执行git merge然后运行测试。如果测试失败,你知道合并有问题 - 所以停止使用它,然后返回并修复它。

手动扫描合并

这会捕获更多错误,但也会错过更多错误,因为它可以捕获未经过测试的问题,但依赖于错误的人类。

如果您发现在测试期间未显示的问题,您可以编写自动测试来检测它。如果这不需要太长时间,这是一个好主意。

在按

之前解决问题的选项

由于合并未在任何地方使用,您可以取消它:

git merge

放弃合并(提交$ git reset --hard HEAD^ ),以便C再次指向branch1。我们也不需要保留索引或工作树,因为Git以完全自动化的方式进行合并。我们现在必须决定如何解决问题。

选项1:恢复A

上缺少的代码
branch2

这给了我们:

$ git checkout branch2
... edit shared.py to restore the code ...
$ git add shared.py; git commit

A <-- branch1 / ...--* \ B--C <-- branch2 上创建D的合并将再次成功,但不会删除必要的代码。我们可以重新合并并重新测试。

选项2:进行合并但不提交

branch1

现在我们可以修复$ git merge --no-commit branch2 shared.py并提交。这个选项很快,但有点脏,因为我们现在有一个合并提交,它有一个由Git本身看到的问题而不是的手动修复。如果通过自动化测试发现问题,并且我们一直使用自动化测试,并且由于某种原因我们将来必须重复此合并,我们将重新发现问题。如果我们也在合并消息中对此进行评论,那可能就足够了。

此选项 的优势在于我们无需向git add添加其他提交。

选项1的一个轻微变体比其中任何一个工作更多,但对于某些案例(令人不安的branch2是一个问题)可能是最好的:使用固定版本的branch2创建一个 new 分支,然后将其合并,以便我们得到:

B

选项3:事后修复事情

当然,我们现在所拥有的是合并已被推送(或发布)。撤回已发布的合并比撤消已发布的常规(非合并)提交更难,因为还原合并会使我们在后续合并中遇到不同但相关的失败。所以我们可以离开&#34;坏&#34;那里的版本,只需修复并推动:

       A---D   <-- branch1
      /   /
...--*   C     <-- fixed-branch2
      \ /
       B       <-- branch2

提交 A---C--D <-- branch1 / / ...--* / \ / B <-- branch2 ,我们将其作为D的新提示,只是放弃了缺失的功能 - 就像我们直接修复branch1时一样。

这使branch2落后于#34;破坏了提交&#34;。这基本上只是事物的方式:人们有承诺;人们正在使用它;所以我们有点坚持下去。如果/当我们使用C来查找问题时,它会导致头痛,因为提交git bisect并不真正起作用,但唯一的选择是可以其他人< / em>从他们的存储库中清除损坏的提交C - 在这种情况下,您可以&#34;重写历史记录&#34;假装你首先使用前两个选项中的一个。

(幸运的是C 可以处理损坏的提交 - 如果要保留历史记录,可以使用git bisect在二等分期间注释这些提交以实现自动化。 http://xkcd.com/974/了解详情。:-))