重新格式化和版本控制


23

代码格式很重要。甚至缩进也很重要。一致性比微小的改进更为重要。但是,项目通常从第一天起就没有清晰,完整,可验证强制的样式指南,并且重大改进可能会在任何一天出现。也许你发现

SELECT id, name, address
FROM persons JOIN addresses ON persons.id = addresses.person_id;

可以更好地写成/比

SELECT persons.id,
       persons.name,
       addresses.address
  FROM persons
  JOIN addresses ON persons.id = addresses.person_id;

同时在查询中添加更多列。也许这是代码中所有四个查询中最复杂的查询,或者成千上万个简单查询。无论过渡有多困难,您都认为值得。但是,如何跟踪主要格式更改中的代码更改?您可以放弃并说“这是我们再次开始的地方”,也可以重新格式化整个存储库历史记录中的所有查询。

如果您正在使用像Git这样的分布式版本控制系统,则可以还原到有史以来的第一次提交,然后从那里重新格式化为当前状态。但这是一项艰巨的工作,其他所有人都必须在工作进行期间暂停工作(或为所有合并的母亲做好准备)。有没有更好的方法可以更改历史记录,从而获得最好的结果:

  • 所有提交中的样式相同
  • 最少的合并工作

需要澄清的是,这与启动项目时的最佳实践无关,而是在大型重构被视为Good Thing™却又想获得可追溯的历史时应该怎么做?如果这是确保您的版本始终保持相同工作的唯一方法,则永远不要重写历史记录,这是伟大的,但是对于开发人员而言,全新重写的好处是什么?特别是如果您有方法(测试,语法定义或编译后的相同二进制文件)可以确保重写后的版本与原始版本完全一样?


24
您为什么要重写历史记录?它违反了版本控制的目的。您要确保3个月前发货的应用程序与版本xxxxxx匹配,没有丝毫疑问。即使是琐碎的重新格式化也是不可接受的。
西蒙·贝格

5
我想评论我用“重新格式化。无功能更改”标记的提交
2012年

3
在一个不相关的主题上,听起来您建议通过重新格式化所有代码来重写Git历史记录。不要让人们知道,改写Git历史对于99.9%的情况是不好的。重新格式化不是.1%优势的情况。
Andrew T Finnell 2012年

4
在某些语言中(我正在使用Python,我正在研究您),重新格式化可以更改代码的逻辑功能。您必须能够解析存储在VCS中的所有语言,以安全地跟踪和忽略重新格式化。
Joris Timmermans 2012年

3
重新格式化是代码更改,应这样提交。
大卫·考登

Answers:


26

重新格式化为单独的提交。这将对历史的影响降到最低,并且您应该能够一眼看出哪些提交只是重新格式化,哪些实际上更改了代码。它可能会歪斜git blame并且类似,但是如果它指向仅重新格式化的提交,那么在此之前查找先前的更改是相当简单的。


我已经看到项目偏离了几周,因为其中一位开发人员认为这是个好主意。如果要执行此操作,请事先了解风险,并准确确定要进行格式化的程度。我认为mjfgates有正确的答案。
Johntron

1
听起来像这样的团队比代码格式化有更大的问题。但是,是的,除非您必须这样做,否则我不建议您这样做。如果您想重新格式化更改,我仍然会说最好将它们作为单独的提交而不是与功能更改混合在一起。
哈拉尔德

是的,有很多问题:PI只是要警告新开发人员,这听起来并不简单。批量重新格式化工具存在风险(特别是如果您使用正则表达式自己构建-至少使用AST),并且如果您在乎代码审查和错误跟踪,则它确实会干扰您的流程。就我个人而言,我编写代码时要与每个文件的样式保持一致,尽管我不介意在重新格式化一些函数时检查代码。许多开发人员都迷上了代码样式,而忽略了诸如体系结构,流程,工具等更大的问题
。– Johntron

在编程中,没有什么听起来简单:)
harald

13

不要重写VCS历史记录:它违反了VCS原则。

不要尝试自动修复格式:它是在处理症状,而不是真正的问题(=开发人员未遵循编码标准)。

在通用文档中定义编码标准和格式化最佳做法,并征得所有开发人员的同意。

您提到了Git,它很棒,因为它是分布式的。使用DVCS,可以通过网守工作流程轻松实施最佳实践。看门人拒绝不符合通用准则的合并建议(= Git中的拉取请求)。我的意思是用粗体字母拒绝,否则违反代码的编码人员将不会理会规则并继续重复同样的错误。

这项技术对我来说效果很好。编码人员希望合并他们的工作,因此在开始时犯了一些错误之后,他们开始遵循规则。

按照固定现有代码库的方式...我建议逐步进行此操作,也许逐个模块进行,或者对您的项目有意义。在每一步都要仔细测试。听起来可能很愚蠢,但是即使进行了微小的更改(例如仅格式化),也确实会发生错误,因此请为路上的一些小事故做好准备。


