commit --amend を実行すると、コミットが既にリモート リポジトリにプッシュされている場合、コミットは安全ではありません。
安全でない commit --amend を pre-commit フックとアボートで検出したい。
ただし、pre-commit フックには引数がありません。--amend を検出する方法がわかりません。
私は何をすべきか ?
TL;DR バージョン: 以下のスクリプト (真ん中のようなもの) があり、特定のワークフローを強制します。git commit --amend
特定の sを正確に防止するわけではなく(さらに、スクリプトをスキップするためにいつでも使用できます)、必要な場合とそうでない場合がある--no-verify
他の s を防止 (または少なくとも警告)します。git commit
警告ではなくエラーアウトにするには、 に変更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
まだ 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
がその上流の祖先であるかどうかを確認できます。これを行う少し安っぽい pre-commit フックを次に示します (エラー終了ではなく警告とスリープを使用)。
#!/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
、正しいことをするでしょう。しかし、あなたが 1 つのバグを修正している間に、Joe は自分のリポジトリのコピーで別のバグを修正し、自分のバージョンをにプッシュしてorigin/branch
、あなたをpush
一歩先に進めました。したがって、実行git fetch
して更新すると、次のようになります。
A - B - C - D <-- master, origin/master
\
E - J <-- origin/branch
\
F <-- HEAD=branch
(J
ジョーのコミットはどこにありますか)。git commit
これは完全に正常な状態であり、別の修正 (たとえば、3 番目のバグ) を追加してから、Joe の修正も含めるようにマージまたはリベースできるとよいでしょう。サンプルの pre-commit フックは反対します。
常に最初にリベースまたはマージしてから 3 番目の修正を追加すると、スクリプトは反対しません。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}
は commitを検出し、 commit( ) が の祖先であるJ
かどうかをチェックします。そうではなく、追加の新しいコミットが許可されているため、「3番目のバグを修正」コミットすると次のようになります。HEAD
M
J
N
A - B - C - D <-- master, origin/master
\
E - J <-- origin/branch
\ \
F - M - N <-- HEAD=branch
別の方法として、3 番目のバグを修正する前に次のことを行うことgit rebase
ができます。J
A - B - C - D <-- master, origin/master
\
E - J <-- origin/branch
\ \
(F) F' <-- HEAD=branch
(F'
これはチェリーピックされたコミットF
です。括弧を付けF
て、まだリポジトリにある間は、それを指すブランチ ラベルがなくなっているため、ほとんど見えないことを示しています。) これで、プレコミット フック スクリプトは動作しません。 t オブジェクト、再び。
これが該当するかどうかを確認する別の方法は--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