【问题标题】:modifying working directory and staging area temporarily in git pre-commit hook在 git pre-commit 钩子中临时修改工作目录和暂存区
【发布时间】:2014-10-21 13:29:41
【问题描述】:

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

我喜欢尝试保持我的提交干净,所以当有自动更改被暂存/提交时,我希望在提交消息中得到大声警告。这是我的 pre-commitpre-commit-msg 钩子:

.git/hooks/pre-commit

#!/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/hooks/prepare-commit-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/ 进行了暂存 更改并且它们被自动提交覆盖,那么用户预期的更改会在提交后消失。糟糕!

这是我希望发生的情况:如果用户对db/ 进行了更改,并且它们与自动更改不完全匹配,那么整个事情应该中止。我在弄清楚如何实现这一点时遇到了很多麻烦:如何保存用户所做的分阶段更改,然后查看自动更改是否不匹配?

【问题讨论】:

  • 我在这里没有考虑过你的整个问题,但你可能想看看stackoverflow.com/q/20479794/1256452,我在那里写了如何在预提交钩子中使用git stash save --keep-index,以及如何制作它主要是可靠的。在您的情况下,我认为您在这里要做的是进行几次提交(包括隐藏的提交)并进行比较。
  • 谢谢,@torek。您线程中的策略很好。尝试使用git stash 技巧来做到这一点让我有点发疯。你指出的错误也令人沮丧......我肯定也被它咬过。

标签: git bash githooks


【解决方案1】:

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

  1. 检查用户是否已暂存更改db/ 目录(如果是user_staged=1
  2. 隐藏用户更改,同时保留暂存区域
  3. 自动生成db/目录的内容
  4. 比较 db/ 目录的暂存版本与自动生成的版本(auto_changes=1,如果它们不同)
  5. 恢复用户的工作目录(模the bug torek identified),同时保留db/的自动生成版本的副本
  6. 决定:
    • 如果用户分段和自动生成的db/ 匹配,则一切正常
    • 如果用户暂存和自动生成的db/ 不匹配,中止
    • 如果用户没有暂存对 db/ 的任何更改,但有自动生成的更改,暂存它们并继续,但在提交消息中警告它们

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

【讨论】:

  • 顺便说一句,我最喜欢的 sh 脚本技巧之一是将变量设置为 truefalse(而不是 1 或 0),然后使用 $var &amp;&amp; iftrue_action 等 @987654336 @例如。
  • 聪明!我的版本当然可以使用一些清理/简化。我的第一个重点是让它在不破坏提交和工作树的情况下完全工作:-)。
  • 是的,我以前被“先优化,以后工作”的方法烧死了 :-)
猜你喜欢
  • 2019-06-18
  • 2017-05-15
  • 2014-02-15
  • 1970-01-01
  • 2021-11-05
  • 2020-05-21
  • 1970-01-01
  • 2021-03-19
  • 1970-01-01
相关资源
最近更新 更多