git预提交钩子代码格式化与部分提交?

时间:2014-04-04 09:29:10

标签: git formatting githooks pre-commit-hook

有没有办法让预先提交钩子自动格式化代码(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,那就是 脚本格式化源代码后需要添加"另一个"块, 太。但我不想要那个。

有没有办法实现这个目标?

2 个答案:

答案 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命令至少有两种方法可以做到这一点。由于重新格式化已提交的版本,两者都可能导致冲突:

  1. git diff stash^1 stash | git apply --reject(最终git stash drop
  2. git stash branch tempbranch; git commit -m for-cherry-pick; git checkout prev-branch; git cherry-pick -n tempbranch; git branch -D tempbranch
  3. 方法1更简单但更混乱,因为合并冲突的变化会被放入"拒绝"文件。方法2使用合并机制,因此如果需要,更改将获得冲突标记。 (如果没有冲突,-n会阻止提交,以便您可以使用真实消息自行执行,而不是复制虚拟for-cherry-pick消息。)

    当然我还没有测试过这个。此外,还有一些方法可以在不使用git stash的情况下执行此操作,例如将git add - ed文件的索引版本签出到单独的目录中,在那里格式化,然后再添加格式化版本,这样这个过程都不​​会影响当前的工作目录。无论如何,如果你真的决定这样做,这可能是优越的。这是 方法的一个脚本(也没有经过测试 - 它需要添加一些健壮性,使用-zxargs -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将所有内容恢复到创建存储时的方式。

相关问题