ズボラな僕らだからこそ「正しそうで正しくない少し正しいgitの使い方」

本日のいまさら記事はこちらです。

# この記事の対象読者

* [x] commit messageは `wip` `fix` が9割を超える
* [x] 「宇宙人」と書いて「マメな人」と読む
* [x] 夏休みの宿題を9/1までに終える人は未来人である
* [x] 「毎日1分頑張るだけ」などと言える超能力者がいるらしい

> ただの人間にしか興味ありません。
この中に宇宙人、未来人、異世界人、超能力者がいたら、そっと記事を「いいね」して閉じなさい。以上！

## コミットの正しい運用って？

色んな世界線の人が参考になる記事を書いています。

http://qiita.com/kozyty@github/items/87fa95a236b6142f7c10
http://qiita.com/itosho/items/9565c6ad2ffc24c09364
http://qiita.com/risacan/items/f4cbabc62b684ab9296d
http://qiita.com/crifff/items/1abf08bca4ce51db4775

などなど。

正しい世界の人はこれらの記事を参考にしましょう。

# 正しいコミットを常に意識しろ、なんて面倒くさい。ならどうすればいい？

ズボラなコミットメッセージ、適当なプルリクエスト、`File Changes(243)` は嫌われます。
チームメンバーのMPを削っても、良いことはありません。

