見やすいPRのためにcommitを整理しよう


# はじめに

今日はgitのコミットログについて書きます。この記事を読んでいる人はgit操作にはなれてきている人かと思います。PRを出すときにコミットログのつくり方の工夫はされていますか？

僕は「コミットを細かくして確認する範囲を小さくする。」くらいしかわかっていませんでした。

先日仕事で注意していただいたためログの整理についてしらべました。

コードレビューをしてもらうようになると相手の立場や気持ち考えなければいけません。

少しでも負担を減らすためにコミットログを整理することができるrebaseについて学習しました。設定した内容やコマンドについて書きます。この記事ではコミットメッセージはこういっ他内容を書きましょうといったことは触れていません。

## こんな人むけです

- githubを使用した開発はだいたい理解できる。
- mergeはわかるがrebaseには自信がない。

# 環境

```bash
# git version
$ git --version
git version 2.33.1

#mac version
$ sw_vers
ProductName:	macOS
ProductVersion:	12.0.1
BuildVersion:	21A559

```

# コミットログをまとめるには？

コミットログをまとめたい場面について考えます。

細かく刻んだコミット`master1`, `master2` をまとめて`rebase1`というコミットを作成したいと思います。そのままプッシュしてしまうとレビュワーはコミットを２件確認することになります。

内容が全く異なるコミットであればそのままにしておくべきですが、同一機能の編集を２つのコミットに分ける必要はないと考える場合です。

```bash
$ git log
* 71d5b2d 2022-02-12  (HEAD -> master) master2
* 26be359 2022-02-12  master1
* 0295646 2022-02-12  first commit

```

現状はこのような状況になっています。コミットをまとめるには`git rebase -i`で実現する事ができます。

コマンドを叩くと操作方法のヘルプと同時に現在コミットしている `master1`, `mster2`が出力されます。（引数でコミットログを指定することでさかのぼる場所を指定することもできます。）

⚠一度プッシュしてしまったコミットをまとめることは混乱を招きやすく、推奨されていないようです。ローカルのコミットをリベースしてからプッシュする使用方法が理想だと思います。

```bash
$ git rebase -i

pick 26be359 master1
pick 71d5b2d master2

# Rebase c495810..71d5b2d onto c495810 (2 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 [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# 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
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.

```

今回の処理ではまとめて`rebase1`というコミットを作りたいため、２件目のコミットを`squash`に変更します。master1はそのままにしておきます。そうすることで`master1`に`master2`を圧縮するような動きになります。vimで開いている場合は保存して終了してください。

```
pick 26be359 master1
squash 71d5b2d master2

```

終了するとコミットメッセージをどうするかメニューが出てきます。

自分が作成したいコミットメッセージに変更するといいでしょう。

```bash
pick 26be359 master1
# This is a combination of 2 commits.
# This is the 1st commit message:

master1

# This is the commit message #2:

master2

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sat Feb 12 21:38:50 2022 +0900
#
# interactive rebase in progress; onto c495810
# Last commands done (2 commands done):
#    pick 26be359 master1
#    squash 71d5b2d master2
# No commands remaining.
# You are currently rebasing branch 'master' on 'c495810'.
#
# Changes to be committed:
#▸------new file:   sample.txt

```

`rebase1`に変更したいため`rebase1`とだけ書いて保存します。

```bash
pick 26be359 master1
# This is a combination of 2 commits.
# This is the 1st commit message:
# This is the commit message #2:

rebase1

```

終了したあと成功のメッセージがでると完了です。

```bash
[detached HEAD 1e94a07] rebase1
 Date: Sat Feb 12 21:38:50 2022 +0900
 1 file changed, 7 insertions(+)
 create mode 100644 sample.txt
Successfully rebased and updated refs/heads/master.

$ git log
* 1e94a07 2022-02-12 (HEAD -> master) rebase1
* 0295646 2022-02-12 first commit

```

## うまく行かないとき

どこか操作を間違えてしまうとメッセージでヘルプも出してくれます。

応急処置のコマンド例です。

```bash
# 失敗しているないようのあるrebaseをもう一度開くことができます。
$ git rebase --edit-todo

# 失敗したrebaseをキャンセルすることができます。
$ git rebase --abort
```

## rebase -iでの設定追加

