手順

git checkout、switch、restore: 使い分けの方法

更新日: · Skillaria.Top

git checkout はブランチの切り替えとファイルの復元という2つの異なる操作を行います。そのため Git 2.23 以降は、ブランチは git switch、ファイルは git restore に役割が分かれています。ブランチの切り替えは git switch 名前、未コミットのファイル変更の取り消しは git restore ファイル を使い、checkout は古いスクリプトや慣れた手順のために残しておきます。

git checkout が2つの役割を持つ理由

git checkout は歴史的に、まったく異なる2つの操作を行います。ブランチ名を指定すると、HEAD を切り替えて作業ディレクトリを更新します。ファイルパスを指定すると、インデックスまたはコミットからそのファイルのバージョンを取り出し、保存していない変更を黙って上書きします。同じコマンドが、ある時は安全にブランチを移動し、別の時は保存していない作業を取り返しのつかない形で捨ててしまうのです。

この問題を解決するため、Git 2.23 で2つの専用コマンドが追加されました。ブランチには git switch、ファイルには git restore です。checkout が削除されたわけではなく、今でも完全に動作します。ただし、新しいコードやドキュメント、Git自身のヒントメッセージでは switch と restore が使われています。学び始めたなら、まずこれらから覚えてください。それでも checkout は手順書やスクリプト、面接でよく出てくるので、知っておく必要があります。

コマンドの対応表

作業git checkout新しいコマンド
ブランチに切り替えるgit checkout maingit switch main
ブランチを作成して移動するgit checkout -b featuregit switch -c feature
ブランチを作成または再設定するgit checkout -B featuregit switch -C feature
直前のブランチに戻るgit checkout -git switch -
コミットに移動する(detached HEAD)git checkout 3f2a1c9git switch --detach 3f2a1c9
ファイルの変更を取り消すgit checkout -- app.jsgit restore app.js
ファイルをインデックスから外すgit reset HEAD app.jsgit restore --staged app.js
別のブランチやコミットからファイルを取り出すgit checkout main -- app.jsgit restore --source=main --staged --worktree app.js
フォルダ内のすべての変更を取り消すgit checkout -- .git restore .

checkout のコマンドにある -- に注目してください。これは「この後にはパスが続く」という区切りです。ファイルとブランチの名前が同じ場合のあいまいさを防ぎます。restore はファイルだけを扱うので、この問題は起こりません。

ブランチに使う git checkout

git checkout feature/login        # 切り替え
git checkout -b feature/login     # 現在のコミットから作成して切り替え
git checkout -b hotfix origin/main   # リモートのブランチから作成
git checkout -t origin/feature/x  # リモートのブランチを追跡するローカルブランチ

ローカルにブランチがなく、サーバーに同じ名前のブランチがある場合、git checkout feature/x(git switch feature/x と同様)はローカルのブランチを作成し、追跡を設定します。ただし、その前に git fetch が必要です。そうしないと、Gitは新しいリモートブランチを知りません。

切り替えても、保存していない変更は失われません。競合しなければ、変更は移動先のブランチへ持ち越されます。競合する場合、Gitは操作を拒否し、コミットするか git stash で退避するよう提案します。-f(--force)を付けると、ローカルの変更を捨てて切り替えます。これは危険な操作です。

git switch: ブランチだけを扱う

switch はブランチに関する checkout の機能を受け継いでいますが、より厳密なルールで動作します。主な違いは、--detach を明示しない限り、コミットやタグに移動しないことです。git switch v1.0 は、知らないうちに detached HEAD へ移るのではなく、エラーを出します。そのため、うっかりブランチの外に出てしまうことが起こりにくくなります。

git switch main
git switch -c feature/cart
git switch -c feature/cart origin/main
git switch --detach v1.0
git switch -              # 直前のブランチ
git switch -m main        # 3方向マージで変更を持ち越す

git restore: ファイル用の取り消しボタン

restore はファイルを保存済みの状態へ戻し、コミット履歴には触れません。確実に使うには、Gitの3つの領域を理解しておく必要があります。作業ディレクトリ(エディタで見えているもの)、インデックス(git add で準備したもの)、コミットです。

git add の前にファイルの変更を取り消す

git restore app.js         # インデックスのバージョンに戻す
git restore src/           # フォルダ内のすべてのファイル
git restore .              # プロジェクト内で変更されたすべてのファイル

オプションなしの restore は、作業ディレクトリをインデックスの内容で復元します。最後のコミット以降にインデックスへ追加されていないファイルであれば、最後のコミットのバージョンと同じです。変更は跡形もなく消え、ゴミ箱にも残りません。そのため、git restore . の前には git diff で内容を確認してください。

git add の後でファイルをインデックスから外す

git restore --staged app.js

ファイルはコミット対象から外れますが、変更はファイルに残ります。これは最も安全な restore です。何も失われません。git status の「Changes to be committed」の見出しの下にあるヒントも、このコマンドを案内しています。

インデックスと作業ディレクトリの両方を取り消す

git restore --staged --worktree app.js
# 短い形式
git restore -SW app.js

別のコミットやブランチからファイルを戻す

git restore --source=HEAD~2 app.js          # 2つ前のコミットのバージョン
git restore --source=main config.yml        # ブランチ main のバージョン
git restore --source=3f2a1c9 -- docs/       # コミットからフォルダ全体

--source を指定した場合、既定では作業ディレクトリだけが変わります。変更をすぐにコミット対象にしたい場合は --staged を追加してください。古い書き方の git checkout main -- app.js は、常にインデックスと作業ディレクトリの両方を変更します。

削除したファイルを復元する

git restore deleted.txt                          # 削除済みだがまだコミットしていない
git restore --source=HEAD~1 deleted.txt          # 削除がすでにコミットされている

