↩️

git reset/switch/restore/checkout の違いを完全理解する

に公開

Gitで「戻りたい」「戻したい」と思ったときに使うコマンドの挙動の違いを、表を用いて解説します。

TL;DR

  • git reset: ブランチを動かす。
  • git switch --detach: 作業対象のコミット(HEAD)を動かす。ブランチは動かない。
  • git restore: 作業中のファイル(作業ツリーインデックス)を復元する。ブランチもHEADも動かない。
  • git checkout: 非推奨。

checkout は switch/restore に置き換えられた

git switch および git restore は、git checkout の代替としてリリースされた新しいコマンドです。git checkout がブランチの切替(switch)とファイルの復元(restore)という2つの機能を持っていたため、分かりやすいように2つのコマンドに分離されました。

git checkout でできた操作は git switchgit restore でもできるため、これからGitを学ぶ方には git switchgit restore の利用がおすすめです

以下に主要な操作におけるコマンドの対応表を示します。

操作 switch/restore checkout
ブランチを切替 git switch <branch> git checkout <branch>
新規ブランチを作成して切替 git switch -c <branch> git checkout -b <branch>
特定コミットに切替 git switch --detach <commit> git checkout <commit>
ファイルの変更を破棄 git restore <file> git checkout -- <file>
特定コミットのファイルを復元 git restore -s <commit> <file> git checkout <commit> -- <file>

reset/switch/restore の違い

前提知識

各コマンドの挙動の説明にあたり、以下の概念を知っていることを前提とします。

  • 「作業ツリー(作業ディレクトリ)」は、手元のファイル領域のことです。
  • 「インデックス(ステージング領域)」は、git add したファイルの領域のことです。
  • 「ブランチ」は、あるコミットを指す参照(ポインター)のことです。
  • 「HEAD」は、現在作業中のブランチまたはコミットを指す参照(ポインター)のことです。コミットを指す場合は特にdatached HEADといいます。


git reset 時のイメージ図[1]

ブランチを過去のコミットに戻す

ブランチの先頭位置を特定のコミットに移動するには、git reset を使用します。ブランチの移動に合わせて作業ツリーとインデックスの内容も変えるかどうかは、オプションで指定します。

コマンドを実行したとき、作業ツリー、インデックス、ブランチ、HEADのどれが移動するのかを以下の表にまとめました。

コマンド 作業ツリー インデックス ブランチ HEAD
git reset --soft <commit> - - ↩️ ↩️
git reset <commit> - ↩️ ↩️ ↩️
git reset --hard <commit> ↩️ ↩️ ↩️ ↩️

「↩️」は、状態または参照先が変化することを表しています。具体的には以下のとおりです。

  • 作業ツリーとインデックスでは、領域内のファイルが指定したコミットのファイルの状態になることを表しています。
  • ブランチでは、ブランチの参照先が指定したコミットに移動することを表しています。
  • HEADでは、ブランチが移動する場合は、それに合わせてHEADの参照先コミットも結果的に移動することを表しています。ブランチが移動しない場合(後述)は、HEAD自体がdetached HEADとして指定したコミットを参照するようになることを表しています。

HEADを過去のコミットに戻す

HEAD位置をdetached HEADとして特定のコミットに移動するには、git switch --detach を使用します

コマンド 作業ツリー インデックス ブランチ HEAD
git switch --detach <commit> ↩️ ↩️ - ↩️
git checkout <commit> ↩️ ↩️ - ↩️

ファイルだけ過去のコミットに戻す

作業中のブランチやHEADはそのままで、特定のコミットのファイルを手元に復元するには git restore を使用します

コマンド 作業ツリー インデックス ブランチ HEAD
git restore -s <commit> . ↩️ - - -
git restore -s <commit> --staged . - ↩️ - -
git restore -s <commit> --staged --worktree . ↩️ ↩️ - -
git checkout <commit> -- . ↩️ ↩️ - -

-s <commit>--source=<commit> の省略表記です。

特定のファイルを指定する場合は、. をファイルパスに置き換えてください。

ファイルの作業中の変更を破棄する

ファイルのまだコミットしていない変更を破棄するには、以下のコマンドを使用します。

  • まだ git add していない変更を破棄するには、git restore . を使用します
  • git add を取り消す(アンステージする)には、git restore --staged . を使用します[2]

これらはそれぞれ、作業ツリーを戻す操作と、インデックスを戻す操作に当たります。

他の類似のコマンドも含めた挙動の詳細を以下の表に示します。

コマンド 作業ツリー インデックス
git restore . ↩️* -
git checkout -- . ↩️* -
git restore -s HEAD . ↩️ -
git restore --staged . - ↩️
git reset - ↩️
git restore --staged --worktree . ↩️ ↩️
git reset --hard ↩️ ↩️
git checkout HEAD -- . ↩️ ↩️

「↩️」はファイルがHEADの状態に変わることを表しています。「↩️*」はファイルがインデックスの状態に変わることを表しています。

余談ですが、git checkout -- . ではインデックスにも「↩️*」がある(インデックスがインデックスの状態になる、つまり何も変化しない)と考えると、前述の git checkout <commit> -- . との対応が見えやすいですね。

参考

脚注
  1. 図解 Git ↩︎

  2. ここで git reset ではなく git restore --staged . を挙げたのは、現在 git status を実行すると use "git restore --staged <file>..." to unstage と表示されるためです。 ↩︎

Discussion