87

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

我试图保持问题的通用性,以便更多人可以回答,但我想做的是编写一个 git pre-commit 钩子,它将拒绝任何与预期 .clang-format 不匹配的提交。在索引中的文件列表上运行 clang-format 很容易。但很难知道 clang-format 是否真的改变了任何东西。

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

4

9 回答 9

48

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

我现在拥有的是

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

这实际上返回了我想要的退出代码的倒数,因为 grep 如果匹配则返回 0,如果不匹配则返回 1。但这很容易处理。

所以我的 git pre-commit 钩子的相关位是

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-format 已找到它想要进行的替换的替换(而不是替换)。(将所有输出作为 XML 丢弃对用户没有意义。)
  6. 最后一个命令退出 1(grep 说我们什么也没找到)我们完成了,一切都很好。
  7. 如果不是,则显示一条消息并退出 1,这将取消提交。不幸的是,我们没有简单的方法来告诉用户哪个文件是问题所在,但他们可以自己运行 clang-format 并查看。
于 2014-04-04T15:13:45.067 回答
33

使用--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/

于 2020-09-19T21:36:25.767 回答
12

run-clang-format是一个简单的包装器,clang-format专门设计用作钩子或用作持续集成脚本:它输出差异并以合理的状态退出。

主页上给出的示例不言自明:

运行clang格式示例

于 2018-06-15T09:15:51.427 回答
4

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

于 2014-06-03T08:09:01.890 回答
2

我稍微调整了这篇文章中 phs 的评论以提出:

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 脚本中运行这一行,以确保格式正确。我有另一个脚本用于实际运行格式化程序。这让我想起了一个警告:您必须使用可以执行子进程的 shell(posix shell 不能)。

于 2018-05-10T01:12:26.643 回答
2

命令行

您可以git diff使用clang-format-diff

$ git diff -U0 --no-color --staged HEAD | clang-format-diff -p1

笔记:

  • --staged用于clang-format-diff仅在阶段性更改上运行
  • 如果要在特定目录上运行此命令,可以将其重写为:
$ git diff -U0 --no-color --staged HEAD -- $PWD/dir1 $PWD/dir2 $PWD/dir3  | clang-format-diff -p1

..和pre-commit

现在你的预提交可以是这个:

#!/bin/bash

dir_list="$PWD"  # Add the directories you want here
cmd="git diff -U0 --no-color --staged HEAD -- $dir_list | clang-format-diff -p1"

echo ""
echo "Running clang-format on this commit"
echo ""

# Execute the format command
diff=$(eval "$cmd")
if [[ $? -ne 0 ]]
then
    echo "Command failed to execute."
    exit 1
fi

# Print the outcome
if [[ -z "$diff" ]]
then
    echo "Everything is clean"
    exit 0
else
    echo "$diff"
    echo ""
    echo "Commit aborted due to code format inconsistencies."
    exit 1
fi

于 2021-03-11T19:31:31.110 回答
1

我使用git-clang-formatMike Rhodes 博客中的预提交脚本:

#!/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)。要绕过这一点,请使用-nor--no-verify选项。

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

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

于 2016-10-06T20:24:33.037 回答
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
于 2017-08-29T21:41:57.920 回答
0

您可以简单地使用 -n 选项

clang-format -n <file>
于 2021-06-01T19:52:08.420 回答