gitで間違えてファイルをコミットしたのを取り消したい

「ディレクトリ構造を変更したのに.gitignoreを修正し忘れて、うっかり含む必要のないファイルをコミットに含めてしまった」ということは稀によくあることだと思います。 そこで該当コミットをrebaseを使って修正する、という手段に出ることに。 まずはコミットの履歴を探します。
$ git log --oneline
e22657b (HEAD -> master, origin/master, origin/HEAD) 亀と鯉を追加
318a16e 拝殿の広さを仮定
3952e24 おおよその間取りを追加
65c24ea 博麗神社の間取りfirst commit
今回は3952e24を修正したい、というケースとします。この場合、一つ前の65c24earebaseで指定します。
$ git rebase -i 65c24ea
続いて、修正したい3952e24に対してpickeditに変更
## 変更前

pick 3952e24 おおよその間取りを追加
pick 318a16e 拝殿の広さを仮定
pick e22657b 亀と鯉を追加

# Rebase 65c24ea..e22657b onto 65c24ea (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
## 変更後

edit 3952e24 おおよその間取りを追加
pick 318a16e 拝殿の広さを仮定
pick e22657b 亀と鯉を追加

# Rebase 65c24ea..e22657b onto 65c24ea (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
ちなみに、git bash使用の場合エディタは普通のvimっぽいのでxで削除してiで編集モードに入って編集し、:wqで保存。
$ git rebase -i 65c24ea
Stopped at 3952e24...  おおよその間取りを追加
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue
するとrebaseモードに入るので、通常のgitと同様にgit rmgit addして行きます。
$ git rm hoge/path/img.jpg
rm 'hoge/path/img.jpg'

## 略
手元で.gitignoreを編集して無視対象を追加し、git addします。
$ git add .gitignore
そうしたら祈ります(amend)。
$ git commit --amend
[detached HEAD 569dd35] おおよその間取りを追加

## 略
最後に--continueHEADに戻ります。
$ git rebase --continue
Successfully rebased and updated refs/heads/master.
これで修正完了です。

備考: 作業の取り消し

途中で何が何やら分からなくなってしまったら--abortオプションでrebaseの作業を取り消すことができます。
$ git rebase --abort

備考2: プッシュ時の注意

rebase後にプッシュする際、コミットのIDが変わってしまうた
め通常のプッシュだとリモートリポジトリにrejectされます。 そのため、git push -fで強制プッシュすると良い模様。 ちなみに今回はここで操作を誤ってしまいツリーが汚くなってしまったので、次回以降気を付けたいです……。

参考

rebaseのやり方

rebaseの後のプッシュ

rebaseについての解説

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。