1
不推荐使用,因为作者清楚地指出,这是在项目的背景下开始的,而这些项目并非以“ ...从第一天起就提供清晰,完整,可验证和强制执行的样式指南”开头。他无法解决真正的问题,因为它已经发生了。我确实同意你的意见:)
Johntron '16

2
拒绝意味着人类与机器人之间会发生争斗。到过那里。迟早,机器人将需要用一种难以理解的方式来格式化非常复杂的代码。示例:Java字符串实际上是一个SQL语句,但是机器人不知道这一点;在关闭括号之前的空白可能会携带有关代码结构的信息,供人类使用,但不适用于机器人。函数参数以最无意义的方式拆分为多行...
18446744073709551615 '18

9

实际问题的答案是“您不知道”。我不知道当前的SCM工具可以跟踪以一种方式格式化的代码,通过主要的格式化更改以及在以新方式格式化代码后进行的进一步更改而引起的逻辑更改。而且,您知道这一点,丢失一段代码的历史记录是不好的。

因此,我将有点矛盾您的第一句话。代码格式并不重要的是很多。漂亮很好,但这不是我们想要的。与任何人一样,我也了解到,使用两个空格的缩进将其转储到某人的旧的地狱般怪异的K&R变体代码中很糟糕(1),但是...格式化实际上并不是理解正在发生的事情的障碍,除非有特殊情况病理的。在那种情况下,无论如何您都会遇到更改代码的问题,因此不要理会它。

因此,不宜对现有代码进行严格更改以重新格式化它。更改变量名称,破坏长函数,所有可以改变内容的好的重构内容,是的,但不能重新格式化。

1)-我曾经拥有Windows Clipboard Viewer一段时间。整个过程是一个150k C模块。我发现一个地方,不同的人在30行之间使用了五种不同的花括号样式。但是那部分工作正常。我进行了十年的代码输出,但是我没有戳它,因为历史很重要,并且该代码存在于至少三个都存在的源代码树(Windows 3.x,NT,future 95)中。在不同的建筑物中。


过去,使用hg,我发现按部分合并是应对棘手的大型重构合并的宝贵工具。通常,我要做的是在大型重构之前合并提交,然后合并大型重构本身,然后最后合并自重构以来的提交。这三个合并的对自己太多容易然后试图理清混乱,做完所有的合并在一气呵成的效果。
Mark Booth

我完全同意!另外,我已经看到许多开发人员在重新格式化和代码风格方面过分关注(包括我自己的较新版本),并且最终引入了缺陷。这里缺少逗号/分号,变量声明移至函数顶部,for循环更改为for-each-它们都可能引入细微的错误。安全地进行这些更改需要欺骗性的技能。
强创公司

4

但是,如何跟踪主要格式更改中的代码更改?

格式更改就是代码更改;就像对待代码进行任何其他更改一样对待它们。任何从事重大项目工作的人都可能会看到错误和其他问题,这些错误和其他问题是当某人决定“仅”重新格式化某些代码时产生的。

但这是一项艰巨的工作,其他所有人都必须在工作进行期间暂停工作(或为所有合并的母亲做好准备)。

为什么必须同时重新格式化所有内容?尤其是如果重新格式化不会改变代码的含义,则您应该能够单独重新格式化文件并在进行操作时将其检入。更好的是,让团队中的每个人都同意一种风格(否则无论如何都没有必要重新格式化),并让他们在其他工作中都照顾到重新格式化。一段时间之后,您将覆盖大部分代码,而不会破坏项目的其余部分。


1

我已经看到了两种可行的方法。

1.重新格式化提交挂钩

虽然最初提交代码后修改代码是很麻烦的,但是如果重新格式化过程(例如astyle)不会损害代码,那么它是安全的操作。随着时间的流逝,整个团队将意识到所有代码最终看起来都是相同的。显然,进行全面的单元/自动测试将确保没有任何损坏。

2.一次性重新格式化所有代码

根据我的经验,这更加危险,并且很难在大爆炸中跟踪问题,但是有可能。之后必须运行所有测试。对于编码样式,大多数差异都围绕空白的使用-缩进或换行。应该能够让一个体面的合并工具忽略所有空白差异,因此这将有助于合并。


1
当打开大多数代码库中的涟漪图时,是否会迅速选择一个选项,从而导致每个文件都发生变化呢?
签署

@Sign:正是我的意思-当提交挂钩发生变化时,您的历史记录可能会恶化为几乎无用的东西。不会更改功能的格式不应该作为提交,而应在整个代码历史中进行移植。
l0b0 2012年

1
如果有IDE的支持,那么还有3)保存时具有IDE自动格式设置。然后,只需在各处使用相同的设置-如果将默认设置与IDE一起使用,这是最简单的。

我已经完成了这两种方法。第一种方法非常麻烦,因为每次首次提交新文件时都会进行大量更改。第二种方法对团队来说更好,例如快速撕掉创可贴。
Druska
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.