Git 履歴をすっきり整理！「git rebase -i」を活用したコミットの統合・修正・メッセージ編集

Git を使って開発を進めていると、細かいコミットが積み重なって履歴が見づらくなったり、後からコミットメッセージを修正したくなったりすることがあります。そんなときに便利なのが **`git rebase -i`**（インタラクティブリベース）です。ちゃんと計画どおりに実装を進めていたように履歴を構成し直すことができます！

## **1. リベースとは？**

**リベース（rebase）** とは、**Gitの履歴を整理するための機能** で、特定のブランチの変更を別のブランチに適用し直すために使われます。一般的には、メインブランチの変更を開発ブランチに適用したり、複数のブランチを統合する際に使用されます。

当記事では、そのようなブランチ間の調整方法ではなく、**自分の作業ブランチ内でコミット履歴を整理する用途** にリベースを活用する方法について説明します。

例えば、以下のような場合にリベースを使うと便利です。

- **コミットメッセージの誤字・脱字を修正したい（よくある）**
- **複数の小さなコミットをまとめたい（粒度が大きいタスクだとよくある）**
- **コミットの順番を整理したい**
- **不要なコミットを削除したい（よくある）**
- **改修内容自体を再編集したい**

**分かりやすく整理された状態** にしてからプルリクを送信すると、レビュアーの負担を減らすことができます。

## **2. `git rebase -i` を実行すると何が起きるのか？**

`git rebase -i` コマンドを実行すると、ターミナル上に **“編集用の一覧”** が表示されます。
この画面は、実際にリベースが始まる「前」のステップです。

🧠 **イメージ：コマンドが「バッチファイルの下書き」を開く**

- `rebase -i` を実行 → **「どのように履歴を書き換えるか？」の編集画面が表示される**
- 編集を保存・閉じた時点で → **実際のリベース処理がスタートする**

この編集画面では、対象コミットに対して `pick` / `reword` / `edit` などのコマンドを指定できます。行の順番を変えたり、不要な行を削除したりすることで、**履歴の整理方法を自由に設計**できます。

```
pick abc123 初期実装
reword def456 メッセージ修正
fixup ghi789 不要な修正を統合
```

上記のように編集し保存すれば、そのとおりに Git が履歴を書き換えていきます。

この時点ではまだ履歴は変わっておらず、**編集画面で指定した指示通りに処理が走るのは保存後**です。上記の例だと `reword def456 メッセージ修正` のタイミングで手動での保存を求められます。

```mermaid
graph TD
  A[git rebase -i HEAD~5 実行] --> B[編集画面でコミット操作を指定]
  B --> C[保存・終了]
  C --> D[Gitが指示通りに履歴を書き換え]
```

✅ **ポイント：**
- 編集画面は **「リベースの手順書」** のようなもの
- 保存・終了すると、Git がその順番に従って履歴再構築を開始する

具体的には、以下のようなことが可能です：
- **コミットメッセージを修正（`reword`）**
- **コミットの順番を変更**
- **コミットを統合（`squash` / `fixup`）**
- **不要なコミットを削除（`drop`）**
- **過去のコミットの改修内容を修正（`edit`）**

これにより、履歴をすっきり整理し、分かりやすい状態にすることができます。

## **3. `git rebase -i` の基本操作**
まず、履歴を編集したい範囲を指定して `git rebase -i` を実行します。

```sh:sh
git rebase -i HEAD~5
```
:::note info
`HEAD~5` は **「現在のHEADから5つ前までのコミット」** を対象とする、という意味です。
:::

直近5つのコミットが一覧表示され、編集できるようになります。

```:viモードで編集
pick abc123 コミット1
pick def456 コミット2
pick ghi789 コミット3
pick jkl012 コミット4
pick mno345 コミット5
```
行頭のコマンドを書き換えることで、バッチのイメージで順番にリベース処理（コミット編集）が行われます。

※この画面はviモードで開きます。操作がよく分からない場合は `esc`を押してから `q! + enter` で強制的に抜けることができます。詳しくは後述します。 

```sh
Successfully rebased and updated refs/heads/main.
```
リベースが完了すると、このようなメッセージが表示されます。


## **4. コミットメッセージを修正する（`reword`）**
**過去のコミットメッセージを修正** したい場合は、対象の `pick` を **`reword`** に変更します。

