【问题标题】:git pre-commit hook to format and re-add files at the same timegit pre-commit hook 同时格式化和重新添加文件
【发布时间】:2015-09-12 11:36:36
【问题描述】:

我们目前正在使用 git 挂钩(如下)在允许用户提交之前在我们的源代码上运行样式。这有一个警告,用户必须提交,格式化他们的代码,然后再次提交,这有点麻烦。理想情况下,我们希望钩子格式化代码,然后将格式化的代码包含在原始提交中。我尝试重新添加更改的文件,但它会导致 ref 错误(显然)。我还尝试在 pre-commit 钩子中获取历史记录并尝试退出钩子并重新运行 git commit 命令,但没有成功。

# Run astyle on changed .cs files, ignoring $ignored
res=$(exec git diff --cached --name-only | \
    grep -Ev $ignored | \
    xargs astyle --options=conf/astylerc | \
    tail -n 1)
num_formatted=$(echo $res | cut -b 1) # We are only interested in the number preceeding 'formatted'.
[[ $num_formatted -ne 0 ]] && echo "WARNING: Code has been automatically formatted. Please re-add and re-commit" && exit 1 || echo "No code to format! Continuing commit"

有人有什么想法吗?

【问题讨论】:

  • 为什么不在提交前运行预提交挂钩并格式化代码?
  • 这是一个预提交钩子。只是我的开发人员需要在文件格式化后再次运行 git add 和 git commit 。所以它看起来像: git add file.cs git commit -m "msg" 警告:代码格式化 git add file.cs git commit -m "msg again" 这很烦人。我更喜欢: git add file.cs git commit -m "msg" Code Formatted, Re-Added, and Commit!谢谢!

标签: git formatting githooks


【解决方案1】:

已编辑 this 回答。

您可以在挂钩中重新格式化和添加文件。问题是您可能对暂存文件进行了未暂存的修改。要以干净的方式执行此操作,您可以从索引中获取文件作为 tmp,格式化 tmp 并使用格式化的 tmp 替换索引中的条目。这是解决问题的方法:

# Regexp for grep to only choose some file extensions for formatting
exts="\.\(ext\|ext2\)$"

# The formatter to use
formatter=`which your_formatter`

# Check availability of the formatter
if [ -z "$formatter" ]
then
  1>&2 echo "$formatter not found. Pre-commit formatting will not be done."
  exit 0
fi

# Format staged files
for file in `git diff --cached --name-only --diff-filter=ACMR | grep $exts`
do
  echo "Formatting $file"
  # Get the file from index
  git show ":$file" > "$file.tmp"
  # Format it
  "$formatter" -i "$file.tmp"
  # Create a blob object from the formatted file
  hash=`git hash-object -w "$file.tmp"`
  # Add it back to index
  git update-index --add --cacheinfo 100644 "$hash" "$file"
  # Remove the tmp file
  rm "$file.tmp"
done

# If no files left in index after formatting - fail
ret=0
if [ ! "`git diff --cached --name-only`" ]; then
  1>&2 echo "No files left after formatting"
  exit 1
fi

【讨论】:

  • 此脚本使文件系统中的文件未格式化,这使得差异在提交后仍然呈现未格式化的代码。我在格式化 .tmp 的行之后添加了:"$formatter" -i "$file"。不过,几乎可以肯定有比这更好的方法来解决这个问题。
【解决方案2】:

在你的预提交钩子中,你需要添加你的文件,所以如果你的钩子是这样的:

#!/bin/bash
echo 1 > file
exit 0

那么你需要修改它以添加:

#!/bin/bash
echo 1 > file
git add file
exit 0

要获取所有修改文件的列表,您可以使用 git-ls-files:

git ls-files -m

但是,如果您可以从代码中获取修改了哪些文件的列表,或者只是再次添加所有文件,那会更好。 git diff-tree -r --name-only --no-commit-id <tree-ish> 应该可以为您获取所有文件的列表。

基本上,在修改后再次添加文件是可行的,因为直到您的预提交挂钩运行之后才会发生提交,因此此时工作树中暂存的任何内容都会被提交。

【讨论】:

  • 哇,你说的太对了。我不知道为什么我试图从钩子中提交......其中一次你被过度复杂化的问题蒙蔽了双眼。无论如何,现在我在所有需要它的文件上运行格式化程序,将它存储在 $changed 变量中,然后 git add $changed。很有魅力!谢谢!
【解决方案3】:

您可以使用here 描述的技术仅存储未暂存的更改。然后仅在暂存的更改上运行格式化程序并弹出存储。下面pre-commit钩子使用clang-format-diff

#!/bin/sh

# stash unstaged changes
git commit --no-verify -m 'Save index'
old_stash=$(git rev-parse -q --verify refs/stash)
git stash push -m 'Unstaged changes'
new_stash=$(git rev-parse -q --verify refs/stash)
git reset --soft HEAD^

# format staged changes
git diff -U0 --no-color --staged HEAD -- '*.java' | $PWD/clang-format-diff.py -i -p1

git add -u
if [ "$old_stash" != "$new_stash" ]; then # if unstaged changes were stashed reapply to working tree
    git stash pop
fi
exit 0

【讨论】:

    猜你喜欢
    • 2023-04-11
    • 2012-01-18
    • 2011-06-17
    • 1970-01-01
    • 2013-02-19
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 2015-09-02
    相关资源
    最近更新 更多