当我提交--amend时,如果提交已经被推送到远程存储库,则它是不安全的提交。
我想通过pre-commit hook和abort来检测不安全的commit --amend。
但是pre-commit hook没有参数。我不知道怎么检测--amend。
我该怎么办?
答案 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
,提交G
是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
现在,在您的回购副本中,您决定使用branch
。您修复了一个错误并git commit
:
A - B - C - D <-- master, origin/master
\
E <-- origin/branch
\
F <-- HEAD=branch
你现在领先于origin
而git 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
或者您可以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'
周围加上括号表示,虽然它仍在您的仓库中,但它不再有任何分支标签指向对它来说,它几乎是不可见的。)现在,预提交钩子脚本不会再次反对。
答案 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