英文:
Trying to make a pre-commit hook that checks if files in index are formatted correctly
问题
我一直在尝试创建一个预提交挂钩,用于检查索引中的文件是否格式正确。我已经尝试了很多方法,但无法让grep正常工作。这是我当前的代码:
for FILE in $(git diff --cached --name-only)
do
if [[ "$FILE" =~ \.(c|h|cpp|cc)$ ]]; then
exec C:/dev/uct/clang-format-15.0.3.exe --dry-run -Werror $FILE | grep -c "violations"
fi
done
我期望它会打印具有格式问题的文件数量(实际上我想搜索字符串"-Wclang-format-violations",但为了测试,我简化了它)。
如果我将clang-format的输出重定向到文件,违规内容将正确打印到该文件中。
exec C:/dev/uct/clang-format-15.0.3.exe --dry-run -Werror $FILE &> temp.txt
然后,如果我从命令行上运行grep命令,它也可以正常工作。
grep -c violations temp.txt
我还尝试从脚本中对文件使用grep,但由于某种原因,它也没有产生任何输出(尽管我更希望不必创建文件)。
exec C:/dev/uct/clang-format-15.0.3.exe --dry-run -Werror $FILE &> temp.txt
grep -c violations temp.txt
我做错了什么?我原以为我最多只需要半个小时就能让它正常工作。
英文:
I've been trying to make a pre-commit hook that checks if files in the index are formatted correctly. I've tried so many things already but I just can't get the grep to work correctly. This is my code right now:
for FILE in $(git diff --cached --name-only)
do
if [[ "$FILE" =~ \.(c|h|cpp|cc)$ ]]; then
exec C:/dev/uct/clang-format-15.0.3.exe --dry-run -Werror $FILE | grep -c "violations"
fi
done
I expect it to print the number of files that have formatting issues (I actually want to search for the string -Wclang-format-violations but I simplified it for testing).
If I &> the clang-format output to a file the violations correctly printed to that file.
exec C:/dev/uct/clang-format-15.0.3.exe --dry-run -Werror $FILE &> temp.txt
If I then run grep from the command line on that file it works.
grep -c violations temp.txt
I also tried grep on the file from the script but for some reason that didn't produce any output either (though I would prefer to not have to create a file).
exec C:/dev/uct/clang-format-15.0.3.exe --dry-run -Werror $FILE &> temp.txt
grep -c violations temp.txt
What am I doing wrong? I thought I would get this to work in half an hour at most.
答案1
得分: 1
The problem with grep
is that you're not redirecting stderr
, only stdout
. (In your redirection to a file, you are redirecting stderr as well, with >&
). To capture stderr in the pipe, use |&
.
However, you have a mismatch between what's staged to be committed and what you're looking at. In git, a file isn't staged to be committed, but its contents are. That means that you can stage - for example - part of a file's contents to be committed. The file's contents in the working directory thus would not match what is staged.
Here's a concrete example:
echo "hello" > hello_world.txt
git add hello_world.txt
echo "world" >> hello_world.txt
At this point, the contents staged in the index (from the git add
on line 2) are hello
. But the file contains hello world
on disk.
git status
will show the file both as staged and modified:
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello_world.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: hello_world.txt
If you were to run git commit
now, only the hello
would be committed, not the file as it exists on disk.
That means that you want to look at the staged contents of the file -- which is what will actually be committed -- not just the file on disk. Otherwise, you may get false positives or negatives.
To handle this case, you can pass what's staged to clang-format
using the git show
command. (git show :filename
will output the staged contents of filename
.)
Try this:
for FILE in $(git diff --cached --name-only)
do
if [[ "$FILE" =~ \.(c|h|cpp|cc)$ ]]; then
git cat-file --filters ":${FILE}" | C:/dev/uct/clang-format-15.0.3.exe --dry-run -Werror --assume-filename="${FILE}" |& grep -c grep -c "-Wclang-format-violations"
fi
done
英文:
The problem with grep
is that you're not redirecting stderr
, only stdout
. (In your redirection to a file, you are redirecting stderr as well, with >&
.) To capture stderr in the pipe, use |&
.
However you have a mismatch between what's staged to be committed and what you're looking at. In git, a file isn't staged to be committed, but its contents are. That means that you can stage - for example - part of a files contents to be committed. The file's contents in the working directory thus would not match what is staged.
Here's a concrete example:
echo "hello" > hello_world.txt
git add hello_world.txt
echo "world" >> hello_world.txt
At this point, the contents staged in the index (from the git add
on line 2) are hello
. But the file contains hello world
on disk.
git status
will show the file both as staged and modified:
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello_world.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: hello_world.txt
If you were to run git commit
now, only the hello
would be committed, not the file as it exists on disk.
That means that you want to look at the staged contents of the file -- which is what will actually be committed -- not just the file on disk. Otherwise you may get false positives or negatives.
To handle this case, you can pass what's staged to clang-format
using the git show
command. (git show :filename
will output the staged contents of filename
.)
Try this:
for FILE in $(git diff --cached --name-only)
do
if [[ "$FILE" =~ \.(c|h|cpp|cc)$ ]]; then
git cat-file --filters ":${FILE}" | C:/dev/uct/clang-format-15.0.3.exe --dry-run -Werror --assume-filename="${FILE}" |& grep -c grep -c "\-Wclang\-format\-violations"
fi
done
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论