如何通过预提交钩子检测commit --amend?

时间:2013-10-15 17:12:42

标签: git githooks

当我提交--amend时,如果提交已经被推送到远程存储库,则它是不安全的提交。

我想通过pre-commit hook和abort来检测不安全的commit --amend。

但是pre-commit hook没有参数。我不知道怎么检测--amend。

我该怎么办?

5 个答案:

答案 0 :(得分:4)

TL; DR版本:下面有一个脚本(中间的一种)强制执行可能适合您的特定工作流程,或者可能不适用。它并不完全阻止特定的git commit --amend(加上你总是可以使用--no-verify跳过脚本),它确实阻止(或至少警告)其他git commit s,可能是也可能不是你想要的。

要将其设置为错误而非警告,请将WARNING更改为ERROR并将sleep 5更改为exit 1

编辑:错误输出不是一个好主意,因为你无法在这个git钩子中告诉这是一个“修改”提交,所以这会失败(你必须如果您只是将新提交添加到具有上游且位于上游头部的分支,则添加--no-verify


这不是必然不安全,因为git commit --amend实际上并没有更改您的仓库中的任何提交,它只是添加了一个新的,不同的提交和重新点那里的分支小费。例如,如果您的分支看起来像这样:

A - B - C - D      <-- master, origin/master
          \
            E - F  <-- HEAD=branch, origin/branch

那么成功git commit --amend的作用是什么:

A - B - C - D      <-- master, origin/master
          \
            E - F  <-- origin/branch
              \
                G  <-- HEAD=branch

您仍然有提交F,提交GF的“修正”版本。但是,G确实不是F的“快进”,在这种情况下您可能不应该git push -f origin branch

如果您已经已经在这种情况下发生类似的情况,即在成功git commit --amend之后(没有或尽管下面的脚本完成):

A - B - C - D       <-- master, origin/master
          \
            E - F   <-- origin/branch
              \
                G   <-- HEAD=branch

如果您现在git commit(即使没有--amend),您还会添加新的提交,例如G连接到H;但同样,试图推动H是一个非快进。

您无法专门测试--amend,但可以检查是否存在“上游”,如果是,则检查当前HEAD是否为该上游的祖先。这是一个稍微俗气的预提交钩子(用警告和睡眠而不是错误退出)。

#!/bin/sh

# If initial commit, don't object
git rev-parse -q --verify HEAD >/dev/null || exit 0

# Are we on a branch?  If not, don't object
branch=$(git symbolic-ref -q --short HEAD) || exit 0

# Does the branch have an upstream?  If not, don't object
upstream=$(git rev-parse -q --verify @{upstream}) || exit 0

# If HEAD is contained within upstream, object.
if git merge-base --is-ancestor HEAD $upstream; then
    echo "WARNING: if amending, note that commit is present in upstream"
    sleep 5:
fi
exit 0

这里的基本问题是,即使不使用git commit --amend,这种情况也会一直发生。假设您从上面的设置开始,但提交F尚不存在:

A - B - C - D      <-- master, origin/master
          \
            E      <-- HEAD=branch, origin/branch

现在,在您的回购副本中,您决定使用branch。您修复了一个错误并git commit

A - B - C - D      <-- master, origin/master
          \
            E      <-- origin/branch
              \
                F  <-- HEAD=branch

你现在领先于origingit push origin branch会做正确的事。但是当你修复一个错误时,Joe在他的副本中修复了一个不同的错误,并将他的版本推送到origin/branch,打败了你{ {1}}步骤所以你运行push进行更新,现在你有了这个:

git fetch

(其中A - B - C - D <-- master, origin/master \ E - J <-- origin/branch \ F <-- HEAD=branch 是Joe的提交)。这是一个完全正常的状态,能够J添加另一个修复(例如,第三个错误)然后合并或重组以包含Joe的修复也会很好。示例预提交钩子将对象。

如果您始终首先将重新绑定或合并,那么添加第三个修补程序,脚本将不会反对。让我们看一下当我们进入上面的git commit - 和 - F情况并使用J(或进行合并的git merge)时会发生什么:

git pull

您现在处于提交A - B - C - D <-- master, origin/master \ E - J <-- origin/branch \ \ F - M <-- HEAD=branch ,即合并,它位于“{1}}之前”。因此脚本M找到提交J并检查@{upstream}提交(J)是否是HEAD的祖先。它不是,并允许其他新的提交,所以你的“修复第三个错误”提交M给你这个:

J

或者您可以NA - B - C - D <-- master, origin/master \ E - J <-- origin/branch \ \ F - M - N <-- HEAD=branch ,以便在您修复第三个错误之前:

git rebase

(这里J是挑选的提交A - B - C - D <-- master, origin/master \ E - J <-- origin/branch \ \ (F) F' <-- HEAD=branch ;我在F'周围加上括号表示,虽然它仍在您的仓库中,但它不再有任何分支标签指向对它来说,它几乎是不可见的。)现在,预提交钩子脚本不会再次反对。

答案 1 :(得分:3)

在预提交挂钩中检测“纯”修改的快速方法:

if git diff --cached --quiet ; then
  echo "This is a pure amend"
else
  echo "This is a commit with changes"
fi

“纯”是指你只是重写提交消息而不是提交中的任何更改。如果在调用git commit --amend时索引中有任何更改,则您的重写次数会超过提交消息,这就像您正在执行传统的git commit一样。

答案 2 :(得分:0)

在标准shell中检查这是否为--amend情况的另一种方法:

git_command=$(ps -ocommand= -p $PPID)
if [ -z "${git_command##git\ commit*--amend*}" ]; then
    echo "The original command was a: git commit --amend"
    exit 0
fi

答案 3 :(得分:0)

在@perror的回答之后,我想到了以下内容:

parent=$(/bin/ps -o ppid -p $PPID | tail -1)
if [ -n "$parent" ]; then
    amended=$(/bin/ps -o command -p $parent | grep -e '--amend')
    if [ -n "$amended" ]; then
        echo "This is an 'amend'"
    fi  
fi

答案 4 :(得分:0)

按照@Roger Dueck的回答,最终做了:

#./.git/hooks/prepare-commit-msg

IS_AMEND=$(ps -ocommand= -p $PPID | grep -e '--amend');

if [ -n "$IS_AMEND" ]; then
  return;
fi