clang-format可以告诉我是否需要更改格式吗?


73

有没有一种方法可以clang-format在报告文件是否符合指定格式的模式下运行?一种空运行模式,它报告是否需要更改,但不进行更改。理想情况下,如果文件需要更改,我希望clang格式仅返回非零退出代码。或者,更理想的情况是,非零退出代码和需要在标准输出上更改的文件列表。

我正在尝试使问题更通用,以便更多人可以回答,但是我正在尝试编写一个git pre-commit钩子,该钩子将拒绝任何与预期.clang-format不匹配的提交。在索引中的文件列表上运行clang格式很容易。但是很难知道clang格式是否真的改变了什么。

我有一个基于的潜在解决方案-output-replacements-xml(我将作为答案发布),但这是一个hack,我觉得这应该更简单。欢迎评论/建议,编辑,不同的答案/方法。

Answers:


9

run-clang-format是一个简单的包装,clang-format专门设计为用作挂钩或连续集成脚本:它输出diff并以明智的状态退出。

主页上给出的示例说明了一切:

run-clang-format示例


46

我之所以认为应该比它容易的原因之一是因为-output-replacements-xml本质上给了我想要的答案,只是没有以一种易于使用的方式给我。但是,由于不需要替换的输出是非常可预测的,因此解析输出不是太难。

我现在所拥有的是

clang-format -style=file -output-replacements-xml | grep -c "<replacement " >/dev/null

实际上,这将返回我想要的退出代码的反函数,因为如果某项匹配,则grep返回0,否则则返回1。但这很容易解决。

所以我的git pre-commit hook的相关部分是

git diff --cached --name-only --diff-filter=ACMRT |
  grep "\.[cmh]$" |
  xargs -n1 clang-format -style=file -output-replacements-xml |
  grep "<replacement " >/dev/null
if [ $? -ne 1 ]; then 
    echo "Commit did not match clang-format"
    exit 1
fi
  1. 获取索引中文件的完整文件名(不包括正在删除的文件以及我可能不想处理该文件的其他异常情况)
  2. 只保留我要检查格式的东西的文件名(在我的情况下,仅c,m和h文件)
  3. 通过xargs运行结果以实质上“为每个”下一个命令
  4. 在所有文件上使用-output-replacements-xml选项运行clang-format
  5. 搜索表示clang格式已找到要替换的替换的替换(与替换相反)。(丢弃所有输出,因为XML对用户没有意义。)
  6. 最后一个命令退出1(grep说我们什么都没找到),我们已经完成,一切都很好。
  7. 如果不是,则显示一条消息并退出1,这将取消提交。不幸的是,我们没有一种简单的方法来告诉用户问题出在哪个文件上,但是他们可以自己运行clang格式并查看。

1
上面的代码段应该是钩子所需的所有内容,而不是 #!/bin/bash顶部。(我将全部内容都放在了要点上,但其中还包含其他特定于项目的内容。)我的内容唯一的区别是注释,它们解释了与上述相同的内容以及对clang格式路径的完全限定。
David Ogren '02

24
我的两分钱:diff -u <(cat src/*) <(clang-format src/*)代替了XML
phs 2016年

4
@phs是这里的最佳答案!
pattivacek '17

1
grep -c“ <替换>” | grep 0还应该反转返回类型,因为如果第一个grep找到了某项,则grep 0将找不到0
Bomaz

2
似乎clang-format -output-replacements-xml即使clang-format -i已经在文件上运行并且仍然没有其他替换可以输出一些替换(结果只是身份替换)。我不确定在什么情况下会发生这种情况,但是如果有人发现这个问题,为什么答案对他们不起作用,我建议使用
@phs

4

我不完全确定您的用例是什么,但是请查看git-clang-format(https://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/git-clang-format) 。它基本上为git提供了clang格式的集成,也许这就是您所需要的。


1
感谢您的回答,我们将对其进行调查。但是,它并不能真正回答问题或解决我的用例。似乎只是在更改的文件上运行clang格式。本质上,我正在寻找一种安全措施,以确保满足当前样式(按clang格式定义),但是不会任意运行clang格式。(我至少希望有机会回顾一下clang-format可能会进行的格式更改:它并不总是以我想要的方式打破界限。)
David Ogren 2014年

16
那么,怎么样:clang-format <file> | diff <file> -?clang格式(不带-i选项)不会对文件进行任何更改,只需将其打印到stdout。
djasper 2014年

4

使用--dry-run-Werror命令行选项。如果任何输入文件的格式不正确,它们将导致ClangFormat将任何违反格式的内容输出到stdout并返回非零退出状态。

$ clang-format --dry-run --Werror foo.cpp
foo.cpp:129:23: error: code should be clang-formatted [-Wclang-format-violations]
        if (rc <= 0) {
$ echo $?
1

最初来自我的网站:https//rigtorp.se/notes/clang-format/


1
对于将来的读者,该--dry-run标志以clang-format-10添加。如果您使用的是旧版本,则此标志将不起作用。如果您好奇,请查看commit 6a1f7d6c9ff8228328d0e65b8678a9c6dff49837
bitoffdev

2

在这篇文章中,我对ph评论做了些微调整,以得出:

find embedded/ -regex '.*\.\(ino\|cpp\|hpp\|cc\|cxx\|h\)' -exec cat {} \; | diff -u <(find embedded/ -regex '.*\.\(ino\|cpp\|hpp\|cc\|cxx\|h\)' -exec clang-format-3.9 -style=file {} \;) -

那是..

  1. cat到的所有cpp-ish文件和管道diffdiff将接受,stdin因为我-在末尾指定了)
  2. 使用进程替换(<( .. )语法)clang-format在这些相同文件上运行。不要在这里使用就地格式。这是发送到的另一半diff
  3. 如果diff没有输出而退出,那就成功了!您也可以通过以下方式检查退出代码$?-应该为零。

我有我的CI服务(travis)在bash脚本中运行此行,以确保一切格式正确。我还有另一个脚本可以实际就地运行格式化程序。这让我想起了一个警告:您必须使用可以处理sub的shellposix shell不能)。


1

我使用git-clang-format了Mike Rhodes博客中的和pre-commit脚本:

#!/bin/python

import subprocess
output = subprocess.check_output(["git", "clang-format", "--diff"])

if output not in ['no modified files to format\n', 'clang-format did not modify any files\n']:
    print "Run git clang-format, then commit.\n"
    exit(1)
else:
    exit(0)

该脚本有一个小错误,因为它在没有提交时不起作用(尝试检查尚不存在的HEAD)。要绕过它,请使用-n--no-verify选项。

-n当您绕过检查时,使用跳过预提交脚本也很有帮助,因为大型代码库可能需要很长时间。

原始帖子在这里:http : //www.dx13.co.uk/articles/2015/4/3/Setting-up-git-clang-format.html


1

在受到David Ogren的帖子的启发后,我制作了一个pre-commit能够进行阶段性更改的钩子。这将确保该pre-commit钩子可以在将实际构成提交内容的代码上起作用,并且不会被clang-format未上演的运行所欺骗。

#!/bin/bash

files=()
for file in `git diff --cached --name-only --diff-filter=ACMRT | grep -E "\.(cpp|hpp)$"`; do
  if ! cmp -s <(git show :${file}) <(git show :${file}|clang-format); then
    files+=("${file}")
  fi
done

if [ -n "${files}" ]; then
echo Format error within the following files:
printf "%s\n" "${files[@]}"
exit 1
fi
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.