背景:なぜ git checkout は「問題がある」のか?
Git を長く使っていれば、必ず git checkout をさまざまな場面で使ったことがあるはずです。しかし実は、それぞれのケースがロジック的にまったく異なる操作だということに気づいていないかもしれません:
# ケース1:別のブランチに切り替える
git checkout main
# ケース2:新しいブランチを作成して切り替える
git checkout -b feature/login
# ケース3:ステージされていない変更をファイル単位で取り消す
git checkout -- src/auth.js
# ケース4:特定のコミットにチェックアウトする(HEAD デタッチ)
git checkout a1b2c3d
1つのコマンドで、まったく異なる4つの動作を行います。ケース3と4は構文上は似て見えますが、結果はまるで正反対です。チームのジュニアエンジニアが git checkout -- . のつもりで git checkout HEAD~1 と打ってしまい、detached HEAD 状態になっているのに気づかないという場面を実際に見たことがあります。
Git 2.23(2019年8月)では、責務を明確に分離するために git switch と git restore が正式に導入されました:
git switch— ブランチの切り替えだけを行うgit restore— ファイルを以前の状態に戻すだけを行う
各コマンドはただ1つのことだけを行います。Git 初心者とベテランが混在するチームでは、この明確さが説明にかかる時間をかなり削減してくれます。
セットアップ:まず Git のバージョンを確認する
この2つのコマンドには少なくとも Git 2.23(2019年8月リリース)が必要です。まず確認しましょう:
git --version
# git version 2.43.0
バージョンが 2.23 未満の場合は、アップデートが必要です:
# Ubuntu/Debian
sudo add-apt-repository ppa:git-core/ppa
sudo apt update && sudo apt install git
# macOS (Homebrew)
brew upgrade git
# Arch Linux
sudo pacman -S git
# Windows (winget)
winget upgrade --id Git.Git
アップデート後、再度確認してください:
git --version
# 2.23 以上であること
詳細設定:git switch と git restore の使い方
1. git switch — 安全なブランチ切り替え
基本的に、git switch は git checkout のブランチ切り替え部分だけを行います — 余分な動作は一切ありません:
# 既存のブランチに切り替える
git switch main
git switch develop
# 新しいブランチを作成して切り替える(checkout -b の代替)
git switch -c feature/user-auth
# 特定の起点からブランチを作成する
git switch -c hotfix/payment-bug origin/main
# 直前のブランチに戻る(cd - と同じ感覚)
git switch -
大きなメリットの1つは、存在しないブランチ名を入力した場合、git switch は意図しない操作を行う代わりに明確なエラーを表示することです:
git switch non-existent-branch
# fatal: invalid reference: non-existent-branch
HEAD のデタッチについては — --detach フラグを明示的に指定しなければならないため、うっかり発生させることはなくなりました:
git switch --detach a1b2c3d
# HEAD is now at a1b2c3d...
2. git restore — 明確なコントロールでファイルを復元する
日常の実務では、git restore をより頻繁に使います — ファイルを誤ってステージしてしまうことは、思っている以上によくあることです。
ステージされていない変更を取り消す(作業ディレクトリ):
# 特定のファイルの変更を取り消す
git restore src/auth.js
# ステージされていないすべての変更を取り消す
git restore .
誤って add したファイルをアンステージする:
# ファイルをアンステージする
git restore --staged src/config.js
# すべてをアンステージする
git restore --staged .
重要な注意点:--staged はファイルをステージングエリアから外すだけです。ファイルの変更内容はそのまま残ります — コミット対象としてマークされなくなるだけです。
特定のコミットまたはブランチの状態にファイルを復元する:
# 別のブランチからファイルを取得する(そのブランチにチェックアウトせずに)
git restore --source=feature/new-api src/api.js
# 3コミット前の状態にファイルを戻す
git restore --source=HEAD~3 src/database.js
# 特定のコミットからファイルを取得する
git restore --source=a1b2c3d src/utils.js
--source は、別のブランチやコミットからファイルの特定バージョンを取得したいときに使います — そのブランチにチェックアウトする必要はなく、現在のブランチに影響を与えません。
3. --staged と --worktree の組み合わせ
デフォルトでは git restore は作業ツリーにのみ作用します。ステージされた内容と作業ツリーの両方を同時に HEAD にリセットしたい場合:
# ステージと作業ツリーの両方を HEAD に復元する
git restore --staged --worktree src/auth.js
# 同等:git checkout HEAD -- src/auth.js(旧来の方法)
4. 早見表:旧コマンド vs 新コマンド
# === ブランチの切り替え ===
git checkout main # 旧
git switch main # 新
# === 新しいブランチの作成 ===
git checkout -b feature/x # 旧
git switch -c feature/x # 新
# === ステージされていない変更の取り消し ===
git checkout -- src/auth.js # 旧
git restore src/auth.js # 新
# === ファイルのアンステージ ===
git reset HEAD src/config.js # 旧
git restore --staged src/config.js # 新
# === 別のコミットからファイルを取得 ===
git checkout a1b2c3d -- src/utils.js # 旧
git restore --source=a1b2c3d src/utils.js # 新
チームへの導入:スムーズな移行方法
エイリアスを設定して段階的に移行する
8人のチームに展開したとき、誰もすぐに習慣を変えたがりませんでした。私が使った方法は:両方の書き方が使えるようにエイリアスをセットアップしつつ — 社内ドキュメントには新しいコマンドだけを記載するというものです:
# ~/.gitconfig に追加する
[alias]
sw = switch
swc = switch -c
rs = restore
rss = restore --staged
PR テンプレートとコミットガイドには新しいコマンドのみを記載します。追加のトレーニングやアナウンスは不要でした — 約2ヶ月後には、誰かに促されることなく、チーム全員が自然に移行していました。
restore 後の確認
復元後は git status でその場確認しましょう:
# 変更を取り消した後に確認する
git restore src/auth.js
git status
# 「Changes not staged for commit」に src/auth.js が表示されなくなっている
# アンステージ後に確認する
git restore --staged src/config.js
git status
# src/config.js が「Changes to be committed」から「Changes not staged」に移動している
# 実際の変更内容を確認する
git diff HEAD src/auth.js
よくある混乱ポイント
移行時によく聞かれるポイントをいくつか:
- コミットされていない変更があるとスイッチできない — 変更が上書きされる可能性がある場合、Git はエラーを表示します。先に
git stashを使うか、git switch --discard-changesで変更を破棄してから切り替えましょう(ただしこれは慎重に)。 git restoreは追跡されていないファイルを削除できない — 一度もgit addされていない新規ファイルにはgit clean -fdを使いましょう。- リモートトラッキング:
git switch -c feature origin/featureはアップストリームのトラッキングを自動でセットアップします。
# コミットされていない変更を破棄してブランチを切り替える
git switch --discard-changes develop
# 追跡されていないファイルとディレクトリを削除する
git clean -fd
# リモートのブランチからトラッキング付きでローカルブランチを作成する
git switch -c feature/api-v2 origin/feature/api-v2
このワークフローに移行して2ヶ月後、チームメンバーが誤って HEAD をデタッチしたり、コマンドを間違えて変更を失うという件数が明らかに減りました。みんなが慎重になったからではなく — 新しい構文が単純にそうしたミスをうっかり起こせないようになっているのです。さらに、PR の説明やコミットメッセージに書かれた git コマンドも格段に読みやすくなりました。
git checkout は今後も使えますし、削除されることもありません。しかし、新しいプロジェクトを始めるときや、新メンバーをオンボードするときは、最初から git switch と git restore を使いましょう。覚えることが少なく、罠が少なく、そして「detached HEAD state とは何か」を説明しなくて済むようになります。
