リセット、チェックアウト、元に戻す | アトラシアン Git チュートリアル

リセット、チェックアウト、元に戻す

git resetgit checkout、および git revert コマンドは、Git ツールボックスで最も便利なツールです。これらはすべて、リポジトリ内の何らかの変更を元に戻します。また、git reset と git checkout を使用すると、コミットまたは個々のファイルを操作できます。

これらはよく似ているため、特定の開発シナリオでどのコマンドを使用すべきかが非常に混同しやすいといえます。この記事では、git resetgit checkout、および git revert の最も一般的な設定を比較します。これにより、皆さんが自信を持ってこれらのコマンドを自在に使用し、リポジトリを移動できるようになることを願っています。

Git の 3 つの領域

Git リポジトリの 3 つの状態管理メカニズムである作業ディレクトリ、ステージ済みスナップショット、およびコミット履歴に対する影響の観点から各コマンドについて考えると理解に役立ちます。これらのコンポーネントは「Git の 3 つの領域」と呼ばれることもあります。3 つの領域については、git resetページで詳しく説明しています。これらのメカニズムを念頭に置いてこの記事を読んでください。

チェックアウトとは、HEAD ref ポインターを指定されたコミットに移動する操作です。この操作を説明するために、以下のサンプルをご覧ください。

HEAD ref ポインターを指定したコミットに移動する

この例は、main ブランチの連続するコミットを表しています。現在、HEAD ref と main ブランチ ref はコミット d を指しています。今度は git checkout b を実行してみましょう

master ブランチでのコミットのシーケンス

これは、「コミット履歴」領域への更新です。git checkout コマンドは、コミットまたはファイル レベルのスコープで使用できます。ファイル レベルのチェックアウトでは、ファイルの内容が特定のコミットの内容に変更されます。

Revert とは、指定されたコミットの逆の操作を行い新しいコミットを作成する操作です。git revert はコミット レベルのスコープでのみ実行でき、ファイル レベルの機能はありません。

リセットとは、指定したコミットを取り上げ、"3 つの領域" をリセットして、指定したコミットのリポジトリの状態に合わせる操作です。リセットは、3 つの領域に対応する 3 つの異なるモードで起動できます。

チェックアウトとリセットは、通常、ローカルまたはプライベートの「取り消し」を行うために使用されます。リモートの共有リポジトリにプッシュする際に競合を引き起こす可能性があるリポジトリの履歴を変更します。元に戻す操作は、新しい履歴を作成してリモートで共有し、リモートのチーム メンバーが依存している履歴を上書きしないため、「パブリックな取り消し」を行うための安全な操作と見なされています。

Git Reset、Revert、Checkout のリファレンス

以下の表は、これらのすべてのコマンドに関する最も一般的な使用例をまとめたものです。Git の経験を積む間に少なくともこれらの一部は必然的に使用することになるので、このリファレンスを手近に置くようにしてください。

コマンド スコープ 一般的な使用例
Git のリセット コミットレベル プライベートブランチでコミットを破棄したり、コミットされていない変更を破棄したりします。
Git のリセット ファイルレベル ファイルのステージ取り消し
Git checkout コミットレベル ブランチの切り替えまたは古いスナップショットの検査
Git checkout ファイルレベル 作業ディレクトリ内の変更の破棄
Git revert コミットレベル パブリックブランチ内のコミットの取り消し
Git revert ファイルレベル (N/A)

コミット レベルの操作

スコープは、git resetgit checkout に渡すパラメーターによって決まります。ファイル パスをパラメーターとして含めない場合、コミット全体に作用します。このセクションでは、それについて説明します。git revert にはファイル レベルの機能はありません。

特定のコミットをリセットする

コミット レベルでは、リセットはブランチの先端を別のコミットに移動する方法です。これは、現在のブランチからコミットを削除するために使用できます。たとえば、次のコマンドは Hotfix ブランチを 2 つのコミット分後方に移動します。

git checkout hotfix git reset HEAD~2

Hotfix の最後にあった 2 つのコミットは、中ぶらりんの状態 (孤立したコミット) になりました。つまり、これらは Git が次回ガーベッジ コレクションを実行するときに削除されます。言い換えると、これらのコミットを破棄すると宣言していることになります。これは、次のように視覚化できます。

hotfix ブランチを HEAD~2 にリセットする

この git reset の使用法は、他の誰とも共有していない変更を元に戻す簡単な方法です。フィーチャーで作業を開始して、「しまった、何をやっているんだ。最初からやり直しだ」と思ったときに、頼りになるコマンドです。

現在のブランチを移動することに加えて、次のフラグのいずれかを git reset に渡してステージ済みスナップショットおよび作業ディレクトリ (またはそのいずれか) を変更することもできます。

  • --soft – ステージ済みスナップショットおよび作業ディレクトリは両方ともまったく変更されません。
  • —mixed — ステージ済みスナップショットは、指定したコミットに一致するように更新されますが、作業ディレクトリには影響しません。これが既定のオプションです。
  • --hard – ステージ済みスナップショットおよび作業ディレクトリは両方とも指定されたコミットに一致するように更新されます。

これらのモードが git reset 演算のスコープを定義していると考えると理解しやすくなります。詳細については、git reset ページを参照してください。

古いコミットをチェックアウトする

git checkout コマンドは、リポジトリの状態をプロジェクト履歴の特定の時点に更新するために使用されます。ブランチ名を指定して渡すと、ブランチ間を切り替えられます。

 git checkout hotfix

内部的には、上記のコマンドは HEAD を別のブランチに移動し、それに合わせて作業ディレクトリを更新するだけです。これはローカルの変更を上書きする可能性があるので、チェックアウト操作中に失われる作業ディレクトリ内の変更を強制的にコミットするか、スタッシュするよう求められます。git reset と違って、git checkout はブランチを移動させません。

HEAD を master から hotfix に移動する

ブランチの代わりにコミット参照を渡すことで、任意のコミットをチェックアウトすることもできます。これは、ブランチをチェックアウトするのとまったく同じです。HEAD 参照を指定されたコミットに移動します。たとえば、次のコマンドは現在のコミットの祖父母をチェックアウトします。

 git checkout HEAD~2
`HEAD` を任意のコミットに移動する

これは、古いバージョンのプロジェクトをすばやく調べるのに便利です。ただし、現在の HEAD への分岐参照がないため、分離した HEAD 状態になります。これは、別のブランチに切り替えた後にコミットに戻る方法がないため、新しいコミットの追加を開始すると危険です。このため、分離した HEAD にコミットを追加する前に、必ず新しいブランチを作成する必要があります。

Revert でパブリック コミットを元に戻す

Revert を行うと、新しいコミットが作成されてコミットが取り消されます。これは、コミット履歴が書き換わらないため、変更を元に戻す安全な方法です。たとえば、次のコマンドは、最後から 2 番目のコミットに含まれる変更を特定し、それらの変更を元に戻す新しいコミットを作成し、新しいコミットを既存のプロジェクトに追加します。

git checkout hotfix git revert HEAD~2

これは、次のように視覚化できます。

2 番目のコミットを最後のコミットに戻す

これを既存のコミット履歴を変更する git reset と対比してください。このため、git revert はパブリック ブランチに対する変更を元に戻すために使用し、git reset はプライベート ブランチに対する変更を元に戻すことだけに使用を制限すべきです。

git revertコミットされた変更を元に戻すためのツールで、git reset HEADコミットされていない変更を元に戻すためのツールと考えることもできます。

git checkout と同様に、git revert も作業ディレクトリ内のファイルを上書きする可能性があります。このため、ユーザーは、操作中に失われる変更をコミットまたはスタッシュするように求められます。

ファイルレベルのオペレーション

git reset および git checkout コマンドも、オプションのファイル パスをパラメーターとして受け入れます。これにより、これらのコマンドの挙動が劇的に変わります。操作がスナップショット全体に及ぶのではなく、1 つのファイルに限定されます。

特定のファイルを Git Reset する

ファイル パスを指定して呼び出されると、git reset はステージ済みスナップショットを更新して、指定したコミットのバージョンと一致させます。たとえば、次のコマンドは、最後から 2 番目のコミットの foo.py のバージョンをフェッチし、次のコミットのためにステージングします。

git reset HEAD~2 foo.py

git reset のコミットレベル バージョンと同様に、これは任意のコミットよりも HEAD で使用されるほうが一般的です。git reset HEAD foo.py を実行すると foo.py のステージングが解除されます。このファイルに含まれる変更は、作業ディレクトリにそのまま残ります。

ファイルをコミット履歴からステージ済みスナップショットに移動する

ステージ済みスナップショットは常に更新され、作業ディレクトリは決して更新されないため、--soft--mixed、および、--hard フラグはファイル レベル バージョンの git reset には影響しません。

ファイルを Git Checkout する

ファイルのチェックアウトは、ファイル パスを指定して git reset を使用する場合と似ていますが、ステージング エリアではなく作業ディレクトリが更新される点が異なります。このコマンドのコミット レベル バージョンとは異なり、HEAD 参照は移動しないため、ブランチは切り替わりません。

ファイルをコミット履歴から作業ディレクトリに移動する

たとえば、次のコマンドは作業ディレクトリの foo.py を最後から 2 番目のコミットからのバージョンと一致させます。

git checkout HEAD~2 foo.py

git checkout のコミット レベルの呼び出しと同様に、これはプロジェクトの古いバージョンを調べるために使用できますが、スコープは指定されたファイルに限定されます。

チェックアウトしたファイルをステージングしてコミットすると、そのファイルの古いバージョンに「戻す」効果があります。これは、ファイルに対する後続の変更内容をすべて削除することになるため注意が必要です。これに対して、git revert コマンドは指定されたコミットによって生じた変更のみを元に戻します。

git reset と同様に、これは通常 HEAD をコミット リファレンスとして使用します。たとえば、git checkout HEAD foo.py には、foo.py に対するステージングされていない変更を破棄する効果があります。これは git reset HEAD --hard と似た動作ですが、指定されたファイルに対してのみ動作します。

概要

これで皆さんは、Git リポジトリで変更を元に戻すために必要となるすべてのツールを獲得したことになります。git resetgit checkout、および git revert コマンドは混同しやすいですが、作業ディレクトリ、ステージ済みスナップショット、およびコミット履歴に対する影響について考えると目前の開発タスクに適したコマンドを識別することが容易になります。