別ブランチや過去コミットから「このファイルだけ欲しい」ってとき、checkout でブランチ切り替えたり cherry-pick でコミット丸ごと取り込んだりしてないか。
git restore --source ならファイル単位でピンポイントに持ってこられる。
ワーキングツリー / インデックス / HEAD
restore の挙動を理解するには Git の3つの場所を区別する必要がある。
| 場所 | 別名 | 何が入ってる |
|---|---|---|
| ワーキングツリー | 作業ディレクトリ | 今エディタで開いてるファイルそのもの |
| インデックス | ステージングエリア |
git add した内容。次のコミットの下書き |
| HEAD | リポジトリ | 最新コミットの中身 |
[ワーキングツリー] --git add--> [インデックス] --git commit--> [HEAD]
git status で「Changes to be committed」に出るのがインデックス、「Changes not staged for commit」がワーキングツリーとインデックスの差分。
restore はこの3つのどれを書き換えるかを --worktree / --staged で指定する。
基本の使い方
# 別ブランチの特定ファイルを今のワーキングツリーに持ってくる
git restore --source=feature/new-api -- src/api/client.ts
# 3コミット前のファイル内容に戻す
git restore --source=HEAD~3 -- src/config.ts
# 特定コミットから複数ファイル
git restore --source=abc1234 -- "src/**/*.ts"
持ってきた後は未ステージの変更として見える。
要らなかったら git restore src/api/client.ts で破棄するだけ。
--source なしだと意味が逆になる話
ここが混乱ポイント。
| コマンド | 動き |
|---|---|
git restore <path> |
インデックスの内容でワーキングツリーを上書き(=未ステージの変更を破棄) |
git restore --source=<commit> <path> |
指定コミットの内容でワーキングツリーを上書き(=任意地点から取得) |
つまり --source を付けないと「変更を捨てる」コマンド、付けると「持ってくる」コマンドになる。名前は同じ restore なのに用途がほぼ逆。最初は必ず --source の有無を意識する。
--staged だけを付けると、ワーキングツリーは今のまま・インデックスだけ指定コミットの内容になる。コミット直前の状態をピンポイントで作りたいときに便利。
checkout / cherry-pick との比較
| やりたいこと | git restore --source |
git checkout <commit> -- <path> |
git cherry-pick |
|---|---|---|---|
| ファイル単位で取得 | ○ | ○ | × (コミット単位) |
| ブランチ切り替え | なし | なし | なし |
| コミット作成 | しない | しない | する |
| インデックス操作の明示 |
--staged / --worktree で選択 |
デフォルトで両方 | N/A |
| 推奨度 | 現行 | 古い書き方 | 用途が違う |
checkout のファイル指定版は今も動くが、Git 2.23 以降は restore / switch に役割分離されたので新しく書くなら restore 一択。
使いどころのイメージ
「feature ブランチの client.ts 実装 コミットからファイルだけ欲しい」という状況。
git restore --source=feature/new-api -- src/api/client.ts 一発で済む。
実例 レビューで指摘された古い実装を一時的に戻す
$ git log --oneline src/auth.ts
e4f2a1b refactor: JWT検証をミドルウェアに分離
a7b3c9d feat: 認証フロー実装
$ git restore --source=a7b3c9d -- src/auth.ts
$ git diff --stat
src/auth.ts | 42 ++++++++++++++++++++++++------------------
1 file changed, 24 insertions(+), 18 deletions(-)
差分を見ながら「この部分だけ戻そう」と判断できる。確認が終わったら元に戻す。
git restore --source=HEAD -- src/auth.ts
stash からファイル1つだけ取り出す
地味に知られてないが stash も --source に渡せる。
git restore --source=stash@{0} -- src/experimental.ts
git stash pop で全部展開してコンフリクトと格闘するより安全。