どのコミットでファイルを削除したか分からない場合は、git log --oneline -- deleted.txt で探します。一覧の一番上(最新)のコミットでファイルが削除されていて、その親から復元するなら git restore --source=<ハッシュ>~1 deleted.txt を使います。

restore、reset、revert の違い

どのコマンドも「取り消し」を行いますが、深さが異なります。

  • restore は作業ディレクトリとインデックスのファイルを扱い、履歴は変更しません。チームにとっても安全です。
  • reset はブランチのポインタを後ろに動かし、履歴を書き換えます。--soft は変更をインデックスに残し、--mixed(既定)は作業ディレクトリに残し、--hard は変更を削除します。共有ブランチでサーバーに送信済みのコミットに使うと危険です。
  • revert は、指定したコミットを打ち消す新しいコミットを作ります。履歴は書き換えられないので、すでにサーバーにあるコミットを取り消す正しい方法です。

簡単なルールがあります。まだコミットしていない変更は restore。ローカルだけにあるコミットは reset。チームメンバーがすでに持っているコミットは revert。

checkout の後の detached HEAD

git checkout 3f2a1c9 や git checkout v1.0 を実行すると、「You are in 'detached HEAD' state」という長い警告が表示されます。HEAD はブランチではなくコミットを直接指すようになります。古いバージョンを見たり、そのバージョンでテストを実行したりするには便利です。

この状態でもコミットはできますが、新しいコミットはどのブランチにも属しません。何か変更して残したい場合は次のコマンドを実行します。

git switch -c fix-for-v1

見ただけなら、git switch - または git switch main で戻ります。ブランチを作らずに離れてしまった場合でも、コミットは git reflog から見つけられます。ハッシュを確認し、git branch rescue <ハッシュ> を実行してください。

よくあるエラー

error: pathspec 'feature' did not match any file(s) known to git

この名前のブランチもファイルも見つかりません。たいていはブランチがサーバーにだけあり、fetch していないことが原因です。git fetch を実行してからもう一度試してください。綴りも確認してください。ブランチ名は大文字と小文字を区別します。

checkout -- や restore で変更を誤って捨ててしまった

インデックスに入っていなかった未コミットの変更は、Gitは保存していないため、Gitの機能では取り戻せません。エディタやIDEのローカル履歴が唯一の手段になることがあります。一方、git add で追加していたファイルは、オブジェクトとしてデータベースに残っており、git fsck --lost-found で見つけられる場合があります。

ファイルとブランチが同じ名前

ブランチ test とフォルダ test が両方ある場合、git checkout test はあいまいだという警告を出します。ブランチには git switch test、ファイルには git restore test(または git checkout -- test)を使ってください。

毎日の作業で使う場面

よく起こる場面と、それぞれに適したコマンドをまとめます。

  • ファイルを編集し始めたが、方向を間違えたと気づいた: git restore ファイル。
  • コミット前に余計なファイルをインデックスへ追加してしまった: git restore --staged ファイル。
  • 作業の途中で急いで別のタスクに移る必要がある: git stash を実行し、git switch ブランチ で移動します。戻ったら git switch - と git stash pop。
  • 統合せずに、隣のブランチから1つのファイルだけを取り入れたい: git restore --source=feature/x --staged --worktree パス。
  • 古いバージョンでバグが起きていたか確かめたい: git switch --detach v1.0 で確認し、その後 git switch -。
  • ファイル内の一部の変更だけを取り消したい: git restore -p ファイル。Gitが各部分を表示し、取り消すかどうか尋ねます。

対話モードの -p(--patch)は特に便利です。checkout と restore の両方にあり、デバッグ用の出力だけを捨てて、同じファイル内の有用な変更は残せます。

取り消す前に結果を確認する

パスを指定した restore や checkout は、確認なしに変更を捨てます。何が失われるかを先に確認しておきましょう。git diff は作業ディレクトリとインデックスの差、git diff --staged はコミット対象の内容、git diff main -- app.js は別のブランチとの差を表示します。迷ったら git stash で現在の状態を退避してください。stash からはいつでも変更を戻せますが、restore では戻せません。

まとめ

git checkout はブランチの切り替えとファイルの復元を兼ねており、そのため危険になりがちです。ブランチには git switch、ファイルには git restore を使いましょう。一方のコマンドは HEAD の位置だけを、もう一方はファイルの内容だけを変更します。ブランチ、restore、reset、revert の良い使い方と悪い使い方は、Gitコースで実践的に扱っています。

よくある質問

git checkout は非推奨になりましたか?

いいえ、非推奨にはなっておらず、今後も動作します。ただし日常の作業では、より範囲が狭く安全な git switch と git restore が推奨されています。

switch と restore はどのバージョンの Git から使えますか?

どちらも Git 2.23(2019年)で追加されました。バージョンは git --version で確認できます。

git restore は取り消せますか?

作業ディレクトリを復元した場合、捨てられた変更は Git が保存していないので取り消せません。git restore --staged は安全です。変更はファイルに残るので、git add で再度追加すればよいだけです。

同僚のブランチに切り替えるにはどうすればよいですか?

git fetch を実行し、その後 git switch ブランチ名 を実行します。Gitは origin/ブランチ名 に紐付いたローカルブランチを作成します。

git checkout . と git reset --hard の違いは何ですか?

git checkout .(または git restore .)は変更されたファイルをインデックスのバージョンに戻しますが、インデックスと履歴には触れません。git reset --hard はインデックスと作業ディレクトリの両方をコミットの状態に戻し、コミットを指定した場合はブランチも動かします。

動画でこのテーマを学ぶ

このテーマはコース「Git:初心者から自信を持って使える開発者へ」で実践とともに詳しく解説しています。

コースへ