```bash
$ git config --global rebase.autosquash true
```

この設定をgitconfigに追加しておくことでpickのところがもともとsquashとなった状態で出力されます。細かいですが、もともとsquashになってくれている方が編集箇所が少なくなったので自分が好きでした。

# 自分が切ったブランチで開発中に誰かのPRがマージされたとき

チーム開発をしていると自分のPRをみてもらうまえに別のブランチがマージされる事があります。そういったときは変更点を`pull`してから自分が開発しているブランチに変更点を反映させておく必要があります。運が良ければコンフリクトにならないこともありますが、余計な操作は回避しておくべきです。そんなときも`rebase`を使用します。

```bash
$ git branch --contains  # 現在のブランチはhogeです
hoge
$ git stash              # hogeで編集していた内容をスタッシュ
$ git checkout master    # masterブランチに移動
$ git pull               # マージされたPRの変更点を反映
$ git checkout hoge      # hogeブランチに戻る
$ git rebase master      # hogeにmasterのコミットを反映
$ git stash pop          # stashの内容を適用
```

以上の内容が`rebase`するときの一例です。

このときの`stash`が自分は面倒で良くエラーを出してしまいます。

 今回リベースについて調べていると便利な設定がありました。

```bash
$ git config --global rebase.autostash true
```

この設定を追加しておくとrebaseしたときのstash popまでが自動になるという便利な設定です。aliasでもオプションに含められるのでどちらでも好みでいいと思います。

# rebaseするときに別のブランチのコミットを使いたい。

ブランチfugaで作業していましたが、作成したコミットをhogeのブランチに移動して作り直したいとします。そんなときに使用するのが`cherry-pick`です。cherry-pickはコミットをかいつまんで移動させることができます。実際にやってみました。

現状の確認です。

```bash
$ git branch --contains
fuga
$ git log
* 4d3c42d 2022-02-12   (HEAD -> fuga) fuga
* 1e94a07 2022-02-12   (master, hoge) rebase1
* 0295646 2022-02-12   first commit
```

```bash
$ git branch --contains
hoge
$ git log
* ef41c70 2022-02-12  (HEAD -> hoge) hoge
* 1e94a07 2022-02-12  (master) rebase1
* 0295646 2022-02-12   first commit
```

今回は hogeで新しく作業したいです。

fugaで作業していたコミットをhogeに移動させてfugahogeという名前でsquashしたいと思います。

```bash
$ git branch --contains
hoge
$ git cherry-pick 4d3c42d
# 同じ内容のコミットを作ってくれます。
# 全く別のコミットとして作られるためIDはかわります。
$ git log
* fea2ac7 2022-02-12   (HEAD -> hoge) fuga
* cec14b3 2022-02-12   hoge
* 1e94a07 2022-02-12   (master) rebase1
* 0295646 2022-02-12   first commit

 # rebase -iを使用してコミットをまとめます。
$ git rebse -i
$ git log
* a17559e 2022-02-12   (HEAD -> hoge) fugahoge
* 1e94a07 2022-02-12   (master) rebase1
* 0295646 2022-02-12   first commit
```

IDを指定することで特定のオミットの変更内容を移動させて新しブランチに移行することができました。コミットごとに移動できるのがcherry-pickのいいところだと思います。

ただcherry-pickはうつとながいのと個人的にrが一つ抜けてタイポしやすいのでalias登録しておきました。

```bash
$ git config --global --add alias.cp cherry-pick
```

以上rebaseについて調べた内容でした。

公式のリファレンスを読んで学習してみましたが、読んだだけでは実際使えないということは何度も経験しているため今回は記事にして自分なりにまとめました。これでrebaseについて質問が来ても答えられるきがしました。

今回使用していないオプションもたくさんありましたが、今のところは i オプションだけで対応できるかと思います。

今は先輩エンジニアの真似をしてエイリアス登録もすすめているので、github内の`gitconfig`を参考にしています。いいものが見つかればそんな記事も書いていきたいと思います。ありがとうございました。

# 参考
[git rebaseが捗るオプション紹介 - madokaのブログ](https://xmadoka.hatenablog.com/entry/2019/05/09/003825)

[Git コマンドリファレンス（日本語版）](https://tracpath.com/docs/git-rebase/)