当我执行commit --amend时,如果该提交已经推送到远程存储库,则这是一次不安全的提交。
我想通过pre-commit hook检测不安全的commit --amend,并中止提交。
但pre-commit hook没有参数。我不知道如何检测--amend。
我应该怎么办?
跟随@Roger Dueck的回答,最终采取了以下措施:
#./.git/hooks/prepare-commit-msg
IS_AMEND=$(ps -ocommand= -p $PPID | grep -e '--amend');
if [ -n "$IS_AMEND" ]; then
return;
fi
-e
选项,则会失败。请改用:if ps -ocommand= -p $PPID | grep -q -e '--amend'; then ...; fi
- fferriexit 0
而不是 return
。 - Max D. -- Stand with Ukraine$(ps -ocommand= -p $PPID)
返回的是 /bin/sh .husky/prepare-commit-msg .git/COMMIT_EDITMSG commit HEAD
。 - NickonA - 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
现在你在你的代码仓库副本中决定在分支
上工作。你修复了一个错误并git commit
:
A - B - C - D <-- master, origin/master
\
E <-- origin/branch
\
F <-- HEAD=branch
现在你已经领先于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
(其中 J
是 Joe 的提交)。这是一个完全正常的状态,希望能够使用 git commit
添加另一个修复(例如第三个 bug),然后合并或变基以包括 Joe 的修复。 但是,示例 pre-commit 钩子会有所反应。
如果你总是先变基或合并,然后再添加第三个修复,那么脚本就不会有任何问题。让我们看看当我们进入上面的 F
-和-J
情况,并使用 git merge
(或执行合并的 git pull
)时会发生什么:
A - B - C - D <-- master, origin/master
\
E - J <-- origin/branch
\ \
F - M <-- HEAD=branch
您现在位于合并提交 M
,它比提交 J
要“领先”一步。因此脚本的 @{upstream}
找到提交 J
并检查 HEAD
提交(M
)是否是提交 J
的祖先。但是它不是,允许添加新的额外提交,所以你的“修复第三个 bug”的提交 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
“pure”指的是您仅重写提交消息而不重写提交中的任何更改。如果在调用git commit --amend
时,您的索引中有任何更改,则会重写提交消息以外的内容,这将表现为您正在执行常规的 git commit
。
--allow-empty
的普通提交。 - Piotr Siupa在@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
如prepare-commit-msg中所述:
它需要一个至三个参数。第一个是包含提交日志消息的文件名。第二个是提交消息的来源,可以是:message;...
或commit,后跟提交对象名称(如果提供了-c、-C或--amend选项)
所以我已经使用了
#!/bin/bash
[ "$2" = "commit" -a "$3" = "HEAD" ] && echo "ignore amend or do something"
检查这是否是标准 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
git commit --amend
,而是阻止人们执行git push --force
。 - pjpscriv