ゆるおたノート

Tomorrow is another day.

【Git勉強中】あっ!そのコミット訂正させてください!

うっかりなあなたに。つまり私。

はじめに

基本の流れ

Git自体はじめての方は、まずはこちらからどうぞ。

凡例

記号・用語 意味 説明
$ コマンドライン」で使えるコマンド 通常、Gitを始めとする「シェルスクリプト」系の説明では、コマンドの文頭に$をつけて表現されることが多いようです。
当記事でもそれに倣って記載しています。

実際にコマンドを入力するときは、git~以下をコピペしてください。
# コメント 補足事項など。
(対話モードでは使えません、、、)
<> 編集点(という表現で良いのかな…?) 任意の値(名前、識別子など)を入力してください。
(コミットの)識別子 コミットごとに付与される半角英数字7桁の番号。 SHA-1というハッシュ値だそうです。
当記事では<コミット>と表記します。

▼コミットの取り消し

【A】直前のコミットを取り消す

[a]ブランチのコミット履歴のみ書き換える場合

git addしているインデックスと、作業ツリーはそのままにします。

$ git reset --soft <ブランチ名>
(例)
$ git reset --soft HEAD^
[b]ブランチのコミット履歴とインデックスを書き換える場合
(※デフォルト)

作業ツリーのみ残ります。

$ git reset --mixed <ブランチ名>

なお、git resetmixedモードがデフォルトなので、↑のコマンドは下記のようにも書けます。

$ git reset <ブランチ名>
[c]作業ツリーも含めてすべて書き換える場合

一つ前のコミット履歴を作成した直後の状態に完全に巻き戻します。
(それまでの変更も含めて)何もなかった状態になります。

$ git reset --hard <ブランチ名>

git reset --hard は、ブランチの歴史を強制的に書き換えてしまう強力なコマンドです。
もしマージコミットをpushしてないのであれば、強気で使ってしまってよいでしょう。
~中略~
もし保存していない変更が作業ツリーやインデックスにあれば、消し飛びますので、実行前にgit statusでなにか作業が残っていないか確認してみて下さい。
- git merge の取り消し方法。reset と revert コマンドについて。 | WWWクリエイターズ

【B】複数のコミットをまとめて取り消し

引数に<ブランチ名>ではなく<コミット>を指定すると、(現在のブランチの)特定のコミットの状態に戻すことが出来ます。

$ git reset --hard <コミット>

【C】間違えたコミットの打ち消し用のコミットを作成

[a]基本の使い方

新しくコミットを作成するのでブランチの過去履歴には影響を与えずに済み、pushのコンフリクトを防止します。

$ git revert
$ git revert <ブランチ名>

なお、mergeのコミットを打ち消す時は、注意が必要のようで…

  • 参考記事
    (まだ詳細読んでない…あとで読みます…たぶん…)
[b]コミットメッセージはそのままにする場合

オプションとして--no-editを付加。

$ git revert --no-edit <コミット>

【D】「取り消し」の取り消し

[a]基本の使い方
(1)「打ち消しコミット」も含めた履歴を確認する

git reflogで、HEADを含めたこれまでのコミット履歴(=「参照」の移動遍歴)を見ることができます。
「reflog」は「reference log」の略ですかね…?

ここで「コミットの巻き戻し先」を確認します。

$ git reflog

すると、ターミナルでコミット履歴が出力されます。
1番上が最新で、何か行う毎に記録されていることが分かります。

※私の環境では、このようになりました。

3e7182e (HEAD -> develop, master) HEAD@{0}: checkout: moving from master to develop
3e7182e (HEAD -> develop, master) HEAD@{1}: commit: fix directory description and change heading positions.
ba3303d HEAD@{2}: commit: fix the css property table.
c14f55a (origin/master, origin/HEAD) HEAD@{3}: commit: add Table of Contents.
3ef84f7 HEAD@{4}: checkout: moving from master to master
3ef84f7 HEAD@{5}: commit: re-edit directories.
6d75462 HEAD@{6}: commit: change indent from tabs to spaces.
909e2a9 HEAD@{7}: commit: edit filename.
df8920d HEAD@{8}: commit (merge): move to several directories.
d616027 HEAD@{9}: clone: from https://github.com/yuricks7/xxxxxxxx
  • 意味

    [識別子] (HEAD -> ソース, 行き先) [HEADから●番目]: [アクション]: [コメントor概要]

