Gitの変更を元に戻す:リセット、リストア、リバートの解説

`git reset`、`git restore`、`git revert`で混乱していませんか?このガイドでは、それらの違いを明確にし、実践的な例を提供します。変更を安全に破棄する方法、ファイルをステージ解除する方法、Git履歴のコミットを書き換えるまたは安全に元に戻す方法を学びます。効果的なバージョン管理とよりクリーンなプロジェクトタイムラインのために、これらの必須コマンドをマスターしましょう。

47 ビュー

Gitでの変更の取り消し:reset, restore, revert の解説

Gitは、コードベースへの変更を追跡できる強力なバージョン管理システムです。しかし、間違いは起こるもので、変更を取り消す必要があるかもしれません。Gitにはこれを支援するいくつかのコマンドがありますが、それらは非常に異なった機能を発揮し、リポジトリにそれぞれ異なる影響を与えます。git resetgit restoregit revert のニュアンスを理解することは、プロジェクトの履歴を効果的に管理し、意図しない結果なしにエラーを修正するために不可欠です。

この記事では、これら3つのコマンドを分かりやすく解説し、それぞれの目的、動作方法、および使用する場面を説明します。公式のGitドキュメントと一般的な使用パターンを活用して、明確な説明と実践的な例を提供します。このガイドの終わりには、変更を自信を持って取り消し、ステージングされたファイルを管理し、クリーンで一貫性のあるコミット履歴を維持できるようになるでしょう。

コアコンセプトの理解

コマンドに入る前に、いくつかのGitの概念を理解することが重要です。

  • ワーキングディレクトリ: 現在編集中のファイル。
  • ステージングエリア(インデックス): コミットする前に変更を準備する一時的な領域。git add はワーキングディレクトリからステージングエリアへ変更を移動させます。
  • コミット履歴: コミットによって表される、時間の経過に伴うプロジェクトのスナップショットのシーケンス。
  • HEAD: 現在のブランチの最新コミットを指すポインタ。

これら3つのコマンドは、主にこれらの領域と対話して変更を修正または破棄します。

git restore: ワーキングディレクトリとステージングエリアでの変更の破棄

Gitに最近導入されたgit restoreコマンドは、ワーキングディレクトリでの変更の取り消しやファイルのステージ解除といった、比較的簡単なタスクのために設計されています。これらの特定の操作においては、git resetよりも一般的に安全で直感的だと考えられています。

ファイルのステージ解除

誤ってgit addでファイルをステージングしてしまい、それを解除したい場合は、git restoreが使用するコマンドです。これは変更をステージングエリアからワーキングディレクトリに戻しますが、変更自体を破棄するわけではありません。

  • 特定のファイルのステージ解除:
    bash git restore <file>
    このコマンドは、インデックス(ステージングエリア)からファイルのバージョンを取得し、それをインデックスに戻します。本質的には、次のコミットのためにステージングされている状態からファイルを除外しますが、変更はワーキングディレクトリに残ります。

  • すべてのファイルのステージ解除:
    git resetができるような、すべてをステージ解除する直接的なgit restore .コマンドはありませんが、通常は個々のファイルにgit restoreを適用するか、必要に応じて他のコマンドと組み合わせて使用します。ただし、最も一般的なユースケースは、特定のファイルのステージ解除です。

ワーキングディレクトリでの変更の破棄

git restoreは、特定のファイルに対するワーキングディレクトリ内のすべてのステージされていない変更を破棄し、ステージングエリア(インデックス)または最後にコミットされた状態に戻すためにも使用できます。

  • ファイルのステージされていない変更の破棄:
    bash git restore <file>
    (注:このコマンドは文脈によって2つの意味を持つ可能性があります。--stagedなしで使用した場合、主にワーキングツリーを対象とします。ファイルがステージングされている場合、ステージ解除します。ファイルがワーキングツリーで変更されており、ステージングされていない場合、ワーキングツリーのファイルをインデックスに一致するように戻します。)

  • ファイルに対するステージ済みおよびステージ未済みの両方の変更の破棄(HEADに戻す):
    ファイルに対するすべての変更(ステージ済みおよびステージ未済みの両方)を完全に破棄し、HEADコミットにあった状態に戻すには、以下を実行します。
    bash git restore --staged --worktree <file>
    これは、ファイルを最後にコミットされた状態に効果的にリセットする強力なコマンドです。

特定のコミットからファイルを復元する

git restoreは、ブランチ履歴を変更せずに、過去のコミットから特定のファイルのバージョンを取得するためにも使用できます。

git restore <file> --source <commit>

<commit>をコミットハッシュまたはHEAD~1のようなシンボリック参照に置き換えてください。

git reset: 履歴の書き換え

git resetは、現在のブランチのHEADポインタを移動させることで、コミット履歴を変更できる、より強力なコマンドです。使用するモードによっては、ステージングエリアやワーキングディレクトリにも影響を与える可能性があります。

モードの理解(--soft--mixed--hard

git resetには3つの主要なモードがあります。

  1. --soft: HEADを指定されたコミットに移動しますが、ステージングエリアとワーキングディレクトリはそのままにします。リセットされたコミットからの変更は、ステージングされた変更として表示されます。
    bash git reset --soft HEAD^ # HEADを1コミット前に移動させ、取り消されたコミットからの変更がステージングされます

  2. --mixed (デフォルト): HEADを移動させ、ステージングエリアを指定されたコミットに一致するようにリセットします。リセットされたコミットからの変更はワーキングディレクトリに存在しますが、ステージングされていません。
    bash git reset HEAD^ # git reset --mixed HEAD^ と同等 # HEADを1コミット前に移動させ、取り消されたコミットからの変更がステージ解除されます

  3. --hard: HEADを移動させ、ステージングエリアとワーキングディレクトリの両方を指定されたコミットに一致するようにリセットします。これにより、リセットされるコミットからのすべての変更と、ワーキングディレクトリ内のそれ以降のコミットされていない変更が破棄されます。 極めて慎重に使用してください。
    bash git reset --hard HEAD~ # 最後のコミットと、それ以降のワーキングディレクトリとステージングエリアのすべての変更を破棄します

git resetの使用例:

  • ファイルのステージ解除: git reset <file>git restore --staged <file>のショートカットであり、ワーキングディレクトリに影響を与えずにステージングエリアからファイルを削除します。
  • すべてをステージ解除: git reset(引数なし、またはHEADを指定)は、現在ステージングされているすべての変更をステージ解除し、ワーキングディレクトリに戻します(git restore --staged .と同等)。
  • 最後のコミットの取り消し: git reset HEAD^(またはgit reset --soft HEAD^)は、最後のコミットを修正するためによく使用されます。前のコミットからの変更がステージングされ、修正を加えて再コミットしたり、新しいメッセージでコミットしたりする準備が整います。
  • すべてのローカル変更の破棄: git reset --hardは、すべてのローカル変更(ステージ済みおよびステージ未済み)を完全に破棄し、リポジトリを指定されたコミットに戻すために使用されます。これは破壊的な操作です。

古いコミットのリセット

最新ではないコミットからの変更を取り消す必要がある場合、git resetを使用できます。例えば、問題のあるコミットののコミットにリセットするには、以下のようにします。

# 例:最後の2つのコミットを取り消し、変更をステージ未済みのままにする
git reset --mixed HEAD~2

警告: git resetは履歴を書き換えます。リセットしたコミットをすでにプッシュしている場合、共同作業者にとって重大な問題を引き起こす可能性があります。ローカルリポジトリにのみ存在するコミットをリセットするのは、一般的に安全です。

git revert: 変更を取り消すための新しいコミットの作成

git revertは、共有または公開された履歴の変更を取り消す最も安全な方法です。履歴を書き換えるのではなく、以前のコミットの逆の変更を導入する新しいコミットを作成します。

仕組み

git revert <commit>を実行すると、Gitは指定されたコミットを分析し、逆の変更を計算し、それらを現在のワーキングディレクトリとステージングエリアに適用します。その後、どのコミットがリバートされたかを示すデフォルトメッセージで新しいコミットを作成するように促します。

  • 特定のコミットのリバート:
    bash git revert <commit-hash>
    これにより、<commit-hash>で導入された変更を取り消す新しいコミットが作成されます。マージコンフリクトが発生した場合、Gitは一時停止し、コミット前に解決を要求します。

  • 複数のコミットのリバート:
    コミットの範囲をリバートできます。
    bash # HEAD~3からHEADまで(HEADを含まない)のコミットをリバート git revert HEAD~3..HEAD
    Gitは、指定された各コミットに対してリバートコミットを作成しようとします。プロセス中にコンフリクトが発生した場合、各リバートについて解決する必要があります。

git revertの利点:

  • 履歴の維持: 既存のコミットを変更しないため、公開または共有ブランチで安全です。
  • 明確な監査証跡: リバートコミットは、何が取り消され、なぜ取り消されたかを明確に示します。
  • マージの円滑な処理: Gitは多くの場合、マージコミットを自動的にリバートできますが、複雑なシナリオでは手動での介入が必要になる場合があります。

git revertを使用する場面:

  • リモートリポジトリにプッシュ済みのブランチの変更を取り消す必要がある場合。
  • すべての変更(修正を含む)の明確で変更不可能な記録を維持したい場合。
  • マージコミットを取り消す場合。

適切なコマンドの選択

決定に役立つ簡単なガイドを以下に示します。

  • ファイルのステージ解除: git restore <file>またはgit reset <file>を使用します。
  • ファイルに対するワーキングディレクトリでのステージ未済みの変更の破棄: git restore <file>を使用します。
  • ファイルのすべての変更(ステージ済みおよびステージ未済み)の破棄: git restore --staged --worktree <file>を使用します。
  • 最後のコミットを取り消し、変更をステージングされたままにする(修正のため): git reset --soft HEAD^を使用します。
  • 最後のコミットを取り消し、変更をステージ未済みのままにする: git reset HEAD^を使用します。
  • 最後のコミットとすべての後続の変更を完全に破棄する(破壊的): git reset --hard HEAD^を使用します。
  • 共有ブランチ上のコミットを履歴を書き換えずに取り消す: git revert <commit-hash>を使用します。
  • リポジトリ全体のすべてのローカル変更を破棄する(破壊的): git reset --hardを使用します。

結論

git resetgit restoregit revertを使いこなすことは、効果的なGitの使用の基本です。git restoreは、ワーキングディレクトリとステージングエリアでの変更を安全に破棄するための頼りになるコマンドです。git resetは強力な履歴書き換え機能を提供し、ローカルでプッシュされていないコミットに使用するのが最適です。git revertは、特に共同作業環境で重要な、変更を取り消すための安全で履歴を維持する方法を提供します。それぞれの異なる動作を理解し、適切なコマンドを選択することで、プロジェクトの進化を自信を持って管理し、発生した間違いを修正することができます。