```:viモードで編集
pick abc123 コミット1
pick def456 コミット2
reword ghi789 修正が必要なコミット
pick jkl012 コミット4
pick mno345 コミット5
```

`reword` に変更してエディターを保存・閉じると、対象のコミットのメッセージ編集画面が表示されるので、そこで内容を自由に書き換えられます。

### **📌 `reword` のポイント**
- **コミットメッセージを修正したいときに `reword` を使う**
- **直前のコミットだけなら `git commit --amend` でも修正可能**

## **5. コミットの順番を変更する**
`git rebase -i` のリストで **行の順番を入れ替えるだけ** で、コミットの順序を変更できます。

```:viモードで編集
pick abc123 コミット1
pick ghi789 コミット3  ← 3番目を2番目に移動
pick def456 コミット2
pick jkl012 コミット4
pick mno345 コミット5
```
➡ **この状態でリベースを実行すると、新しい順番で履歴が整理されます。**

順序に依存するコミットの場合はエラーになりますが、コードを書いている本人が分かることと思うので、うまくいかない時はabort（※後述）して検討し直してください。

## **6. コミットを統合する（`squash` / `fixup`）**
複数のコミットを統合したい場合は、`squash` または `fixup` を使います。

「3番目 (`ghi789`) を基準に、5番目 (`mno345`) を統合する」

```:viモードで編集
pick abc123 コミット1
pick def456 コミット2
pick ghi789 コミット3  ← これを基準にする
fixup mno345 コミット5  ← 3番目に統合
pick jkl012 コミット4
```

### **📌 `squash` / `fixup` の違い**
| 方法 | 動作 |
|------|------|
| `squash` | 直前のコミットに統合し、メッセージも編集可能 |
| `fixup` | 直前のコミットに統合し、メッセージはそのまま適用 |

## **7. 不要なコミットを削除する（`drop`）**
履歴から完全に削除したいコミットがある場合は、`drop` を使います。

```:viモードで編集
pick abc123 コミット1
pick def456 コミット2
drop ghi789 コミット3  ← これを削除
pick jkl012 コミット4
pick mno345 コミット5
```

該当行を削除しても同様に動作します。

```:viモードで編集
pick abc123 コミット1
pick def456 コミット2
pick jkl012 コミット4
pick mno345 コミット5
```


### **📌 `drop` のポイント**
- **コミットが完全に削除される**
- **dropを使わず該当行を削除してもよい**
- **`git reflog` で復元可能**
- **リモートに `push` 済みの場合は影響があるため、慎重に使用**

## **8. 過去のコミットの内容を修正する（`edit`）**

**改修内容自体を変更** したい場合は、対象の `pick` を `edit` に変更します。

```:viモードで編集
pick abc123 コミット1  
pick def456 コミット2  
edit ghi789 修正が必要なコミット  
pick jkl012 コミット4  
pick mno345 コミット5
```

この状態で `git rebase -i` を開始すると、Git は `ghi789` のコミットで一時停止します。

その後、修正を加えて再コミットし、リベースを続行します。

```sh
# ファイルを編集する
vi 修正したいファイル  # エディタは任意（VSCodeなどでもOK）

# ステージング
git add 修正したいファイル

# コミットを上書き
git commit --amend

# リベースを続行
git rebase --continue
```

### **🕒 コミット日時を変更したい場合**

内容を変更せずに日時だけを修正したい場合は、以下のように `--date` オプションを使います。

```sh
# メッセージ・内容をそのままにして日時だけを変更
GIT_COMMITTER_DATE="2025-03-20T07:00:00" \
git commit --amend --no-edit --date "2025-03-20T07:00:00"
```

- `--no-edit`: メッセージを変更せずに適用  
- ISO 8601 形式（例：`2025-03-20T07:00:00`）で指定  
- 内容が変わっていない場合でも `--allow-empty` を付ければコミット可能

### **📌 `edit` のポイント**

- **改修内容を後から変更できる**
- **日時だけを変更することも可能（`--date` オプション）**
- **`git commit --amend` で上書きコミット**
- **`git rebase --continue` でリベース続行**


## **9. 履歴編集後の `push`**
リベースによって履歴が書き換えられた場合、リモートに既に `push` していると通常の `push` ではエラーになります。  
履歴を書き換えたあとは、リモートリポジトリに強制的に反映させるために、`git push --force` を使います。

```sh:sh
git push --force
```

