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

ton*_*ton 9 git githooks

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

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

但是pre-commit hook没有参数.我不知道如何检测--amend.

我该怎么办 ?

kun*_*o20 9

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

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

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

if [ -n "$IS_AMEND" ]; then
  return;
fi
Run Code Online (Sandbox Code Playgroud)


tor*_*rek 5

TL; DR版本:下面有一个脚本(中间是一种),用于强制执行一个可能对您有用或不起作用的特定工作流程。它并不能完全阻止特定的git commit --amends(而且您可以始终使用它--no-verify来跳过脚本),并且可以阻止(或至少警告)其他git commits,而这可能不是您想要的。

要使其出错而不是警告,请更改WARNING为,ERROR然后更改sleep 5exit 1

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


不一定是不安全的,因为git commit --amend实际上不会更改您的存储库中的任何提交,它只是添加了一个新的不同的提交,并在此处重新指向了分支提示。例如,如果您的分支如下所示:

A - B - C - D      <-- master, origin/master
          \
            E - F  <-- HEAD=branch, origin/branch
Run Code Online (Sandbox Code Playgroud)

那么成功的git commit --amend是:

A - B - C - D      <-- master, origin/master
          \
            E - F  <-- origin/branch
              \
                G  <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

您仍然具有commit F,并且commit G是的“修订版” F。但是,确实这G不是“快速前进”的方法Fgit push -f origin branch在这种情况下您可能不应该这样做。

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

A - B - C - D       <-- master, origin/master
          \
            E - F   <-- origin/branch
              \
                G   <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

如果现在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
Run Code Online (Sandbox Code Playgroud)

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

A - B - C - D      <-- master, origin/master
          \
            E      <-- HEAD=branch, origin/branch
Run Code Online (Sandbox Code Playgroud)

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

A - B - C - D      <-- master, origin/master
          \
            E      <-- origin/branch
              \
                F  <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

您现在处于领先地位,origin并且git push origin branch会做正确的事。但是,当您修复一个错误时,Joe修复了仓库中的另一个错误,并将版本推到origin/branch,击败了您push。因此,您运行git fetch更新,现在有了这个:

A - B - C - D      <-- master, origin/master
          \
            E - J  <-- origin/branch
              \
                F  <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

JJoe的提交在哪里)。这是一个完全正常的状态,能够git commit添加另一个修复程序(例如第三个错误),然后合并或重新设置以包括Joe的修复程序也很不错。示例预提交挂钩将成为对象。

如果你总是变基-或合并第一,然后加上你的第三个补丁,脚本不会反对。让我们看看当我们进入上面的F-and- J情况并使用git merge(或git pull进行合并的a)时会发生什么:

A - B - C - D             <-- master, origin/master
          \
            E - J         <-- origin/branch
              \   \
                F - M     <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

您现在处于commit M,merge的位置,该合并处于“ heading of”之前J。因此,脚本会@{upstream}查找commit J并检查HEADcommit(M)是否为的祖先J。并非如此,并且允许进行其他新的提交,因此您的“修复第三个错误”提交N将为您提供:

A - B - C - D             <-- master, origin/master
          \
            E - J         <-- origin/branch
              \   \
                F - M - N <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

另外,您可以git rebase转到J,以便在修复第三个错误之前,您需要:

A - B - C - D          <-- master, origin/master
          \
            E - J      <-- origin/branch
              \  \
              (F) F'   <-- HEAD=branch
Run Code Online (Sandbox Code Playgroud)

(这F'是精心挑选的提交F;我在括号F中指出,虽然它仍在您的存储库中,但它不再指向任何分支标签,因此它几乎是不可见的。)再次反对。