この記事の目次
受付終了を知らせる変更を共有した後で、まだ受付中だったと分かった。その間に別の人が地図の説明を直している。一式を古い版へ戻すと、残したい地図の修正まで失う可能性があります。取り消す対象を一コミットに絞り、その変更を逆向きに適用する方法を試します。
git addとgit commitを使ったことがある人向けです。Git 2.50.1とmacOS・Linuxなどのシェルで、架空の施設案内を扱います。共有済みの履歴を想定したローカル練習であり、リモートの作成や実サイトの公開は行いません。必ず新しい空のフォルダで始めてください。
過去の版へ戻すことと、過去の変更を取り消すこと
git revertは指定したコミットが加えた変更を逆転させ、その結果を新しいコミットとして記録します。元のコミットを履歴から消す操作ではありません。git revertの公式資料にあるこの性質を使い、他の人と共有した履歴の後ろへ訂正を積みます。
練習では、Aで受付と地図を作り、Bで受付終了へ変更し、Cで地図を修正します。取り消したいのはBだけです。
| 記録 | notice.txt | map.txt |
|---|---|---|
| A:最初の案内 | 受付中 | 北口から入る |
| B:受付の変更 | 受付終了 | 北口から入る |
| C:地図の修正 | 受付終了 | 東口から入る |
| R:Bをrevert | 受付中 | 東口から入る |
Rの内容はAと同じではありません。Bの影響を取り消しつつ、Cの地図を残すのが目的です。戻す対象を考える準備はバックアップの範囲を決める記事に共通しますが、ここでは保存一式の復元ではなく、Gitが記録した変更を選びます。
三つの記録を作り、取り消す番号を確保する
まだ存在しない名前のフォルダを使います。同名があれば別名にしてください。次は練習用の二ファイルを作るコマンドです。名前とメールはこのリポジトリだけに設定し、全体のGit設定を変えません。
mkdir git-revert-practice
cd git-revert-practice
git init
git config user.name Practice
git config user.email practice@example.invalid
printf '受付中\n' > notice.txt
printf '北口から入る\n' > map.txt
git add -- notice.txt map.txt
git commit -m "Add initial guidance"
printf '受付終了\n' > notice.txt
git add -- notice.txt
git commit -m "Close registration"
target_commit=$(git rev-parse HEAD)
printf '東口から入る\n' > map.txt
git add -- map.txt
git commit -m "Correct entrance guidance"
git log --oneline -3
git status --short
logは新しい順に地図の修正、受付終了、最初の案内を表示します。statusは空になるのが開始条件です。target_commitにはBの番号を保存しました。同じターミナルを使い続けます。閉じた場合はlogで受付変更の番号を調べ、再設定してください。
番号は実行ごとに異なります。本文のA・B・Cは説明用の呼び名で、コマンドへそのまま入力する値ではありません。実作業でも「一つ前だから」と順番だけで選ばず、次で対象の内容を読みます。
git show --stat "$target_commit"
git show "$target_commit" -- notice.txt
git showの説明に沿って、コミットに含まれるファイルと差分を確認しています。notice.txtだけが対象で、受付中から受付終了への変更であることを確かめます。想定外のファイルが混ざっていたら、revertへ進まず取り消す単位を見直します。
新しい訂正を追加し、残した変更も読む
作業ツリーとステージが空であることを確認してから、同じターミナルで実行します。--no-editは、用意されるコミットメッセージの編集画面を開かない指定です。
git revert --no-edit "$target_commit"
git show HEAD:notice.txt
git show HEAD:map.txt
git log --oneline -4
git status --short
期待する本文は「受付中」と「東口から入る」です。履歴には先頭へRevertで始まる記録が増え、地図の修正と元の受付変更も残ります。statusは再び空です。revertが成功したというメッセージだけでは、狙った内容を得たかは判断できません。二ファイルを読む確認を残します。
さらにgit show HEAD -- notice.txt map.txtで、新しい訂正の差分を見ます。受付終了から受付中へ変わり、地図には変更がないことを確かめてください。手元のエディターと、Gitに記録したHEAD側の内容を取り違えないため、showで記録側を読んでいます。
この時点で本番サイトまで戻ったわけではありません。通常の共有手順や配信があるリポジトリでは、その手順を通し、公開URLの本文も照合します。HTTP 200だけで復旧を判定せず、「受付中」と地図の説明が両方正しいかを見る必要があります。
最新のコミットを選ぶ失敗と、競合で止まる場合
第一の失敗は、Cが最新の段階でgit revert HEADを実行することです。受付ではなく地図の修正を取り消し、北口へ戻します。HEADは「問題の変更」の別名ではなく、現在の先頭を指すためです。予防はlogとshowでBを特定すること。練習で誤って実行した場合は、追加された取消コミットの差分を確認し、その取消をrevertして地図を東口へ戻してから、Bを対象にし直します。
誤操作をした履歴と、最初の四コミットの完成例を混ぜたくなければ、新しい練習フォルダでやり直せます。共有済みの履歴を都合よく消して、最初と同じ見た目へ合わせる必要はありません。どの変更を取り消したかを説明できることが目的です。
第二の失敗は、後続の変更が同じ行を編集しているのに、必ず自動で戻せると考えることです。たとえばBの後でnotice.txtを「受付一時停止」へ変えてコミットした場合、Bを逆転しようとしても、現在の行が「受付終了」と一致せず競合することがあります。
競合では通常のrevert完了コミットはまだできていません。git
statusで対象を読み、どの文を残すか判断します。今回は競合の手編集を扱わないので、意図した文章を決められなければgit revert --abortでこの試行を中止します。開始前のファイルとHEADへ戻ったことをshowとstatusで確認し、残す文章を決めてから改めて訂正します。中止は復旧の完了ではありません。
一コミットを取り消してよい条件を選ぶ
今回のBとCは別ファイルで独立しています。しかし、Bで作ったクラス名をCのHTMLが使っているなら、競合せずrevertできても画面が壊れるかもしれません。Gitが差分を適用できることと、アプリの整合が保たれることは別です。変更を分けてコミットする記事のように、元から説明できる単位へ分けておくと判断材料になります。
一コミットに必要な変更と不要な変更が混在するなら、すべてをrevertするより必要な訂正を新しく書く方が適する場合があります。また、マージコミットのrevertには主となる親の指定と後のマージへの影響があります。この単純な一本道の例を、そのままマージの取消へ使わないでください。
未コミットの編集を捨てたい場面も別の操作です。本記事は、すでに記録した変更の効果を取り消すためのものです。作業途中のファイルがある場合、いきなり消して準備条件を作らず、保存方法と作業の分離を先に決めます。
受付・地図・履歴の三点がそろえば完了
15〜20分の練習では、受付が受付中、地図が東口、履歴がA・B・Cを残してRを追加、という三条件を照合します。statusが空であることも確認します。追加課題では新しい練習フォルダで「受付一時停止」の競合条件を作り、abort後にその文が残るか調べてください。
公開・運用の無料レッスンへつなぐ際は、取消の対象番号、残す変更、配信後に読む文字を記録できます。訂正後の確認先は更新後の確認範囲の記事で整理できます。取り消した受付だけでなく、残した東口の案内まで照合したところで、この練習の完了とします。
TRY A LESSON
記事の内容を、課題で確かめる
記載内容へのご質問や訂正のご連絡は、hello@junkbranding.comまでお願いします。