7

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

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

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

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

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

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

有没有办法在应用漂亮打印后找出必须添加到索引中的内容,以便我可以在运行钩子后暂存正确的内容,而不是暂存整个文件?


编辑 1:
虽然David Brigada提供的答案看起来很有希望,但它不起作用:保持分阶段的更改完好无损(这是好的部分),但是(至少在 msysgit 上)它将所有更改放入存储中(不仅是未分阶段那些)。这会在将存储弹出回 WC 时导致合并冲突,因为暂存行可能会被修改。git stash --keep-index


编辑 2:David
的更新答案也没有成功,因为 git 拒绝将 stash 合并到一个脏 WC 中。


编辑 3:来自larsks
回答指出我使用. 乍一看,这似乎是正确的,但我发现签入的版本是通过过滤器运行的,而 WC 与签入的版本不同,这很令人困惑。至少,这是我的经验,并得到Git Book中以下评论的支持:.gitattributes

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

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

4

5 回答 5

2

您可以将尚未暂存以供提交的更改放在预提交脚本中的存储区中,然后在完成后将它们弹出。该--keep-index选项仅存储您尚未添加到索引中的更改(尚未运行 git add on),并且这些--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

更新:

如果您尝试将此策略与修改您要提交的内容的脚本一起使用,您将遇到合并冲突。我们应该从 pre-commit 脚本中取出 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应尽可能保留脚本所做的更改。

于 2011-12-15T01:48:35.527 回答
2

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

于 2011-12-15T02:26:06.383 回答
0

我不是 100% 确定我理解目标,但也许您可以尝试以下方法。让您的钩子生成补丁而不是将更改写入磁盘。(您可以这样做,例如,在 Python 中使用该difflib模块,或者编写一个临时文件并将外壳输出到diff.)然后使用git apply(使用--cached和其他适当的标志)将您的补丁应用于索引以及工作树(手动或自动通过钩子)。

于 2011-12-23T20:38:35.293 回答
0

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

我只在git gui. 幸运的是,git gui您可以将自定义命令添加到Tools菜单中。我设置了一个名为的脚本hooks.sh,它接收一个参数,要么enable要么disable。该脚本被添加到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
于 2012-07-06T08:33:09.660 回答
0

你想要的是有一个 pre-commit 钩子来执行你描述的操作,然后还有一个 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 挂钩:

    #!/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 --
    

注意将 YOUR_CLEAN_SCRIPT 和 YOUR_SMUDGE_SCRIPT 替换为您要运行的命令名,这将执行实际的清洁/涂抹例程。smudge 例程应该扩展关键字(在提交前发生),而 clean 例程应该撤消更改(在提交中止时发生)。这些脚本将传递需要修改的文件(之前为不同的编辑暂存)。

于 2015-10-22T01:13:53.407 回答