告诉git跟踪移动的内容(而不仅仅是移动的文件)

16

在重构源代码时,有时需要将大块文本移动到文件中的其他位置,甚至移到新文件中。创建一个名为refactored的分支,然后提交更改:

$git checkout master
$git branch refactored
$git checkout refactored
<move code around>
$git commit -m "refactored code"

然而,人们可能会在旧的重构前分支顶部进行提交,改变已经被移动的代码:

$git checkout master
<change code that was moved elsewhere on branch refactored>
$git commit -m "bugfix"

你现在在分支 refactored 上,想要合并 master 分支上的更改:

$git checkout refactored
$git merge master
<giant merge conflict>

这导致了一个大的合并冲突。如果有一种方法可以告诉Git这些内容只是被移动了,那么它应该可以自动合并。

更糟糕的是,即使在解决冲突并提交后,Git仍然不能使用解决方案来进一步合并:

<fix conflicts>
$git commit -m "merge master into refactored"
$git checkout master
<change more code>
$git commit -m "bugfix2"
$git checkout refactored
$git merge master
<another giant merge conflict>

这个问题有什么可避免的吗?我尝试过git rerere,但它不能解决这里的冲突。是否有任何方法可以让git将文本块的移动视为实际的移动,而不是删除和插入?如果不能,那么在需要保留两个平行分支一段时间的情况下,最好的方法是什么,以最小化合并冲突?

虽然这对于移动完整文件的内容很容易,但我找不到有关移动其中部分或在同一文件中移动的信息。

此外,如果有解决方案,重构代码时git blame的行为会是什么?它会指向重构提交,还是忽略它?有办法实现后者吗?

如果有人感兴趣,我在pastebin上放了一个(非常简单的)测试库的base64编码tar.gz

可能的解决方案

一个潜在的解决方案可能是通过应用包含来自预重构分支中更改的(自动编辑的)补丁来执行合并。有开发的软件可以做到这一点吗?使用这种方法,我猜由于这对git是透明的,git blame将指向重构提交。

我在相同的问题中找到了应用于diff的解决方案。没有提到任何现有的非专有实现,但提到了一种追踪块移动的算法。


我建议你提交更多的代码。当你改变很多数据时,有更多的提交,合并代码时出现问题的可能性会更少。 - sensorario
@sensorario 这是一个很好的建议。不过,我不确定如何将其应用于重构 - 一次移动一个函数/小块并不能帮助到什么程度。 - loopbackbee
1
@sensorario 我已经尝试过仅移动一个长度为3行的块,并在master中更改中间行,但仍然存在冲突。 - loopbackbee
重构后的代码库在主分支上进行变基怎么样?你还会遇到合并冲突吗? - Atropo
这确实是git合并的一个失败。答案是有人在为git合并或其替代品的功能请求进行黑客攻击。 - Chris Moschini
显示剩余2条评论
2个回答

2
无法避免这种额外的努力,但这正是 Git 的工作方式:
另一个基本的智能设计决策是 Git 如何合并。合并算法很聪明,但它们不会试图太聪明了。自动做出明确的决策,但当存在疑问时,由用户决定。这就是应该的方式。你不希望机器为你做出这些决定。你永远不会想要它。这是 Git 合并方法的基本见解:虽然每个其他版本控制系统都在尝试变得更加智能,但 Git 快乐地自称为“愚蠢的内容管理器”,这对它来说更好。
(来自 Wincent Colaiuta 的博客)

1
很抱歉,据我所知,您无法替换内置的git合并策略。这意味着您无法避免冲突,但是您可以使用智能工具来解决它们。语义合并看起来很有趣,也可以被Git使用

1
我已经在示例存储库上尝试了SemanticMerge。首先,在Linux上,它引入了80MB的依赖项 - 来自plasticscm的许多软件包和似乎是mono。合并必须使用git mergetool手动完成,并且该工具需要交互(“按回车键开始”)并使用GUI(无法从CLI运行,据我所知)。最后,在我的示例中,它未能合并冲突,显示“找到解析错误,树可能不一致”。我不确定该工具是否实际执行任何操作,因为然后我会得到一个错误“对象引用未设置为对象的实例”。 - loopbackbee
公平地说,我已经在Ubuntu 12.04 LTS(Precise Pangolin)上尝试过了,这并不一定得到支持(“我们仅保证对Debian 6.0提供支持,但此存储库也可能针对更新的Debian版本(例如7.0)和Ubuntu发行版(特别是LTS版本)进行定位”)。 - loopbackbee

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