处理重命名:svn vs. git vs. mercurial

47

这些版本控制系统如何处理重命名操作?

我发现很多矛盾的信息声称git跟踪代码行数而不是文件,因此对于它来说,重命名将没有意义。


2
bzr非常努力地在重命名方面做正确的事情:http://www.markshuttleworth.com/archives/123。但实际上如何呢?我不知道。我主要使用git和svn。 - bendin
3
马克·S非常努力地传播有关其他版本控制系统在重命名方面不安全的FUD。 - tonfa
5
Bzr可能确实做到某件事情很好,但我发现系统的几乎所有其他方面都令人难以忍受。我使用Bzr的唯一原因是它与Launchpad集成,但Launchpad也非常令人沮丧,并将你锁定在它们的版本控制系统中。这是Canonical让我真正不喜欢他们的一件事情。 - Dustin
6个回答

44
  • Git不会跟踪重命名操作,但在合并等操作中使用启发式方法重新发现它们。
  • Mercurial会跟踪重命名操作(记录原始版本和原始文件),并在合并操作中使用此信息。因此,您必须使用hg mv明确告诉hg有关重命名操作,或者使用hg addremove --similarity进行自动发现。目前已经有一些讨论,考虑在合并操作中添加启发式方法。
  • Svn会跟踪重命名操作,但我不知道它如何在合并操作中处理这些操作(从未实际测试过)。

2
当您尝试将文件合并到已重命名的文件时,SVN会生成一个树冲突,以便用户可以正确处理它。较旧版本只会输出一个“跳过”警告,表示合并目标不存在。 - Wim Coenen
7
SVN不追踪重命名。在SVN中,重命名被记录为删除一个文件并添加另一个文件。SVN 1.8计划引入重命名追踪功能,预计将在明年发布,但其效果有待观察。 - jammycakes
4
在SVN中重命名跟踪计划在1.4、1.5和1.6版本中实现。我怀疑将重命名跟踪与版本1.8关联只是一种聪明的方式来表达“尚未”。 - deft_code
2
据我的经验,SVN可以跟踪重命名。我曾看到过它记录了一个文件被删除,另一个文件从原始文件夹中被复制出来的情况,而不仅仅是添加了一个新文件。 - Ole Lynge
1
@OleLynge:是的,这正是关键。SVN目前将重命名跟踪为“复制和删除”。然而,它并没有记录复制和删除是一个重命名的一部分。 - sleske
显示剩余4条评论

23

Git

Git与其他版本控制系统不同,它没有进行重命名跟踪,这意味着它不需要通过SCM命令告知重命名操作(或运行自动检测脚本标记重命名操作并在提交前记录),也不会将此类信息保存在仓库中,而是进行重命名检测。这意味着它使用基于文件名和文件内容相似性的启发式算法来查找重命名操作,在合并过程中以及当请求使用-M选项(或使用diff.renames配置选项进行配置)时进行差异对比。

这种方法的优点如下:

  • 重命名操作不需要显式地标记(或检测):可以通过补丁来实现重命名,也可以通过文件管理器或图形界面来实现
  • 相似性检测算法可以改进,并且不像在检测重命名操作以标记它们之前被冻结在提交时,在历史上它是不会被冻结的;如果存在重命名检测错误,则处理起来也更容易
  • 它遵循了Git的哲学,即内容才是最重要的。参见git blame(以及类似“git gui blame”的图形界面),它可以跟踪代码块的移动,即使跨越文件边界,这比整个文件的重命名更通用。
  • 同样的机制也负责处理合并期间的重命名操作;使用重命名检测意味着它可以针对最终基于的3个提交来完成,无需仔细跟踪历史记录或记录文件的规范名称等非分布式概念。

请注意,路径模式过滤与重命名检测不兼容;如果要跟踪文件在重命名操作中的历史,请使用“git log --follow <filename>”。


2
“git log --follow” 是一个隐藏的宝石。 - Gregg Lind
1
不在提交时冻结重命名检测对我来说好像是一把双刃剑。是的,可以稍后开发改进的重命名检测启发式算法,并且“免费”提高过去重命名的检测能力,但是另一方面,今天我可以运行 git log --follow xyz,而明天,在 xyz 之间没有任何人更改的情况下,结果可能会有所不同。 - j_random_hacker

10

实际上:

Git会自动检测重命名。(顺便说一句,我听说git可以检测您将一个函数从一个文件移动到另一个文件的情况。我的初步测试似乎表明这不是事实。)

对于Mercurial,您必须明确告诉它有关重命名的信息,可以通过hg mvhg addremove--similarity选项,或TortoiseHg的"猜测重命名"选项,或某些工具(例如VisualHg)将为您标记重命名。如果您想使用Git方法来处理Mercurial中的重命名,我编写了一个扩展程序,在提交时检测重命名, 但目前处于非常试验阶段。

Subversion根本不处理重命名。它记录重命名为一个文件被删除和另一个文件被添加。这意味着,例如,如果Alice更改了一个文件而Bob重命名了它,则会出现树冲突。无论您是进行全面的分支和合并还是仅进行svn update,都会发生这种情况。 Subversion 1.8计划增加重命名跟踪功能,预计在明年发布。


6
您听说得没错,但有所不同。 Git 操作的是文件内容而不是文件本身,因此对于 Git 来说,重命名在技术上是没有意义的。对于 Git,重命名看起来像是文件 A 消失了,而文件 B 出现了,并且具有与 A 相同的内容。但事实上,Git 在确定文件是否被重命名方面非常擅长。
您可以试一下:重命名一个文件,然后执行 'git rm oldname' 和 'git add newname' 命令告诉 Git 将更改暂存,然后运行 'git status' 命令查看 Git 认为自己正在做什么--您会看到它告诉您文件已被重命名。不过我不确定之后是否还有任何意义。查看一个提交时使用 'git show' 命令,您将不会看到有任何关于重命名的提及,只会看到一堆从一个路径中删除的行和添加到另一个路径中的行。
另外,您也可以使用 'git mv' 命令重命名文件。这并不会改变 Git 的操作方式,它只是将 'mv oldname newname'、'git rm oldname' 和 'git add newname' 效果合并成一步。
有关 Mercurial 的概述,请参见 tonfa 的答案
另一方面,SVN 无法检测到重命名,但必须使用 'svn mv' 命令告知它们。不过只要告知了,它就会将重命名视为"第一类"更改,因此在稍后查看更改日志时,您将看到该更改是一个重命名操作。
然而,我不建议仅基于这个特性来选择 SVN 而不是 Git 或 Mercurial。这些工具之间的差异更大、更重要。我首先要确定您想要分布式版本控制系统(Git 或 Mercurial)还是集中式版本控制系统(SVN)。

1
这不是一场枪战。我正在使用所有这些工具,其中mercurial是我个人项目的首选工具,因为我有选择权。 - Johannes Rudolph

3
关于git还有一件事情没有提到,除了使用启发式算法来确定重命名操作是否已经发生之外:
如果一个文件或整个目录树被重命名、复制或移动,而且其下面的任何内容都没有被修改,那么文件或目录实际上作为同一个对象存储在仓库中,不会占用任何额外的空间。
如果修改了它,则像往常一样存储为新的对象。
我不确定hg和svn,但我认为它们的基于更改列表的架构意味着它们在这种情况下的行为是不同的。除了可能给您避免在您的存储库内移动或复制大型目录树的原因之外,它并不会对使用产生任何影响。

0

Git跟踪内容,而不是文件。我不确定Mercurial如何处理,但SVN需要明确告知重命名操作。


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