はじめに
プッシュ済みのコミットから、一部のファイルだけ変更を取り消したい場面があります。
git revert はコミット全体を打ち消すのでファイル単位の指定ができません。かといって git reset で履歴を書き換えるのは、他の人と共有しているブランチでは避けたいところです。
こんなとき便利なのが git checkout <commit>^ -- <file> です。
結論
git checkout <コミットハッシュ>^ -- <戻したいファイルのパス>
git commit -m "revert some files"
git push
これだけです。
仕組み
<コミットハッシュ>^ は「そのコミットの一つ前」を意味します。つまり「変更が入る前の状態」です。そのファイルを作業ツリーに持ってきて、新しいコミットとしてプッシュします。
履歴を書き換えず、新しいコミットを積むだけなので、共同作業中のブランチでも安全に使えます。
使い方
1. 戻したい変更が入ったコミットを特定
git log --oneline
出力例:
ebd381c 最新の変更
2339119 機能追加
ef29017 ← このコミットの変更を戻したい
164a2a1 develop マージ
2. そのコミットで何が変更されたか確認
git show --stat ef29017
ファイル一覧と変更行数が表示されます。
3. 戻したいファイルを指定して checkout
git checkout ef29017^ -- src/utils/helper.ts
複数ファイルやディレクトリもまとめて指定できます。
git checkout ef29017^ -- src/utils/ src/hooks/useAuth.ts
4. 確認してコミット&プッシュ
git status
git diff --staged
git commit -m "revert some files from ef29017"
git push
似たコマンドとの違い
| コマンド | 用途 | ファイル指定 | 履歴書き換え |
|---|---|---|---|
git revert <commit> |
コミット全体を打ち消す | できない | しない |
git reset <commit> |
HEAD を過去に移動 | ステージのみ可 | する |
git checkout <commit>^ -- <file> |
特定ファイルだけ過去の状態に | できる | しない |
注意点
^ を忘れない
^ が無いと、そのコミット自体の状態になってしまい、逆に戻したい変更が適用されます。
# NG: ef29017 の状態(戻したい変更が入った状態)になる
git checkout ef29017 -- src/utils/helper.ts
# OK: ef29017 の一つ前(変更前)の状態になる
git checkout ef29017^ -- src/utils/helper.ts
間のコミットで変更されている場合
戻したいコミットより後のコミットでも同じファイルが変更されていると、その変更も消えてしまいます。先に確認するのが安全です。
git log --oneline ef29017^..HEAD -- src/utils/helper.ts
何も表示されなければ、間のコミットで触られていないので安心です。