はじめに
このラボでは、Git のコミット履歴を管理する方法を学びます。git reset を使用した変更の取り消し、git revert を使用した公開コミットの安全な元に戻し、インタラクティブなリベースを使用したブランチからの特定のコミットの削除を実践します。また、削除されたコミットを復旧するために reflog を使用する方法も学びます。これらは、クリーンで理解しやすいプロジェクト履歴を維持するために不可欠なスキルです。
このラボでは、Git のコミット履歴を管理する方法を学びます。git reset を使用した変更の取り消し、git revert を使用した公開コミットの安全な元に戻し、インタラクティブなリベースを使用したブランチからの特定のコミットの削除を実践します。また、削除されたコミットを復旧するために reflog を使用する方法も学びます。これらは、クリーンで理解しやすいプロジェクト履歴を維持するために不可欠なスキルです。
履歴を変更する前に、まず履歴を表示する方法を知る必要があります。git log コマンドがそのための主要なツールです。この実験では、実際のプロジェクト履歴をシミュレートするために、いくつかのコミットで Git リポジトリが初期化されています。
まず、コミット履歴を確認しましょう。各コミットを一行で表示するために --oneline フラグを、コミット履歴をグラフとして表示するために --graph フラグを使用します。これはブランチやマージを視覚化するのに役立ちます。
ターミナルで以下のコマンドを実行してください。
git log --oneline --graph
あなたのために作成された 5 つのコミットのリストが表示され、最新のコミットが一番上に表示されるはずです。各行には、一意のコミットハッシュ(短いバージョン)とコミットメッセージが表示されます。
* 7d3d24a (HEAD -> master) chore: Add last-commit file
* 8a9f1b3 docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt
注意:あなたのコミットハッシュは上記の例とは異なります。これは予想されることであり、各ハッシュは一意です。
しばらく時間を取って履歴を確認してください。次のステップでこれらのコミットを操作します。
git reset による直近のコミットの取り消しコミットを行った直後に間違いに気づくことがあります。このようなシナリオには git reset コマンドが最適です。これは、現在のブランチの HEAD ポインタを以前のコミットに移動させることで、1 つ以上のコミットを効果的に「元に戻す」ことができます。
ここでは --soft オプションを使用します。これはコミットを取り消しますが、そのコミットによる変更はステージングエリア(インデックス)に残します。これは、変更を再コミットしたい場合、例えば異なるメッセージでコミットしたり、他の変更と組み合わせたりする場合に便利です。
直近のコミットである「chore: Add last-commit file」を取り消してみましょう。
git reset --soft HEAD~1
ここで、HEAD~1 は現在の HEAD の直前のコミットを参照します。
次に、リポジトリのステータスを確認してください。
git status
last-commit.txt が「Changes to be committed」(コミットされる変更)の下にリストされていることがわかります。これは、コミット自体は取り消されましたが、ファイルの変更はステージングされたままになっていることを意味します。
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: last-commit.txt
最後に、ログを再度表示して、コミットが履歴から削除されたことを確認してください。
git log --oneline --graph
* 8a9f1b3 (HEAD -> master) docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt
ご覧のとおり、直近のコミットはログから削除されましたが、作業はステージングエリアに安全に待機しています。
git revert によるコミットの取り消しgit reset はローカルの変更には便利ですが、履歴を書き換えるため、既に他の人と共有したコミットに対して行うと問題が発生する可能性があります。公開されているブランチの変更を取り消すより安全な方法は git revert です。このコマンドは、指定したコミットの変更の逆の変更を適用する 新しい コミットを作成します。
まず、前のステップの変更を再コミットして、作業ディレクトリをクリーンアップしましょう。
git commit -m "chore: Add last-commit file again"
次に、「file2.txt」を追加したコミットを取り消したいとします。このコミットは履歴のさらに前にあります。これをリバートします。 HEAD に対する相対位置で参照できます。ステップ 1 のログを見ると、「feat: Add file2.txt」は上から 4 番目のコミットなので、HEAD~3 として参照できます。
リバートコマンドを実行します。
git revert HEAD~3
これにより、デフォルトのテキストエディタ(デフォルトでは vim)が開き、「Revert 'feat: Add file2.txt'」のようなコミットメッセージが事前に入力されます。デフォルトのメッセージを受け入れるには、ファイルを保存して閉じるだけで構いません。
vim では、:wq と入力して Enter を押すと保存して終了します。
次に、ログを再度確認してください。
git log --oneline --graph
一番上に「Revert 'feat: Add file2.txt'」という新しいコミットが表示されます。
* 1e2d3f4 (HEAD -> master) Revert "feat: Add file2.txt"
* 7d3d24a chore: Add last-commit file again
* 8a9f1b3 docs: Update documentation in file1
* c2e4d6f fix: Add a temporary file that should be removed
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt
元のコミットは履歴に残っていますが、その変更は新しいリバートコミットによって取り消されています。ディレクトリ内のファイルをリストすることで、これを検証できます。
ls
file2.txt が存在しなくなっていることに気づくでしょう。
ブランチの途中にあるコミットを削除するなど、より複雑な履歴操作を行うには、インタラクティブ・リベース (git rebase -i) を使用できます。これはコミット履歴を書き換える非常に強力なコマンドなので、特に他の開発者と共有しているブランチでは注意して使用する必要があります。
目標は、「fix: Add a temporary file that should be removed」というメッセージのコミットを削除することです。現在のログを見ると、このコミットは現在 HEAD~3 にあります。
インタラクティブ・リベースのプロセスを開始します。
git rebase -i HEAD~4
このコマンドは、テキストエディタ(デフォルトでは vim)を開き、直近 4 つのコミットのリストを表示します。
pick fd5a181 fix: Add a temporary file that should be removed
pick 5f27a4d docs: Update documentation in file1
pick 284be6f chore: Add last-commit file again
pick 8a460c5 Revert "feat: Add file2.txt"
## Rebase 9403080..8a460c5 onto 9403080 (4 commands)
#
## Commands:
## p, pick <commit> = use commit
## d, drop <commit> = remove commit
## ...
コミットを削除するには、「fix: Add a temporary file that should be removed」を含む行の pick を drop(または d)に変更します。
これを変更します。
pick fd5a181 fix: Add a temporary file that should be removed
これに:
drop fd5a181 fix: Add a temporary file that should be removed
vim では、i を押して挿入モードに入り、変更を加えてから Esc を押して挿入モードを終了します。:wq と入力して Enter を押すと保存して終了します。Git は残りのコミットを新しい履歴の上に再適用します。
ログを確認して結果を見てみましょう。
git log --oneline --graph
「悪い」コミットは履歴から削除されました。
* a5b4c3d (HEAD -> master) chore: Add last-commit file again
* f9e8d7c docs: Update documentation in file1
* 1e2d3f4 Revert "feat: Add file2.txt"
* 5e1a3b2 feat: Add file2.txt
* 1b4c0a9 feat: Add file1.txt
また、ディレクトリ内のファイルも確認してください。ファイル bad-commit-file.txt は削除されました。
ls
git reflog による削除されたコミットの復元誤ってコミットを削除してしまった場合はどうすればよいでしょうか?Git は、HEAD ポインタに対するすべての変更を reflog と呼ばれる特別なログに記録しています。これはあなたのセーフティネットです。これを使用して、失われたコミットを見つけて復元できます。
リファログを表示して、先ほど書き換えた履歴を見つけましょう。
git reflog
実行したアクションのリストが表示されます。「rebase (finish): returning to refs/heads/master」という行を探してください。そのすぐ下のエントリ、おそらく HEAD@{1} は、リベース前のブランチの状態を表しています。
a5b4c3d (HEAD -> master) HEAD@{0}: rebase (finish): returning to refs/heads/master
1e2d3f4 HEAD@{1}: rebase (start): checkout HEAD~3
...
この前の状態にブランチを復元するには git reset --hard を使用できます。このコマンドは破壊的であり、コミットされていない変更をすべて破棄するため、注意して使用してください。
この場合 HEAD@{1} である、リベース前の状態にブランチをリセットしましょう。
git reset --hard HEAD@{1}
リベースが開始される前のコミットに HEAD が現在あることを確認するメッセージが表示されます。
HEAD is now at 1e2d3f4 Revert "feat: Add file2.txt"
最後にログを確認してください。
git log --oneline --graph
リベース前の履歴が表示されるはずです。
* f461400 (HEAD -> master) Revert "feat: Add file2.txt"
* acea45c chore: Add last-commit file again
* c04b3f5 docs: Update documentation in file1
* 9403080 feat: Add file2.txt
* ee39412 feat: Add file1.txt
コミット「fix: Add a temporary file that should be removed」はまだ存在しないことに注意してください。これは正しいです! git reset --hard HEAD@{1} コマンドは、インタラクティブ・リベースを開始する前の状態に私たちを復元したのであり、コミットを削除する前の状態に戻したわけではありません。インタラクティブ・リベースは、その不要なコミットを履歴から正常に削除しました。ls を実行して、bad-commit-file.txt がまだ存在しないことを確認できます。reflog は、Git での間違いから回復するための非常に貴重なツールです。
この実験では、Git のコミット履歴を管理するための実践的なテクニックを学びました。まず git log で履歴を検査しました。次に、git reset --soft を使用してコミットを元に戻す練習、git revert で安全に変更を元に戻す方法、そして強力な git rebase -i コマンドで特定のコミットを削除する方法を実践しました。最後に、履歴から削除されたコミットを復元するためのセーフティネットとして git reflog を使用する方法を学びました。これらのスキルは、クリーンで管理しやすいプロジェクト履歴を維持するために不可欠です。