git revert, reset, commit --amend, rebase (git LT vol.3)

社内 LT に使ったものを少し改編しています
主に git 初級者の方 (私もですが) に向けた内容だと思います
Qiita スライドモードでご覧頂ければ幸いです

---

# 過去の commit を修正したい！時

---

## 目次

以下の 4 つをよく使っています

- 1. `git revert`
- 2. `git reset`
- 3. `git commit --amend`
- 4. `git rebase`

---

### 勝手なイメージ

- 1. `git revert` -> 安全!
- 2. `git reset` -> 手軽!
- 3. `git commit --amend` -> commit log 修正でよく使う!
- 4. `git rebase` 本格派!

**重要**
1 以外は commit の歴史の改ざんのため
既に push した commit には基本的には使いません

---

## 1. git revert

過去の commit の打ち消しの commit を新たに作る

---

### 1. git revert

下記のように A, B, C, と commit して、直近の C を取り消したい場合・・

A -> B -> C

---

### 1. git revert

`$ git revert HEAD`
(HEAD は最新の commit を示す)

A -> B -> C -> D

D = C の打ち消し commit
commit は進みましたが、状態は B と同じ

---

### 1. git revert

**ポイント**
新しく修正 commit を作る = 修正 commit が歴史に残る

---

### 1. git revert

GitHub や Azure DevOps で PR を merge した後にも
revert ボタンが出てきますね

A -> B
↓ PR を merge
A -> B -> C
↓ revert ボタンを押す
A -> B -> C -> D

C は merge されなかったことに

---

### 1. git revert 使いどころ

- 既に push してしまった commit を commit ごと打ち消したい時

master に merge
-> 障害発生
-> とりあえず戻す！
な感じでしょうかね～

---

ちなみに
`$ git revert HEAD`は
`$ git revert @`でもいけます
(@ は HEAD のエイリアス)

---

## 2. git reset

過去の commit を無かったことにする

---

### 2. git reset

下記のように A, B, C, と commit して、直近の C を取り消したい場合・・

A -> B -> C

---

### 2. git reset

`$ git reset HEAD~1`
(HEAD から 1 つ前の commit の状態へ)

A -> B

C を消し去ります
ただし C の変更分は手元 (ワークツリー) に残ります

---

### 2. git reset --hard

ハードな奴もあります

`$ git reset --hard HEAD~1`
(HEAD から 1 つ前の commit の状態へ)

A -> B

C を葬り去ります
C の変更分は手元 (ワークツリー) には残りません

---

### 2. git reset 使いどころ

- push されていない直近の commit を修正したい時
  - 必要なファイルが commit されていなかった
  - 不要なファイルを commit してしまった
  - typo したのを発見したけど新しく commit したくないな～
とか？

---

### 2. git reset --hard 使いどころ

- commit の連鎖が壊れてしまってどうにもならない時に、壊れる前の歴史まで遡る
- merge や conflict で訳わからなくなった時に、いったん作業中のものを stash (退避) してとりあえず git pull できるようにしたい時
とか？

---

## 3. git commit --amend

直近の commit を改ざんする

---

### 3. git commit --amend

下記のように A, B, C, と commit して、C の commit log の typo を直したい場合・・

A 1st commit log
B 2nd commit log
C 3nd commit log
↑ 3rd!

---

### 3. git commit --amend

`$ git commit --amend`
と打つと commit log 入力画面 (vim 等) になるので 3rd commit に書き換え、保存して終了 (:wq)

A 1st commit log
B 2nd commit log
C 3rd commit log

---

### 3. git commit --amend

また、不要なファイルを commit してしまった時にも使えます

git rm で不要なファイルを削除し、
`$ git rm unnecessary.txt`
git commit --amend
`$ git commit --amend`
と打つと commit log 入力画面 (vim 等) になるので 3rd commit に書き換え、保存して終了 (:wq)

---

### 3. git commit --amend 使いどころ

- 直近の commit や commit log を手軽に修正したい時

---

## 4. git rebase

過去の commit を自由自在に改編 (何でもできる!)

---

### 4. git rebase

用途は多岐に渡りますが
私がよく使うのが commit をまとめて commit log を綺麗にする時 (後で自分も何を変更したのか追いやすい!)

---

### 4. git rebase

例えば下記のように A, B, C, D と commit して、B, C, D を 1 つの commit にまとめたいなあという時

A -> B -> C -> D

---

### 4. git rebase

A -> B -> C -> D

歴史を B まで 3 つ遡ります
`$ git rebase -i HEAD~3`

---

### 4. git rebase

何やら編集モードに入ります

~~~
  1 pick 922348a A commit log
  2 pick 3b03490 B commit log
  3 pick a2e6bff C commit log
  4
  5 # Rebase fa4e3e0..a2e6bff onto fa4e3e0 (3 commands)
  6 #
  7 # Commands:
  8 # p, pick = use commit
  9 # r, reword = use commit, but edit the commit message
 10 # e, edit = use commit, but stop for amending
 11 # s, squash = use commit, but meld into previous commit
 12 # f, fixup = like "squash", but discard this commit's log message
 13 # x, exec = run command (the rest of the line) using shell
 14 # d, drop = remove commit
 15 #
 16 # These lines can be re-ordered; they are executed from top to bottom.
 17 #
 18 # If you remove a line here THAT COMMIT WILL BE LOST.
 19 #
 20 # However, if you remove everything, the rebase will be aborted.
 21 #
 22 # Note that empty commits are commented out
~~~

---

### 4. git rebase

B と C の commit log の前方を pick から s (squash) に変更して保存して終了します

~~~
  1 pick 922348a A commit log
  2 s 3b03490 B commit log
  3 s a2e6bff C commit log
  4
  5 # Rebase fa4e3e0..a2e6bff onto fa4e3e0 (3 commands)
  6 #
  7 # Commands:
  8 # p, pick = use commit
  9 # r, reword = use commit, but edit the commit message
 10 # e, edit = use commit, but stop for amending
 11 # s, squash = use commit, but meld into previous commit
 12 # f, fixup = like "squash", but discard this commit's log message
 13 # x, exec = run command (the rest of the line) using shell
 14 # d, drop = remove commit
 15 #
 16 # These lines can be re-ordered; they are executed from top to bottom.
 17 #
 18 # If you remove a line here THAT COMMIT WILL BE LOST.
 19 #
 20 # However, if you remove everything, the rebase will be aborted.
 21 #
 22 # Note that empty commits are commented out
~~~

---

### 4. git rebase

続いて commit log 編集モードになります
このままで良ければ保存して終了します

~~~
    1 # This is a combination of 3 commits.
    2 # This is the 1st commit message:
    3 
    4 A commit log
    5 
    6 # This is the commit message #2:
    7 
    8 B commit log
    9 
   10 # This is the commit message #3:
   11 
   12 C commit log
   13 
   14 # Please enter the commit message for your changes. Lines starting
   15 # with '#' will be ignored, and an empty message aborts the commit.
   16 #
   17 # Date:      Wed Dec 5 10:10:14 2018 +0900
   18 #
(以下略)
~~~

---

### 4. git rebase

A -> B -> C -> D
が
A -> E
となります

E = B, C, D を含む

---

### 4. git rebase

git rebase は奥が深いので詳しくはまたの機会に！

---

commit log を綺麗に保って自分も他の開発者にも優しいリポジトリを作って行きたいですね！ :smile:

---

ご清聴ありがとうございました！