:::note warn
※強制プッシュは他の開発者に影響を与える可能性があるため、共有ブランチでは使用前に確認しましょう。
:::

### **📌 補足：コミット日時を編集する**
`git commit --amend` に `--date` オプションを追加すると、**コミット日時を変更することもできます。**

```sh:sh
git commit --amend --no-edit --date "2025-03-20T07:00:00"
```

- `--no-edit` を使えばメッセージはそのままで、日時だけを変更可能
- ISO 8601形式の日時で指定（例：2025年3月20日 午前7時）

これは、**履歴の見た目を整えるために有効なテクニック**です。

## **10. リベースを取り消す方法**

リベース中に「やっぱりやめたい！」と思った場合は、以下を実行すると元の状態に戻ります。

```sh:sh
git rebase --abort
```

```sh:表示されるメッセージ
.git/rebase-apply directory removed. Rebase aborted.
```

## **11. リベース完了後に元に戻す（`git reflog` + `git reset --hard`）**
リベース完了後に「やっぱり戻したい！」場合は、`git reflog` と `git reset --hard` を使います。

```sh:sh
git reflog
```

:::note info
`git reflog` は、Git が記録している操作履歴を参照できるコマンドで、 **「どの状態からどこに移動したか」** の履歴を一覧で確認できます。
:::

```sh:出力例
HEAD@{3}: rebase (start): checkout HEAD~5
```

```sh:sh
git reset --hard HEAD@{3}
```
リベース前の状態に戻ります。

## **12. インタラクティブリベースでの編集には ****`vi`**** の基本操作が必要**

`git rebase -i` を使う際には、Git がデフォルトのエディタとして `vi` を使用します。多少分かる人向けに基本的な操作方法を列挙します。

### **🔹 ****`vi`**** の基本操作**

- **編集モードに入る**: `i`（インサートモード）
- **1行切り取る（削除）**: `dd`
- **切り取った行を貼り付ける**: `p`（カーソルの後に貼り付け）または `P`（カーソルの前に貼り付け）
- **操作を元に戻す**: `u`
- **変更を確定して終了する**: `ESC` → `:wq` → `ENTER`
- **変更を破棄して終了する**: `ESC` → `:q!` → `ENTER`

### viは難しい？？？

`vi` に慣れない場合は、一般的なスクリーンエディタである `nano` に変更することもできます。

```sh:sh
git config --global core.editor nano
```

これで、インタラクティブリベース時に `nano` を使うように設定できます。

## **13. トラブルシューティング早見表**

| 現象・エラー | 対処法 |
|--------------|--------|
| `vi` から抜けられない | `ESC` → `:wq` → `ENTER`（保存して終了）<br>`ESC` → `:q!` → `ENTER`（保存せず終了） |
| リベース中に「Conflict」が出る | `git status` で対象確認 → 該当ファイルを修正 → `git add` → `git rebase --continue` |
| リベース中にやり直したい | `git rebase --abort` |
| リベース後に戻したくなった | `git reflog` → `git reset --hard HEAD@{n}` で復元 |
| `rebase --continue` で進まない | `git add` で変更をステージ → 再度 `git rebase --continue` |
| 編集中に意図しないファイルを変更してしまった | `git restore` や `git reset` で取り消す（状況による） |

### 💡 補足：操作を誤ったときの鉄則
- **慌てずに `git status` で状況確認**
- **まず `git log` や `git reflog` で履歴を確認**
- **最悪の場合でも `reflog` からほぼ必ず復元可能**

## **14. まとめ**

### **📌 ****`git rebase -i`**** でできること**

✅ **コミットメッセージを修正（****`reword`****）**
✅ **過去のコミットの内容を修正（****`edit`****）**
✅ **不要なコミットを削除（****`drop`****）**
✅ **コミットを統合（****`squash`**** / ****`fixup`****）**
✅ **コミットの順番を変更**
✅ **コミット日時を変更（`--date` オプション）**

### **📌 ****`vi`**** の基本操作も理解しておくと便利**

- `vi` を使いこなせるとスムーズにリベースできる
- `nano` をエディタとして設定することも可能

**`git rebase -i`** は、Git 履歴を綺麗に整え、レビューしやすい形にするための強力なツールです。コミットメッセージの修正や統合、不要な履歴の削除まで、細かく制御できます。

インタラクティブリベースを使いこなして、誰にとっても読みやすく、レビューしやすい Git 履歴を目指しましょう！