![adv2016-12-02-01.png](https://qiita-image-store.s3.amazonaws.com/0/31053/a00c7f4f-f5ed-800b-d943-d018bde8be10.png)

ですから、次の事**だけ**は気を付けましょう。

## みんなで作業するブランチは汚さない

* 自分だけのブランチで作業して、できあがったらPR！PR！！PR！！！
* マージするときはGitHubの[Squash and Merge](https://github.com/blog/2141-squash-your-commits)を使おう

## 人の目に触れるもの（≒プルリクエスト）はキレイにする

* プルリクエストの description で何を目的とした変更なのか明確に説明する
    * ポエムにしない。
    * **せめて**新機能の追加なのか、バグフィクスなのか、リファクタリングなのかだけでも書く。
* どこを重点的に見てほしいのか、小さな単位で説明する
    * もちろん「小さい変更・小さいPR」が良いけど、~~面倒くさい~~諸事情により大きくなることもある。
    * **「●●ってファイルの✕✕行目、△△のためにこうしたけど、□□かもしれんので見て欲しい」**

# 「●●ファイルの✕✕行目」って・・・もっとなんか良いのあるだろ！

はい。そこまで来て初めて、「コミットは小さくしよう、メッセージは適切に」という言葉が身にしみ得るんです。最近実感しました。

とはいえ、「あれやってる最中にここが気になってこっちも直した」は普通の人ならよくあることです。
実装中には**面倒くさくて**、「あ、コレ違う、別のブランチ切ろう、あの時点に戻ってここからブランチ切って・・・」なんて、**面倒くさくて**やってられません。**面倒くさくて**。

例えばSlackに投稿する前に、間違いがないか厳密に推敲するでしょうか？しませんよね。
とりあえずEnterキーをターン！で、後からEditすりゃいいですよね。あとで見えなきゃいいんです。

## 「あとでキレイにすればいいや」。

そもそもVersion管理を**とても乱暴に**言ってしまえば、「あとで何があったか見返すためのもの」「あとで過去の編集を改めるためのもの」です。
ですから、コミットなんて最初は適当でいいんです。
実装中は直したいところを直して、やりたい放題コミットしまくりましょう。

**ただし、自分用の作業ブランチで。**他人様に迷惑をかけることだけはやめましょう。

## こんなふうに出来たらいいな

> ●●の機能を追加するため、…
> 
> * ●●の実装: <font color="#337ab7">9xd4941</font>
> * ●●のテスト: <font color="#337ab7">0979xd2</font>
> * ついでにやった✕✕のリファクタリング: <font color="#337ab7">337xab7</font>
> * 自動生成コード部分: <font color="#337ab7">6exd364</font>

見やすいですね。もっとキレイにすることはできるでしょうが…

http://qiita.com/umanoda/items/93aec41213f8e3ce14c8

# 「あとでキレイにする」ための色々

## ユースケースごとに便利な git コマンド

では実際、一度コミットしてしまった内容をキレイにするために、ユースケース別に使えるgitコマンドを少しばかり。

### 過去のコミットをまとめたい・並び替えたい

さて、貴方の目前には作業中に作った大量のwipコミットがあります。

```shell-session
$ git branch

  master
* wip-branch

$ git log

commit 035a324a7e9e03822be202536a89d3bd27451c95
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 08:15:42 2016 +0900

    wip まだゴミあった

commit d387cf8dd21b297d9b8c427db8b7007fe9dee108
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 07:57:33 2016 +0900

    wip バグ直し

commit 101f652a9dd51a29269fa818b91d2fac8a2e3d0c
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 07:51:02 2016 +0900

    wip できた？

commit 59be7f5930f6d3fe7d3cda89ddb1dd252d170b77
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 06:53:42 2016 +0900

    wop ごみ掃除

commit df735e2e2b31df94d73ad134fa02692996e3ad9d
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 05:09:27 2016 +0900

    functions 再整理 (#42)
```

`df735e2` (functions 再整理 (#42)) より後のコミットがすべてwipのようです。
さほど複雑な状況にはなっていません。ただ、「ゴミ掃除」コミットが２個あったり、「出来た？」と「バグ直し」のように作業の途中経過がコミットされていたりします。
本来は「ゴミ掃除」で１コミット、「◯◯機能の実装」で1コミット、というのが望ましいところです。

そこで、`git rebase -i` でコミットを並び替えて、いくつか一つにまとめてしまいます。

```shell-session
$ git rebase -i df735e2e2b31df94d73ad134fa02692996e3ad9d # ← "functions 再整理 (#42)" のコミットハッシュ

pick 59be7f5 wop ごみ掃除
pick 101f652 wip できた？
pick d387cf8 wip バグ直し
pick 035a324 wip まだゴミあった

# Rebase 326fc9f..0d4a808 onto d286baa
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
```

エディタ（デフォルトではvim）が開いて、説明通りに編集すれば、

* 並び替える
* １つにまとめる
* コミットメッセージを変える
* コミット内容を修正する

といったコミットの操作を行えます。
ここでは、以下のようにいじってみます。

```shell-session
reword 59be7f5 wop ごみ掃除
squash 035a324 wip まだゴミあった
reword 101f652 wip できた？
squash d387cf8 wip バグ直し
```

結果、git logは次のようになりました。

```
commit 4641774bde2c85eaa3ce905e1e6d9b4e1cebf60a
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 07:51:02 2016 +0900

    ◯◯の実装

commit 398acba7019ca7f7d915b55711c9ac53d7b22d3e
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 06:53:42 2016 +0900

    リファクタリング：使用していない✕✕パッケージを削除

commit df735e2e2b31df94d73ad134fa02692996e3ad9d
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 05:09:27 2016 +0900

    functions 再整理 (#42)
```

めでたしめでたし。

`git rebase -i` による編集の詳細は、別の記事でまとめている人がたくさんいらっしゃいます。

http://qiita.com/takke/items/3400b55becfd72769214

### 過去のコミットをやりなおしたい

さて、貴方の目前にはまたしても大量の wip コミットがあります。

```shell-session
$ git log

commit eb8b87a08098fc2fd7df9805970dd197ab850447
Author: Kyoichiro Yamada <yamada@wacul.co.jp>
Date:   Tue Sep 27 21:52:57 2016 +0900

    fix

commit 61a476ba015556e167a46c3e29fcada60c510e9b
Author: Kyoichiro Yamada <yamada@wacul.co.jp>
Date:   Tue Sep 27 21:48:03 2016 +0900

    fix

commit c2df90e8bc50446b736249ecaaac89f2c4c4dc9e
Author: Kyoichiro Yamada <yamada@wacul.co.jp>
Date:   Tue Sep 27 21:44:01 2016 +0900

    fix

commit 37d070c710f4df384686a27a99b9a2e9dcf84610
Author: Kyoichiro Yamada <yamada@wacul.co.jp>
Date:   Tue Sep 27 21:41:06 2016 +0900

    fix

commit b3293ccd4420938941843aa70d4b85d5ac4f3acc
Author: Kyoichiro Yamada <yamada@wacul.co.jp>
Date:   Tue Sep 27 21:40:45 2016 +0900

    fix

commit 490e1c0c46b67594aee31e38fcb28076ed6c1fdb
Author: Kyoichiro Yamada <yamada@wacul.co.jp>
Date:   Sun Sep 25 16:35:56 2016 +0900

    fix

commit df735e2e2b31df94d73ad134fa02692996e3ad9d
Author: kyoh86 <yamada@wacul.co.jp>
Date:   Tue Nov 22 05:09:27 2016 +0900

    ◯◯機能の実装 (#43)
```

さて、rebaseしますか・・と思ったものの、今度は**どのコミットで何をしたか、メッセージは何も語りかけてはくれません**。
過去の自分をぶん殴りたいですか？**ぶん殴りましょう。**

![adv2016-12-02-02.png](https://qiita-image-store.s3.amazonaws.com/0/31053/1e0ae2d5-6329-c8b3-0324-e90706a73d34.png)

`git reset` で、巻き戻りたい過去のコミットを指定します。
この目的では**間違っても `--hard` は使わないよう**に気を付けましょう。

```shell-session
$ git reset df735e2e2b31df94d73ad134fa02692996e3ad9d # ← "◯◯機能の実装 (#43)" のコミットハッシュ
```

これで、過去の自分の愚かなコミットたちはなかったことになりました。
ただし、`--hard` オプションは使用していないので、**ファイルの変更は生き残ったままです**。
あとは正しい commit を生産するだけです。

```shell-session
$ git add foo.go
$ git add foo_test.go
$ git commit -m '△△機能の実装'
$
$ git rm bar.go
$ git rm -r baz/
$ git commit -m '□□機能の廃止'
```

### 同じファイルの中に、違う目的の変更が混じってしまった！

前述の `git reset` が要るようなケースではママあることですが、
**単一のファイル内に**違う目的の変更が混じってしまった場合、どうすれば良いんでしょう。

`git add` には、 `-p` というオプションがあります。`partial` の `p` でしょうね。
実行すると、対象ファイルのdiffが表示され、必要な変更だけを選択して `add` することができます。

詳細はこちらを参考にしましょう。

http://qiita.com/crifff/items/1abf08bca4ce51db4775

### ブランチ名にwipって入ってるんだよね…

**誰かが自分の作業ブランチから新しくブランチを切ってしまう** という悲劇を少しでも避けるため、
作業ブランチ等には`wip-xxxxx`のようなそれと分かる名前を付けることもあります。（というか付けたほうが良い）

ですが、いざPRを出す段になって、ブランチ名が`wip-xxxxx` では締まりません。
「もうコレは特定の新しい要件を満たしたブランチだよ」ということを示すためにも、名前を変えてしまいましょう。

```shell-session
$ git branch -M feature-xxx
$ git push -u origin feature-xxx

# 作業ブランチを既にリモートにプッシュしていた場合、リモートのゴミも残さずキレイにしましょう。
$ git push origin :wip-xxxxx 
```

これらのコマンドを使いこなせると、作業中は気持ちよくコミットし、
終わった後の「キレイにする」際もミス少なく、気楽に挑むことが出来ます。

## もっと気持ちよく作業するために

### tig

`git log` や、 `git add -p` は便利ですが、随所で痒いところに手が届きません。
[tig](https://github.com/jonas/tig)を使うことで、そのあたりで楽をすることができます。

詳細はこちらの記事を参考にして下さい。

http://qiita.com/suino/items/b0dae7e00bd7165f79ea

また、この記事では取り上げられていませんが、「tigの画面で選択したコミットのハッシュをコピーする」keybindを設定すると、
特定のコミットに遡ったり、rebaseしたりがとても楽になります。

```~/.tigrc
# main viewの左端にコミットIDを表示する
set main-view = id:width=12 date author commit-title:graph=yes,refs=yes
# デフォルト
# set main-view = date author commit-title:graph=yes,refs=yes

# 水平分割したウィンドウの下画面サイズを % で指定（行数指定も可）
set split-view-height = 80%

# Shift-Cで、選択行のコミットのハッシュをクリップボードにコピー（macOS用）
bind main C !@git pbcopy %(commit)
```

### 作業用ブランチを猛烈に切り替える

自分用の作業用ブランチは、用途ごとに作りまくります。
ですから、どれが何のためのブランチか、忘れがちです。

`fzf`や`peco`を使いこなしましょう。https://github.com/junegunn/fzf/wiki/examples#git を参考にしてもいいですし、
コレのためだけの拙作、ブランチリスト出す君[git-branches](https://github.com/kyoh86/git-branches) を使ってもいいかもしれません。

```function/checkout-git-branch.zsh
function checkout-git-branch() {
  git-branches --color --exclude-current \
    | fzf \
    | awk '{print $2}' \
    | xargs -r git checkout
}
autoload -Uz checkout-git-branch
bindkey '^xgb' checkout-git-branch
bindkey '^xg^b' checkout-git-branch
bindkey '^x^gb' checkout-git-branch
bindkey '^x^g^b' checkout-git-branch
```


# 終わりに

いかがだったでしょうか。
几帳面な人や、真面目な人が読んだら卒倒するか、罵倒の声が挙がりそうな記事ですね。
マメにコミットしたり、丁寧にメッセージ書いたりを継続的にできない・・・僕のような人たちの、お役に立てれば幸いです。

間違いなく言えることは、「正しくコミット運用できるに越したことはありません」。
チームメンバーに厳しい人がいたり、マメな人がいるのであれば、あえて道を踏み外すようなことはせず、みんなに歩調を合わせて「正しい運用」ができるよう、心がけましょう（自戒）。