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

Posted

技术标签:

【中文标题】处理重命名:svn vs. git vs. mercurial【英文标题】:Handling renames: svn vs. git vs. mercurial 【发布时间】:2010-12-04 23:44:09 【问题描述】:

这些 VCS 如何处理重命名?

我发现很多相互矛盾的信息表明 git 跟踪的是 LOC(代码行)而不是文件,因此重命名对它没有任何意义。

【问题讨论】:

bzr 非常努力地做正确的事情 WRT 重命名:markshuttleworth.com/archives/123。实际情况如何?我不知道。我主要使用 git 和 svn。 马克 S. 非常努力地传播关于其他 VCS 不安全的 FUD。重命名... bzr 可能会很好地做到这一点,但我发现系统的几乎所有其他方面都非常令人沮丧。我工作的唯一一个使用 bzr 的东西使用它是因为启动板也非常令人沮丧,并将您锁定在他们的修订控制系统中。这是 canonical 做的一件事让我真的不喜欢他们。 【参考方案1】:

git 跟踪内容,而不是文件。我不确定 mercurial,但 svn 必须明确告知重命名

【讨论】:

【参考方案2】:

你没听错,有点。

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's answer。

另一方面,SVN 无法检测重命名,但必须使用“svn mv”命令告知它们。但是,当被告知时,它会将重命名跟踪为“第一类”更改,因此稍后查看更改日志时,您会看到更改是重命名。

不过,我不建议基于此功能选择 SVN 而不是 git 或 mercurial。这些工具之间存在更大和更重要的差异。我首先要决定你是想要一个分布式版本控制系统(git 或 mercurial)还是一个集中式版本控制系统(svn)。

【讨论】:

这不是枪战。我正在使用所有这些工具,在我可以选择的个人项目中,merucrial Beeing 被用作我的首选工具。【参考方案3】: Git 根本不跟踪重命名,而是在合并等过程中使用启发式方法重新发现它们。 Mercurial 轨道重命名(记录原始版本和原始文件)并在合并期间使用该信息。所以你必须用hg mv明确告诉hg关于重命名,或者使用hg addremove --similarity进行自动发现。也有一些关于在合并期间添加启发式的讨论。 Svn 跟踪重命名,但我不知道它在合并期间对它们的处理效果如何(从未实际测试过)。

【讨论】:

SVN 在尝试合并到已重命名的文件时会产生树冲突,以便用户决定如何正确处理。旧版本只会输出“已跳过”警告,说明合并目标不存在。 SVN 不跟踪重命名。 SVN 中的重命名记录为一个文件被删除,另一个文件被添加。 SVN 1.8 计划在明年某个时候推出重命名跟踪,但其效果如何仍有待观察。 SVN 中的重命名跟踪计划用于 1.4、1.5 和 1.6。我怀疑将重命名跟踪附加到 1.8 版只是一种巧妙的说法还没有 @jammycakes:据我所知,SVN 跟踪重命名。我看到它记录为一个文件被删除,另一个文件从原始文件中复制,而不仅仅是作为新文件添加。 @OleLynge:是​​的,这就是重点。 SVN 当前跟踪重命名为“复制和删除”。但是,它并没有记录复制和删除是一次重命名的一部分。【参考方案4】:

Git

Git 的不同之处在于它不进行重命名跟踪,这意味着它不需要通过使用 SCM 命令来进行重命名(或运行自动检测脚本来标记在提交之前重命名),并且不会将此类信息保存在存储库中,但它会重命名检测。这意味着它使用基于文件名和文件内容相似性的启发式算法来查找重命名,无论是在合并期间,还是在通过 -M 选项(或使用 diff.renames 配置选项进行配置)请求时的差异。

这种方法的优点如下:

重命名不需要明确标记(或检测):重命名可以来自补丁,也可以通过文件管理器或图形界面完成 相似度检测算法可以改进,在提交时不会像在提交前检测重命名以标记它们的情况那样在提交时冻结,并将此信息保存在存储库中;如果重命名检测错误没有被冻结在历史记录中,也更容易处理它们 遵循 Git 哲学,内容才是最重要的;看看git blame(以及它的图形前端,如“git gui blame”)如何跟踪代码块跨文件边界的移动,这比文件的整体重命名更通用。 相同的机制负责在合并期间处理重命名;使用重命名检测意味着它可以针对合并最终基于的 3 个提交完成,并且无需仔细跟踪历史记录,注意每次重命名 - 或文件规范名称的非分布式概念

请注意,路径规范过滤不适用于重命名检测;如果您想跨重命名跟踪文件的历史记录,请使用“git log --follow <filename>

【讨论】:

那个 'git log --follow' 是一个隐藏的宝石。 在提交时不冻结重命名检测对我来说似乎是一把双刃剑。是的,可以稍后开发改进的重命名检测启发式并“免费”改进对过去重命名的检测,但是我可以在今天和明天运行 git log --follow xyz,而无需任何人在两者之间更改 xyz,并获得不同的结果。 【参考方案5】:

关于 git 的另外一件事还没有提到,除了使用启发式方法来确定是否发生了重命名:

如果一个文件甚至整个目录树被重命名、复制或移动,并且下面的没有以任何方式被修改,那么文件或目录树实际上作为同一个对象存储在存储库,并且不占用任何额外空间。

如果你修改它,那么它会像往常一样存储为一个新对象。

我不确定 hg 和 svn,但我怀疑它们面向变更列表的架构意味着它们在这种情况下的行为不同。它实际上对使用没有任何影响,但它可能会让您有理由避免在存储库中移动或复制大树。

【讨论】:

【参考方案6】:

在实践中:

Git 会自动检测重命名。 (顺便说一句,我听说 git 可以检测到您何时将函数从一个文件移动到另一个文件。My initial tests 似乎表明情况并非如此。)

使用 Mercurial,您必须通过 hg mvhg addremove--similarity 选项或 TortoiseHg 的“猜测重命名”选项或某些工具(例如VisualHg 将为您标记重命名。如果你想在 Mercurial 中使用 Git 方法,我已经写了一个 extension to detect renames at commit time,但它目前处于非常实验阶段。

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

【讨论】:

以上是关于处理重命名:svn vs. git vs. mercurial的主要内容,如果未能解决你的问题,请参考以下文章

开发小结-杂项类

通俗易懂的Git vs SVN比较

Git vs SVN:性能和存储

将具有重命名中继历史的SVN仓库导入Git

git + vs2015

VS2010中重命名项目