有没有办法让预先提交钩子自动格式化代码(for
示例astyle
)但不是否会破坏部分提交?
工作流:
# edit a file.txt
git add -p file.txt
# add one chunk, but not another
git commit -m 'a message'
[PRE_COMMIT_HOOK] Formatting source code
git status
# the "another" chunk is still not added
我的问题是,如果你在预提交钩子里面做git add
,那就是
脚本格式化源代码后需要添加"另一个"块,
太。但我不想要那个。
有没有办法实现这个目标?
答案 0 :(得分:3)
我通过使用低级“管道”命令来完成这项工作,我的第一次尝试将是
git ls-files --stage \*.c | while read mode object stage path; do
case $mode in
10*)
formatted=`git show $object | indent | git hash-object -w --stdin`
git update-index --cacheinfo $mode $formatted "$path"
;;
esac
done
为避免冗余处理,请从@torek建议的git diff-index --name-only --diff-filter=AM
输出开始。
答案 1 :(得分:1)
有某种方法可以做到这一点。我不会,但如果你真的想,请继续沿着这些方向前进。
首先,您需要将现有的两个项目分开:
此外,您希望第一组可用于重新格式化。
这可以使用git stash
完成,正如我在How do I properly git stash/pop in pre-commit hooks to get a clean working tree for tests?的答案中所示(尽管如此,请参阅有关git stash中的错误的警告)。
基本上,您希望在运行测试的脚本中达到这一点:
# Run tests
status=...
一旦处于此状态,您可以通过格式化程序运行工作树项,并在预提交挂钩中git add
结果(如您已发现的那样)。这样可以避免格式化另一个"另一个"块,因为它不在工作目录版本中。然后,您可以继续提交(即,此处的其余部分不适用)。
问题是现在从存储中恢复工作树版本。因为您修改了索引,所以即使在提交完成后也无法返回:
# Restore changes
git reset --hard -q && git stash apply --index -q && git stash drop -q
相反,你想要的是在stashed索引(stash^1
)和stashed work-tree(stash
)之间找到差异,并将其应用于新的HEAD
提交。不使用git plumbing命令至少有两种方法可以做到这一点。由于重新格式化已提交的版本,两者都可能导致冲突:
git diff stash^1 stash | git apply --reject
(最终git stash drop
)git stash branch tempbranch; git commit -m for-cherry-pick; git checkout prev-branch; git cherry-pick -n tempbranch; git branch -D tempbranch
方法1更简单但更混乱,因为合并冲突的变化会被放入"拒绝"文件。方法2使用合并机制,因此如果需要,更改将获得冲突标记。 (如果没有冲突,-n
会阻止提交,以便您可以使用真实消息自行执行,而不是复制虚拟for-cherry-pick
消息。)
当然我还没有测试过这个。此外,还有一些方法可以在不使用git stash
的情况下执行此操作,例如将git add
- ed文件的索引版本签出到单独的目录中,在那里格式化,然后再添加格式化版本,这样这个过程都不会影响当前的工作目录。无论如何,如果你真的决定这样做,这可能是优越的。这是 方法的一个脚本(也没有经过测试 - 它需要添加一些健壮性,使用-z
和xargs -0
来处理包含的文件名空格,在diff-index输出部分的结帐中):
# make a directory for formatting files
WORKDIR=$(mktemp -d -t reformat) || exit 1
# clean it up when we leave
trap "rm -rf $WORKDIR 0 1 2 3 15"
# for files Added or Modified in the index, copy them to $WORKDIR
git --work-tree=$WORKDIR checkout -- \
$(git diff-index --cached --name-only --no-renames --diff-filter=AM HEAD)
# reformat files in the work-dir
(cd $WORKDIR; ...)
# for each file in the work-dir, re-"add" that version to this tree
# (this assumes the reformatter did not leave extraneous files!)
git --work-tree=$WORKDIR add --ignore-removal .
以下是我推荐的内容:不是在预提交挂钩中的那一点格式化代码,只需检查是否格式化。如果是,请允许提交。如果没有,拒绝它。这更符合预提交钩子的精神,它允许使用that other answer中的脚本。基本上,在它说:
status=...
你只是运行一些东西来检查格式化程序是否会更改任何东西(也许是允许格式化程序做它的事情,并看看结果是否与以下内容不同)承诺指数)。这会让你获得自己的身份。然后,您将完成脚本中的其他内容,git reset --hard -q && git stash apply --index -q && git stash drop -q
将所有内容恢复到创建存储时的方式。