(2)指定のコミットに巻き戻す

ブランチに残っているコミット履歴を、先ほど確認した巻き戻し地点の状態に戻します。

git resetに、ブランチ名ではなくHEADの番号を指定すると「●個前のHEADの状態」に戻すことが出来ます。

$ git reset --hard HEAD@{●●}
(例)

ここまでをまとめると、下記のような流れになります。

# 入力
$ git reflog
 
# 結果
3e7182e (HEAD -> develop, master) HEAD@{0}: checkout: moving from master to develop
3e7182e (HEAD -> develop, master) HEAD@{1}: commit: fix directory description and change heading positions.
ba3303d HEAD@{2}: commit: fix the css property table.
c14f55a (origin/master, origin/HEAD) HEAD@{3}: commit: add Table of Contents.
3ef84f7 HEAD@{4}: checkout: moving from master to master
3ef84f7 HEAD@{5}: commit: re-edit directories. # ← ここに戻したいとき
6d75462 HEAD@{6}: commit: change indent from tabs to spaces.
909e2a9 HEAD@{7}: commit: edit filename.
df8920d HEAD@{8}: commit (merge): move to several directories.
d616027 HEAD@{9}: clone: from https://github.com/yuricks7/xxxxxxxx
 
# HEAD@{5}の時点に強制巻き戻し
$ git reset --hard HEAD@{5}

▼コミットの修正

【A】直前のコミットを修正して上書き

[a]基本の使い方

直前の変更を破棄して、新しいコミットが作成されます。

$ git commit -a
$ git commit --amend

コミットの識別子も変更されることに注意です!

[注意]「push済みのコミット」とコンフリクトしないように注意!

修正前の内容で一度リモートにpush済みの場合、その後にコミット内容を修正するとリモート側とコンフリクトが発生してしまいます。
つまり、そのままでは再pushはできません。めんどくさい…

どうしても修正後の内容で記録したい場合は、再pushの際に-fオプションをつけて新しい内容で強制的に上書きが必要です。

# 強制的に<ブランチ名>へPushする
$ git push -f origin <ブランチ名>
$ git push --force origin <ブランチ名>

ただし、チームで開発しているときなど、複数人が参照しているブランチ(master等)ではさらにコンフリクトが発生する原因になるため、この方法は避けた方が良いようです。

【B】コミットをまとめる

[a]基本の使い方

現在のブランチで、それまでのコミットを<コミット>まで1つにまとめます。

$ git rebase -i <コミット>
$ git rebase --interactive <コミット>

詳しい手順の解説は、こちらの記事が参考になると思います。

[注意]「push先リポジトリ内の履歴」とコンフリクトしないように注意!

「rebase前の履歴」を既にリモートへpush済みの場合、「rebase後の履歴」とコンフリクトしてしまいます。
つまり、これもそのままでは再pushはできません。めんどくさい…

後者の適用で問題なければ、下記のコマンドで強制的にリモート側の履歴を書き換えます。

# 強制的に<ブランチ名>へPushする
$ git push -f origin <ブランチ名>
$ git push --force origin <ブランチ名>

ただし、前述のように複数人が参照しているブランチではこの方法は避けた方が良いようです。

このシリーズについて

Gitのコマンドについて、少しずつ記事にまとめています。

誤り、分かりづらい等ありましたら、ぜひコメント欄Twitterお問い合わせフォーム等でご教示ください!

次回

連載目次

  1. 【Git勉強中】操作に慣れてきたので、流れを整理してみました。 - ゆるおたノート
  2. 【Git勉強中】ブランチの移動を使い分けたい - ゆるおたノート
  3. 【Git勉強中】ステージに上げたり下げたり。 - ゆるおたノート
  4. 【Git勉強中】ステージした変更を記録する - ゆるおたノート
  5. 当記事【Git勉強中】あっ!そのコミット訂正させてください! - ゆるおたノート
  6. 【Git勉強中】自分のコミット履歴を提出する - ゆるおたノート
  7. 【Git勉強中】リポジトリの作成と接続 - ゆるおたノート

参照

  • 巻き戻しの違いについて、図解入りで説明されているので分かりやすいです。