git restore と git switch を使いこなす:モダンな Git で git checkout を置き換える

Git tutorial - IT technology blog
Git tutorial - IT technology blog

背景:なぜ 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 switchgit 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 switchgit restore の使い方

1. git switch — 安全なブランチ切り替え

基本的に、git switchgit 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 switchgit restore を使いましょう。覚えることが少なく、罠が少なく、そして「detached HEAD state とは何か」を説明しなくて済むようになります。

Share: