Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

この記事の対象読者

  • commit messageは wip fix が9割を超える
  • 「宇宙人」と書いて「マメな人」と読む
  • 夏休みの宿題を9/1までに終える人は未来人である
  • 「毎日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

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

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

  • 自分だけのブランチで作業して、できあがったらPR!PR!!PR!!!
  • マージするときはGitHubのSquash and Mergeを使おう

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

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

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

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

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

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

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

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

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

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

●●の機能を追加するため、…

  • ●●の実装: 9xd4941
  • ●●のテスト: 0979xd2
  • ついでにやった✕✕のリファクタリング: 337xab7
  • 自動生成コード部分: 6exd364

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

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

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

ユースケースごとに便利な 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 による編集の詳細は、別の記事でまとめている人がたくさんいらっしゃいます。

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

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

さて、貴方の目前にはまたしても大量の 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しますか・・と思ったものの、今度はどのコミットで何をしたか、メッセージは何も語りかけてはくれません
過去の自分をぶん殴りたいですか?ぶん殴りましょう。

adv2016-12-02-02.png

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 というオプションがあります。partialp でしょうね。
実行すると、対象ファイルのdiffが表示され、必要な変更だけを選択して add することができます。

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

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

ブランチ名に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を使うことで、そのあたりで楽をすることができます。

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

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)

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

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

fzfpecoを使いこなしましょう。https://github.com/junegunn/fzf/wiki/examples#git を参考にしてもいいですし、
コレのためだけの拙作、ブランチリスト出す君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

終わりに

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

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away