取り消した機能を入れ直そうとしたら、合わせるものがないと言われた
目標
マージコミットを元に戻して復活させ、リリースブランチの修正を出典を残しながら取り込みます。そして、何がすでに入っているかを機械がどう判定するのかまで確認します。
なぜ重要なのか
resetはコミットを消し、revertは打ち消すコミットを加えます。すでに他の人に渡った履歴には、revertしか使えませんが、マージコミットでは、その打ち消しが単純ではありません。親が2つあるため、どちらの状態に戻るかを、人が指定する必要があります。そして、元に戻したあとのほうが、より重要です。mergeは、履歴の到達可能性で判断するため、元に戻した機能を再びmergeしても、「マージするものがない」と答えます。この2つを知らないと、障害対応中に、同じ場所で30分を失います。最後の2つのステップは、バックポート運用の基本です。同じ変更がすでに入っているかを、ハッシュではなくpatch-idで判定することと、衝突を解決して取り込んだコミットは、その判定から漏れることを、自分で作って確かめます。
ステップ
/root/gitx7/repoに、マージコミットとリリースブランチがある履歴を作ります。- マージコミットを
-mなしで元に戻してみて、拒否メッセージをnotes/revert-m.txtに残したあと、-m 1で元に戻します。 git merge featureが何と答えるかをnotes/reland.txtに書き、取り消しを取り消して、機能を復活させます。- リリースのホットフィックスのコミットを、
-xでmainに取り込みます。 - リリースの設定変更のコミットを取り込もうとして衝突したら、
--abortで引き返し、もう一度行って--continueで完了させます。過程をnotes/conflict.txtに残します。 git cherry -v main releaseの結果をnotes/cherry.txtに残します。- 元のコミットと、取り込んだコミットのpatch-idを、
notes/patchid.txtに並べて書きます。 notes/report.mdに、いつ何を使うかをまとめます。
参考
- リポジトリごとに、
git config user.emailとuser.nameを指定しないと、コミットできません。コミットの時刻は、GIT_AUTHOR_DATEとGIT_COMMITTER_DATEで固定してください。 git revert --no-editとGIT_EDITOR=trueを使うと、エディターが開きません。- よくある間違い: ステップ3で、
mergeが「Already up to date」と出すことを失敗とみなして、--force系を探すことです。正確な答えであり、解決策は、取り消しを取り消す方向です。 - よくある間違い: ステップ5で、衝突マーカー(
<<<<<<<)をファイルに残したままgit addすることです。そのマーカーまでコミットされます。
マージコミットとリリースブランチがある履歴
/root/gitx7/repoに、マージコミットとリリースブランチがある履歴を作ります。
mainに基本のファイルをコミットし、featureブランチでfeature.txtを追加してapp.txtの2行目を直したあと、--no-ffでマージします。その地点からreleaseを作り、ホットフィックス・設定変更・リリースメモの3つを積み、mainに戻って、同じ設定値を別の内容に直します。この最後の1つが、ステップ5の衝突を作ります。
マージコミットは、どちらに戻るかを指定する必要がある
マージコミットを-mなしで元に戻してみて、拒否メッセージをnotes/revert-m.txtに残したあと、-m 1で元に戻します。
マージコミットのハッシュは、git rev-list --merges mainで探します。ただのgit revert <해시>を実行すると、拒否されます(プレースホルダーはハッシュです)。その行をそのまま残してください。-m 1が何を基準にするという意味かも書き、--no-editを付けて、エディターなしで元に戻します。
再びmergeしても、マージするものがない
git merge featureが何と答えるかをnotes/reland.txtに書き、取り消しを取り消して、機能を復活させます。
git merge featureをそのまま実行してみてください。1行で答えます。なぜそう答えるのかを書いてください。mergeは、内容ではなく履歴の到達可能性で判断します。復活させる方法は、先ほど作った取り消しのコミットを、もう一度git revertすることです。
出典を残しながら取り込む
リリースのホットフィックスのコミットを、-xでmainに取り込みます。
hotfix.txtを追加したリリースのコミットのハッシュは、git rev-list release -- hotfix.txtで探します。git cherry-pick -x <해시>を実行すると(プレースホルダーはハッシュです)、新しいコミットメッセージの末尾に1行が付きます。git log -1 --format=%Bで確認してください。
衝突したら、引き返してまた戻る
リリースの設定変更のコミットを取り込もうとして衝突したら、--abortで引き返し、もう一度行って--continueで完了させます。過程をnotes/conflict.txtに残します。
conf.txtを直したリリースのコミットをgit cherry-pickすると、衝突します。まずgit status --shortでUUを見て、git cherry-pick --abortで、きれいに引き返してみてください。そのあと、再び取り込み、conf.txtをリリースが決めた値に直してgit addし、GIT_EDITOR=true git cherry-pick --continueで完了させます。衝突マーカーの行がファイルに残っていないかを、必ず確認してください。
何がすでに入っているかを数えてみる
git cherry -v main releaseの結果をnotes/cherry.txtに残します。
git cherry -v <upstream> <head>は、headのコミット1つ1つがupstreamにすでにあるかを、patch-idで判定します。-はある、+はないです。3つのコミットのうち、1つだけが-で出ますが、衝突を解決して取り込んだものが、なぜ+なのかを、必ず書いてください。このツールの限界が、そこにあります。
ハッシュは違うのにpatch-idは同じ
元のコミットと、取り込んだコミットのpatch-idを、notes/patchid.txtに並べて書きます。
git show <커밋> | git patch-id --stableは2つの値を出します(プレースホルダーはコミットです)。前がpatch-id、後ろがコミットのハッシュです。ステップ4で-xで取り込んだコミットは、元とpatch-idが同じです。そのコミットは、git log --grep 'cherry picked from commit <해시>'で探せます(プレースホルダーはハッシュです)。
いつ何を使うか
notes/report.mdに、いつ何を使うかをまとめます。
resetとrevertを分ける問い、マージコミットを元に戻すときの-m、復活させるときにmergeではなくrevertのrevertを使う理由、-xをルールにする理由、そしてpatch-id判定の限界まで、5つのまとまりで書いてください。