运行预提交挂钩后要播放什么?

时间:2011-12-14 14:44:02

标签: git hook pre-commit

对于修改暂存文件的预提交钩子,我需要弄清楚钩子运行后必须暂存的内容。

预提交钩子对应该提交的文件应用一些漂亮的打印。钩子执行以下任务:

  • 按空格替换制表符
  • 删除行尾的尾随空格
  • 删除双空行 *)
  • 如果遗漏,请在文件 *)
  • 的末尾添加一个空行

标有 *) 的操作是导致下述问题的操作。

完成这些操作后,钩子会使用git add $filename将修改后的文件添加到索引中。这样,整个文件就会被暂存,我不再只能提交修改过的文件的一小部分(即帅)。

当然,我可以git add --no-verify并绕过钩子,但使用git gui时该选项不可用。另外,我想要在文件的分段行上应用漂亮打印,所以绕过钩子不是我的目标。

有没有办法找出在应用漂亮打印后必须添加到索引的内容,这样我就可以在挂钩运行后暂存正确的内容而不是暂存整个档案?


编辑1:
虽然answer提供的David Brigada看起来很有前途,但它不起作用:git stash --keep-index保持分阶段的更改完整(这是好的部分),但(至少在msysgit上)它放置< strong>所有更改为存储(不仅是未存储的存储)。当将存储弹回到WC时,这会导致合并冲突,因为分段的行可能会被修改。


编辑2:
另外,David的更新答案没有成功,因为git拒绝将存储区合并到脏的WC中。


编辑3:
来自answerlarsks指向我使用.gitattributes。在第一眼看来,这似乎是正确的,但我发现签入的版本是通过过滤器运行并且WC与签入版本不同,这让我很困惑。至少,这是我的经验,并由Git Book中的以下注释支持:

  

如果您提交这些更改并再次检出该文件,则会看到关键字已正确替换

我必须删除该文件,然后再次检查以查看过滤器应用的更改?没门!还有其他提示吗?

5 个答案:

答案 0 :(得分:2)

您可以在预提交脚本中将未提交的更改存储在存储中,然后在完成后将其弹出。 --keep-index选项仅存储您尚未添加到索引的更改(尚未运行git add),--quiet选项会禁止您创建和销毁存储的通知。

你描述的方式并不是规范的“git方式来做”,我见过的大多数预提交脚本只检查不正确的样式,然后如果找到任何内容就会出错。这样你没有你的预提交脚本无意中重写了一些本来应该有标签的文件(例如测试数据)---你可以简单地重新运行提交--no-verify一旦你是确信错误应该在那里。

我这里有一个简单的预提交脚本。它几乎只是运行git对diff-index --check的基本测试,但是它使用git stash来处理应该进行的更改。

#!/bin/sh
if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

git stash save --keep-index --quiet
git diff-index --check --cached $against --
RETVAL=$?
git stash pop --quiet
exit $RETVAL

更新

如果您尝试将此策略与修改要提交的内容的脚本一起使用,则会出现合并冲突。我们应该从预提交脚本中取出stash pop,并在post-commit脚本中执行显式合并。

#!/bin/sh

git stash save --keep-index --quiet
# Add your script to prettify the code here ...
git add .

现在,提交后脚本看起来像这样

#!/bin/sh

git merge stash@{0} -s recursive -Xtheirs
git stash drop --quiet stash@{0}

recursive参数的theirs合并应尽可能保留脚本所做的更改。

答案 1 :(得分:2)

我不确定预提交挂钩是执行此类工作的正确位置。 Git有一个过滤机制,允许您通过.gitattributes将提交/结帐过滤器应用于文档; Pro Git Book包含使用此过滤机制自动将indent程序应用于C源文件的示例。

答案 2 :(得分:0)

我不是100%肯定我理解目标,但也许您可以尝试以下方法。让您的钩子生成补丁而不是将更改写入磁盘。 (您可以使用difflib模块在​​Python中执行此操作,或者将临时文件和shell写入diff。)然后使用git apply(使用--cached和其他标志(如果适用)将您的补丁应用于索引以及工作树(由挂钩手动或自动)。

答案 3 :(得分:0)

由于我的目标似乎无法实现,我帮助了另一个解决方案:

当我在git gui时,我只会暂存部分文件。幸运的是,git gui允许您向Tools菜单添加自定义命令。我设置了一个名为hooks.sh的脚本,它接收一个参数,enabledisable。此脚本已添加到Tools菜单,一次使用enable,一次使用disable作为参数。

这样,我可以很方便地在暂存文件的一部分时禁用钩子,然后提交然后再次启用钩子。

你去了:

#!/bin/sh

################################################################################
# hooks.sh                                                                     #
# enable or disable git hooks.                                                 #
################################################################################

THEHOOK=pre-commit                  # which hook to work with
ENABLEDFILE=./.git/hooks/$THEHOOK   # original name of the file
DISABLEDFILE=$ENABLEDFILE.disabled  # disabled name of the file
OLDFILE=''                          # for mv command: old name
NEWFILE=''                          # for mv command: new name

# parse parameters
case $1 in
  disable)
    OLDFILE=$ENABLEDFILE
    NEWFILE=$DISABLEDFILE
    ;;
  enable)
    OLDFILE=$DISABLEDFILE
    NEWFILE=$ENABLEDFILE
    ;;
  *)
    echo -e "operation:\n    $0 enable  to enable the hook\n    $0 disable to disable the hook"
    exit 1
    ;;
esac

if [ -e $OLDFILE ]
then
  mv $OLDFILE $NEWFILE
else
  echo "nothing to do"
fi

答案 4 :(得分:0)

你想要的是拥有一个预提交钩子来执行你描述的操作,但一个commit-msg钩子。

这就是我解决问题的方法:

  1. 添加以下预提交挂钩:

    #!/bin/sh
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 YOUR_SMUDGE_SCRIPT
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 git add -u -v --
    
  2. 添加以下commit-msg hook:

    #!/bin/sh
    awk '!/^[[:space:]]*(#|$)/{exit f++}END{exit !f}' "$1" && exit
    # NOTREACHED unless commit was aborted
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 YOUR_CLEAN_SCRIPT
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 git add -u -v --
    
  3. 注意将YOUR_CLEAN_SCRIPT和YOUR_SMUDGE_SCRIPT替换为您要运行的命令名,它将执行实际的清理/涂抹程序。涂抹例程应该扩展关键字(发生预提交),清理例程应该撤消更改(在提交中止时发生)。这些脚本将传递需要修改的文件(以前用于不同的编辑)。