19

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

我想通过预提交钩子和中止来检测不安全的提交——修改。

但是预提交钩子没有参数。我不知道如何检测--amend。

我应该怎么办 ?

4

5 回答 5

13

遵循@Roger Dueck 的回答,最终做了:

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

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

if [ -n "$IS_AMEND" ]; then
  return;
fi
于 2019-07-09T14:56:03.610 回答
7

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

要使其出错而不是警告,请更改WARNINGERROR并更改sleep 5exit 1.

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


不一定是不安全的,因为git commit --amend实际上并没有更改您的 repo 中的任何提交,它只是添加了一个新的、不同的提交并将分支提示重新指向那里。例如,如果您的分支如下所示:

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

你仍然有 commit F,而 commitGF. 但是,这确实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

现在你,在你的 repo 副本中,决定处理branch. 您修复了一个错误并git commit

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

你现在领先origingit push origin branch会做正确的事。但是,当您修复一个错误时,乔在的 repo 副本中修复了一个不同的错误,并将他的版本推送到origin/branch,从而使您领先push一步。所以你跑去git fetch更新,你现在有了这个:

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

J乔的承诺在哪里)。这是一个完全正常的状态,如果能够git commit添加另一个修复(例如,第三个错误),然后合并或变基以包含 Joe 的修复,那就太好了。示例预提交挂钩将反对。

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

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

您现在处于 commit M,即“领先于”的合并中J。所以脚本会@{upstream}找到 commitJ并检查HEADcommit ( M) 是否是J. 不是,并且允许进行额外的新提交,因此您的“修复第三个错误”提交N会为您提供:

A - 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'是精心挑选的提交F;我用括号F表示,虽然它仍在你的仓库中,但它不再有任何指向它的分支标签,所以它几乎是不可见的。)现在预提交钩子脚本不会'再次反对。

于 2013-10-15T19:13:57.030 回答
4

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

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.

于 2017-02-01T17:41:35.660 回答
2

按照@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
于 2019-06-27T21:19:24.077 回答
0

--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
于 2019-06-24T13:50:00.273 回答