ズボラな僕らだからこそ「正しそうで正しくない少し正しいgitの使い方」
本日のいまさら記事はこちらです。
この記事の対象読者
-
commit messageは
wip
fix
が9割を超える - 「宇宙人」と書いて「マメな人」と読む
- 夏休みの宿題を9/1までに終える人は未来人である
- 「毎日1分頑張るだけ」などと言える超能力者がいるらしい
ただの人間にしか興味ありません。
この中に宇宙人、未来人、異世界人、超能力者がいたら、そっと記事を「いいね」して閉じなさい。以上!
コミットの正しい運用って?
色んな世界線の人が参考になる記事を書いています。
などなど。
正しい世界の人はこれらの記事を参考にしましょう。
正しいコミットを常に意識しろ、なんて面倒くさい。ならどうすればいい?
ズボラなコミットメッセージ、適当なプルリクエスト、File Changes(243)
は嫌われます。
チームメンバーのMPを削っても、良いことはありません。
ですから、次の事だけは気を付けましょう。
みんなで作業するブランチは汚さない
- 自分だけのブランチで作業して、できあがったらPR!PR!!PR!!!
- マージするときはGitHubのSquash and Mergeを使おう
人の目に触れるもの(≒プルリクエスト)はキレイにする
- プルリクエストの description で何を目的とした変更なのか明確に説明する
- ポエムにしない。
- せめて新機能の追加なのか、バグフィクスなのか、リファクタリングなのかだけでも書く。
- どこを重点的に見てほしいのか、小さな単位で説明する
- もちろん「小さい変更・小さいPR」が良いけど、
面倒くさい諸事情により大きくなることもある。 - 「●●ってファイルの✕✕行目、△△のためにこうしたけど、□□かもしれんので見て欲しい」
- もちろん「小さい変更・小さいPR」が良いけど、
「●●ファイルの✕✕行目」って・・・もっとなんか良いのあるだろ!
はい。そこまで来て初めて、「コミットは小さくしよう、メッセージは適切に」という言葉が身にしみ得るんです。最近実感しました。
とはいえ、「あれやってる最中にここが気になってこっちも直した」は普通の人ならよくあることです。
実装中には面倒くさくて、「あ、コレ違う、別のブランチ切ろう、あの時点に戻ってここからブランチ切って・・・」なんて、面倒くさくてやってられません。面倒くさくて。
例えばSlackに投稿する前に、間違いがないか厳密に推敲するでしょうか?しませんよね。
とりあえずEnterキーをターン!で、後からEditすりゃいいですよね。あとで見えなきゃいいんです。
「あとでキレイにすればいいや」。
そもそもVersion管理をとても乱暴に言ってしまえば、「あとで何があったか見返すためのもの」「あとで過去の編集を改めるためのもの」です。
ですから、コミットなんて最初は適当でいいんです。
実装中は直したいところを直して、やりたい放題コミットしまくりましょう。
**ただし、自分用の作業ブランチで。**他人様に迷惑をかけることだけはやめましょう。
こんなふうに出来たらいいな
●●の機能を追加するため、…
見やすいですね。もっとキレイにすることはできるでしょうが…
「あとでキレイにする」ための色々
ユースケースごとに便利な git コマンド
では実際、一度コミットしてしまった内容をキレイにするために、ユースケース別に使えるgitコマンドを少しばかり。
過去のコミットをまとめたい・並び替えたい
さて、貴方の目前には作業中に作った大量のwipコミットがあります。
$ 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のようです。
さほど複雑な状況にはなっていません。ただ、「ゴミ掃除」コミットが2個あったり、「出来た?」と「バグ直し」のように作業の途中経過がコミットされていたりします。
本来は「ゴミ掃除」で1コミット、「◯◯機能の実装」で1コミット、というのが望ましいところです。
そこで、git rebase -i
でコミットを並び替えて、いくつか一つにまとめてしまいます。
$ 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)が開いて、説明通りに編集すれば、
- 並び替える
- 1つにまとめる
- コミットメッセージを変える
- コミット内容を修正する
といったコミットの操作を行えます。
ここでは、以下のようにいじってみます。
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
による編集の詳細は、別の記事でまとめている人がたくさんいらっしゃいます。
過去のコミットをやりなおしたい
さて、貴方の目前にはまたしても大量の wip コミットがあります。
$ 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しますか・・と思ったものの、今度はどのコミットで何をしたか、メッセージは何も語りかけてはくれません。
過去の自分をぶん殴りたいですか?ぶん殴りましょう。
git reset
で、巻き戻りたい過去のコミットを指定します。
この目的では間違っても --hard
は使わないように気を付けましょう。
$ git reset df735e2e2b31df94d73ad134fa02692996e3ad9d # ← "◯◯機能の実装 (#43)" のコミットハッシュ
これで、過去の自分の愚かなコミットたちはなかったことになりました。
ただし、--hard
オプションは使用していないので、ファイルの変更は生き残ったままです。
あとは正しい commit を生産するだけです。
$ 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
することができます。
詳細はこちらを参考にしましょう。
ブランチ名にwipって入ってるんだよね…
誰かが自分の作業ブランチから新しくブランチを切ってしまう という悲劇を少しでも避けるため、
作業ブランチ等にはwip-xxxxx
のようなそれと分かる名前を付けることもあります。(というか付けたほうが良い)
ですが、いざPRを出す段になって、ブランチ名がwip-xxxxx
では締まりません。
「もうコレは特定の新しい要件を満たしたブランチだよ」ということを示すためにも、名前を変えてしまいましょう。
$ git branch -M feature-xxx
$ git push -u origin feature-xxx
# 作業ブランチを既にリモートにプッシュしていた場合、リモートのゴミも残さずキレイにしましょう。
$ git push origin :wip-xxxxx
これらのコマンドを使いこなせると、作業中は気持ちよくコミットし、
終わった後の「キレイにする」際もミス少なく、気楽に挑むことが出来ます。
もっと気持ちよく作業するために
tig
git log
や、 git add -p
は便利ですが、随所で痒いところに手が届きません。
tigを使うことで、そのあたりで楽をすることができます。
詳細はこちらの記事を参考にして下さい。
また、この記事では取り上げられていませんが、「tigの画面で選択したコミットのハッシュをコピーする」keybindを設定すると、
特定のコミットに遡ったり、rebaseしたりがとても楽になります。
# 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://fithub.com/junegunn/fzf/wiki/examples#git を参考にしてもいいですし、
コレのためだけの拙作、ブランチリスト出す君git-branches を使ってもいいかもしれません。
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
終わりに
いかがだったでしょうか。
几帳面な人や、真面目な人が読んだら卒倒するか、罵倒の声が挙がりそうな記事ですね。
マメにコミットしたり、丁寧にメッセージ書いたりを継続的にできない・・・僕のような人たちの、お役に立てれば幸いです。
間違いなく言えることは、「正しくコミット運用できるに越したことはありません」。
チームメンバーに厳しい人がいたり、マメな人がいるのであれば、あえて道を踏み外すようなことはせず、みんなに歩調を合わせて「正しい運用」ができるよう、心がけましょう(自戒)。
Discussion