【问题标题】:Can clang-format tell me if formatting changes are necessary?clang-format 可以告诉我是否需要更改格式?
【发布时间】:2014-05-16 23:48:34
【问题描述】:

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

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

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

【问题讨论】:

    标签: git clang-format


    【解决方案1】:

    我觉得这应该比它更容易的原因之一是因为 -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 并查看。

    【讨论】:

    • 上面的代码 sn-p 应该是钩子所需的一切,除了顶部的#!/bin/bash。 (我将整个钩子放在要点上,但其中包含其他特定于项目的内容。)我的钩子中唯一的区别是 cmets 可以解释与上述相同的内容以及 clang 格式路径的完整限定。
    • 我的两分钱:diff -u &lt;(cat src/*) &lt;(clang-format src/*) 而不是 XML
    • @phs 这是最好的答案!
    • grep -c "" | grep 0 也应该反转返回类型,因为如果第一个 grep 找到了某些东西,那么 grep 0 将找不到 0
    • 似乎clang-format -output-replacements-xml 输出了一些替换,即使clang-format -i 刚刚在文件上运行并且没有更多替换要进行(结果只是身份替换)。我不确定在什么情况下会发生这种情况,但如果有人对此感到疑惑,为什么答案对他们不起作用,我推荐@phs 的建议
    【解决方案2】:

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

    【讨论】:

    • 对于未来的读者,在 clang-format-10 中添加了 --dry-run 标志。如果您有旧版本,则此标志将不起作用。如果您好奇,请查看提交 6a1f7d6c9ff8228328d0e65b8678a9c6dff49837。
    • 更新(6/2):将其更改为官方答案,因为 clang-format(6 年后)现在已将其作为官方标志,并且此问题仍然有很多流量。
    【解决方案3】:

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

    首页给出的例子不言自明:

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 感谢您的回答,我会调查的。但是,它并没有真正回答问题或解决我的用例。它似乎只是在更改的文件上运行 clang-format。本质上,我正在寻找一种保障措施,以确保满足当前样式(如 clang-format 中定义的),但这不会任意运行 clang-format。 (我希望至少有机会回顾一下 clang-format 会做出的任何格式更改:它并不总是像我想要的那样换行。)
      • 那么,clang-format &lt;file&gt; | diff &lt;file&gt; - 怎么样? clang-format(没有-i 选项)不会对您的文件进行任何更改,只需将其打印到标准输出。
      【解决方案5】:

      我稍微调整了comment from phs in this post想出:

      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 文件并将其通过管道传输到 diff(diff 将接受 stdin,因为我在末尾指定了 -)
      2. 使用进程替换(&lt;( .. ) 语法)在这些相同的文件上运行clang-format。不要在此处使用就地格式化。这是发送到diff 的另一半
      3. 如果diff 退出且没有输出,则成功!您还可以通过$? 检查退出代码——它应该为零。

      我的 CI 服务 (travis) 在 bash 脚本中运行这一行,以确保格式正确。我有另一个脚本用于实际运行格式化程序。这让我想起了一个警告:您必须使用可以执行子进程 (the posix shell does not) 的 shell。

      【讨论】:

        【解决方案6】:

        命令行

        您可以将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

        现在你的 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
        
        

        【讨论】:

          【解决方案7】:

          我使用 git-clang-format 和 Mike 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)。要绕过此问题,请使用 -n 或 --no-verify 选项。

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

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

          【讨论】:

            【解决方案8】:

            在我受到David Ogren's post 的启发后,我制作了一个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
            

            【讨论】:

              【解决方案9】:

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

              clang-format -n <file>
              

              【讨论】:

              • 埃里克的回答重复
              • 实际上这不是真的(我应该知道,因为我首先将这些选项添加到 clang-format),--dry-run == -n(-n 是简短版本), -Werror 将其转换为错误,因此您可以使用返回码(如果这是您需要的) -n 将其保留为警告,因此错误文本将有所不同test1.cpp:7:29: warning: code should be clang-formatted [-Wclang-format-violations],当然 -n 简单得多也可以打字!
              猜你喜欢
              • 2018-07-16
              • 2015-09-07
              • 2015-05-25
              • 2014-07-19
              • 1970-01-01
              • 1970-01-01
              • 2022-09-27
              • 2010-10-25
              • 2019-09-24
              相关资源
              最近更新 更多