暂时在git pre-commit钩子中修改工作目录和暂存区域

时间:2014-08-27 20:05:32

标签: git bash githooks

我正在使用类似于this one的方法来使用预提交挂钩来跟踪对数据库模式的更改(以及一些元数据表)。

我喜欢尝试保持我的提交干净,所以我希望在提交/提交自动更改时在提交消息中大声警告。以下是我的pre-commitpre-commit-msg挂钩:

的.git /钩/预提交

#!/bin/sh

# Save user changes to db/ (if any)
git diff --quiet db/
user_dirty=$?
[[ $user_dirty > 0 ]] && git stash save --keep-index

# Regenerate db/ automatically
db/save_schema_and_meta_tables.sh

# Were any automatic changes made? If so, commit them but warn about it
git diff --quiet db/
auto_dirty=$?
if [[ $auto_dirty > 0 ]]; then
    git add db/
    echo "WARNING: automatic changes to db/ added to commit" | tee .git/COMMIT_WARNING
fi

[[ $user_dirty > 0 ]] && git stash pop
exit 0

的.git /钩/准备提交-MSG

#!/bin/sh
msgf=$1
wf=.git/COMMIT_WARNING
if [ -e $wf ]; then
    msg=$(<$msgf)
    ( cat $wf; echo "$msg" ) > $msgf
    rm -f $wf
fi

以下是它的表现:

  1. 如果我对db/进行了更改但尚未暂存,则可以将它们保存在工作树中而不会干扰提交,这要感谢stash save --keep-indexstash pop。好!
  2. 但是,如果我对db/进行暂存更改并且它们被自动提交覆盖,那么用户意图的更改将在提交后消失。糟糕!
  3. 以下是我喜欢发生的事情:如果db/存在用户阶段的更改,并且它们与自动更改不完全匹配,则整个事情应该中止。我在弄清楚如何实现这个问题时遇到了很多麻烦:如何保存用户所做的分阶段更改,然后查看自动更改是否不匹配?

1 个答案:

答案 0 :(得分:1)

它不漂亮,而且速度很慢,但是根据@ torek的一些建议,我提出了以下建议。

  1. 检查用户是否已暂时更改db/目录(如果是user_staged=1
  2. 存储用户更改,同时保留暂存区域
  3. 自动生成db/目录
  4. 的内容
  5. db/目录的暂存版本与自动生成的版本(auto_changes=1进行比较,如果它们不同)
  6. 恢复用户的工作目录(模the bug torek identified),同时保留自动生成的db/
  7. 版本的副本
  8. 决定:
    • 如果用户上线并自动生成db/匹配,一切正常
    • 如果用户暂停且自动生成的db/不匹配,中止
    • 如果用户没有对db/进行任何更改,但有自动生成的更改,请暂停并继续,但警告在提交消息中
  9. pre-commit挂钩代码:

    # Has user staged changes to db/?
    git diff --quiet --staged db/
    user_staged=$?
    
    # Stash any user changes in the working tree
    old_stash=$(git rev-parse -q --verify refs/stash)
    git stash save -q --keep-index
    new_stash=$(git rev-parse -q --verify refs/stash)
    [[ "$old_stash" != "$new_stash" ]] && stashed=1 || stashed=0
    
    # Automatically regenerate db/
    db/save_schema_and_meta_tables.sh
    cp -a db db_AUTO
    
    # Compare automatically-generated changes to what the user had already staged
    git diff --quiet db/
    auto_changes=$?
    
    # Restore user's state
    [[ $stashed ]] && git reset --hard -q && git stash apply --index -q && git stash drop -q
    
    # abort: if user had staged changes to db/, and automatic changes would overwrite them
    # add but warn: automatic changes added, but no user changes to db/
    # silent: no user-staged changes, no automatic changes
    if (( $auto_changes > 0 )); then
        if (( $user_staged > 0 )); then
            echo "ERROR: automatic changes to db/ conflict with staged changes"
            rm -rf db_AUTO
            exit 1
        else
            rm -rf db/
            mv db_AUTO db
            git add db/
            echo "WARNING: automatic changes to db/ added to commit" | tee .git/COMMIT_WARNING
            exit 0
        fi
    else
        rm -rf db_AUTO
    fi