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番目のバグを修正」コミットすると次のようになります。HEADMJN
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