当我提交 --amend 时,如果提交已经被推送到远程存储库,则它是不安全的提交。
我想通过预提交钩子和中止来检测不安全的提交——修改。
但是预提交钩子没有参数。我不知道如何检测--amend。
我应该怎么办 ?
遵循@Roger Dueck 的回答,最终做了:
#./.git/hooks/prepare-commit-msg
IS_AMEND=$(ps -ocommand= -p $PPID | grep -e '--amend');
if [ -n "$IS_AMEND" ]; then
return;
fi
TL;DR 版本:下面有一个脚本(有点在中间),它强制执行可能对您有用或可能不适用的特定工作流程。它并不能完全阻止特定git commit --amend
的 s(而且您总是可以使用它--no-verify
来跳过脚本),并且它确实阻止(或至少警告)其他git commit
s,这可能是您想要的,也可能不是您想要的。
要使其出错而不是警告,请更改WARNING
为ERROR
并更改sleep 5
为exit 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
,而 commitG
是F
. 但是,这确实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
你现在领先origin
并git 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
并检查HEAD
commit ( 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
表示,虽然它仍在你的仓库中,但它不再有任何指向它的分支标签,所以它几乎是不可见的。)现在预提交钩子脚本不会'再次反对。
在预提交挂钩中检测“纯”修改的快速方法:
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
.
按照@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
--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