这些 VCS 如何处理重命名?
我发现很多相互矛盾的信息表明 git 跟踪 LOC(代码行)而不是文件,因此重命名对它没有任何意义。
这些 VCS 如何处理重命名?
我发现很多相互矛盾的信息表明 git 跟踪 LOC(代码行)而不是文件,因此重命名对它没有任何意义。
hg mv
,或者hg addremove --similarity
用于自动发现。也有一些关于在合并期间添加启发式的讨论。Git 的不同之处在于它不进行重命名跟踪,这意味着它不需要通过使用 SCM 命令进行重命名(或在提交前运行自动检测脚本来标记重命名)来告知重命名,并且不保存存储库中的此类信息,但它确实重命名了 detection。这意味着它使用基于文件名和文件内容相似性的启发式算法来查找重命名,无论是在合并期间,还是在通过-M
选项请求(或使用diff.renames
配置选项配置)时用于差异。
这种方法的优点如下:
请注意,路径规范过滤不适用于重命名检测;如果您想跨重命名跟踪文件的历史记录,请使用“ git log --follow <filename>
”
在实践中:
Git 会自动检测重命名。(顺便说一句,我听说 git 可以检测到您何时将函数从一个文件移动到另一个文件。但我最初的测试似乎表明情况并非如此。)
hg mv
使用 Mercurial,您必须通过或 的--similarity
选项或 TortoiseHg 的“猜测重命名”选项明确告诉它重命名hg addremove
,或者某些工具(例如 VisualHg)会为您标记重命名。如果你想在 Mercurial 中使用 Git 方法,我已经编写了一个扩展来在提交时检测重命名,但它目前处于非常实验性的阶段。
Subversion 根本不处理重命名。它将重命名记录为一个文件被删除和另一个文件被添加。这意味着,例如,如果 Alice 更改了一个文件,而 Bob 重命名了它,就会发生树冲突。无论您是在进行全面的分支和合并还是只是简单地svn update
进行. Subversion 1.8 计划在明年某个时候推出重命名跟踪。
你没听错,有点。
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)。
除了使用启发式方法来确定是否发生了重命名之外,还有一件关于 git 尚未提及的事情:
如果一个文件甚至整个目录树被重命名、复制或移动,并且下面的任何内容都没有被以任何方式修改,那么文件或树实际上作为同一个对象存储在存储库中,并且不会占用任何额外的空间。
如果您修改它,那么它会像往常一样存储为一个新对象。
我不确定 hg 和 svn,但我怀疑它们面向变更列表的架构意味着它们在这种情况下的行为不同。它实际上对使用没有任何影响,除了它可能让您有理由避免在存储库中移动或复制巨大的树。
git 跟踪内容,而不是文件。我不确定 mercurial,但必须明确告知 svn 重命名