おことわり:あくまでも個人の意見です
Microsoft本社で6年、いくつかのプロダクトチームで開発してきて思ったことです。
Git のコマンドで要らないもの
Git rebase は要らない
いろいろと利点だと思える点もあるようですが、マージコミットが無くて済むとか、コミットグラフが読みやすくなるとか、そういうコミットグラフの質が「製品・サービスの価値」に深く関わるとは思えない、というのが理由です。プライベートなブランチ間でやるなら構わないけれど「チーム全体のルール」にしてしまうと逆に作業効率が落ちると思う。git rebase の作業をする時間があったら、次のタスクに移った方がいいと思う。実際使ってる人はほとんど見かけない(というか使ってても見えてないだけかもしれないけど)。少なくとも自分使わない。
Git cherry-pick は要らない
個人的には「百害あって一利なし」だと思います。一見便利そうだし実際便利かもしれませんが、開発のベストプラクティスというよりは「one-off(一度きり)のハック」だと感じます。例えば、緊急のバグを治す時に、開発ブランチから派生したホットフィックスブランチで治し、リリースブランチにチェリーピックする、という流れがありますが、ベストプラクティスは逆でリリースブランチから派生したホットフィックスブランチでそのバグだけを治し、リリースブランチにマージしてからさらに開発ブランチにもマージする、そうするとチェリーピックは要りません。
ある機能を限定的にリリースしたい場合、そもそも設計にフライティングの機能を入れて「露出していない」状態でリリースします。その後、フライティング設定で特定の機能を「露出」させると目的を達成できます。その機能のコミットが特定のブランチに含まれるかどうかが、ユーザがその機能にアクセスできるかどうかを制御してしまうのはブランチには荷が重すぎると思うのです。ベストプラクティスに近づけるためにもcherry-pickを使った裏技はチーム単位で非奨励とすべきだと個人的には思います。
Git merge --squash は要らない
コミット毎にメッセージを細かく残したくないというのが動機のひとつのようです。ですが仮にコミットグラフに無駄なコミットやコミットメッセージが多く残ったとして、それが数週間後、数か月後、数年後にどのぐらい問題になるかとういうと、大した問題にはなっていないと思うのです。昔はせっせとsquashしてたけど今は全くやらない、って人も多いのでは?どうせsquashするからと、雑なコミットをすることを助長するぐらいなら、見えるからこそ「キリのいいところでコミットする」という習慣を付ける方が大切だと思う。周りでもsquashをする・しないを気にする人はあんまりいないと思う。
これだけ分かってればいい Git のコマンド
Git clone
レポジトリのコピー(クローン)を作るのに絶対必要。僕の場合、同じリモートのレポジトリでも複数作ります。同時にいくつかのブランチで作業をするめることもあるので。その時は下のように、末尾に2、3、4、と数字を付けてクローンします。
git clone https://github.com/yoshiwatanabe/test.git test2
Git add
特に、. ワイルドカードを使って全てファイルをステージングエリア(インデックス)に移動するやり方
git add .
を多用してます。
間違ってステージングに入れてしまったファイルは
git reset HEAD <ファイルのパス>
でステージから引きずり下ろします。
Git commit
git commit -m "コミットの目的"
ほとんどこれで済ませます。
たまーに、--amend で忘れものを届けますが、それはまだコミットを例えばGitHubにまだpushしてない時に限ります。
git commit --amend
でもこれは絶対に必要というわけではありません。新しいコミットで忘れ物を届ければいいだけです。
「コミットはちゃんと完全なものにして、あとからコミットで忘れた箇所を変更するのはやめて。まだpushしてないなら commit --amendして」って同僚・上司に言われる、みたいなシチュエーションだとしたら、それはその人が「妙にこだわってるだけ」で、まぁお国柄とかチームの文化にもよるでしょうが、私がいたMicrosoftのチームでコミットの完全さにこだわる人は見たことがないです。そんなことよりさっさと次の仕事に移った方がいいですから。
Git fetch
もっとも安全でなおかつ出来るだけ頻繁に実行すべきGitコマンド。
git fetch
手元にあるGitHubなどの中央レポジトリの状態を最新のものにしてくれます。
実は、このコマンドは中央レポジトリの最新の状態をダウンロードしてくるだけなのでこれだけではあまり意味がありません。私の作業ワークフローでは、このコマンドの後に git pull git merge git remote prune origin などが続きます(後述)
- ネット環境がないキャンプ場で仕事する予定がある場合、同僚のトピックブランチも含めて完全に最新の状態で出発したい
- 同僚のトピックブランチも見える状態になるので、だれがどのあたりのコードをいじってるか分かる
-
git pullは実はgit fetchとgit mergeのショートカットだという理解を深める
といった利点があります。
どちらにしろ、手元のレポジトリが中央レポジトリの最新版であることは悪いことではないです。
Git pull
じつはgit fetch も裏でやってくれます。僕は癖で git fetch を個別にやってから、git pullしますが、本当はgit fetchは要らないですね(以前は 「git fetch からの git merge」コンボで git pull そのものを使ってませんでした)
git pull
現在checkout中のローカルブランチを最新(=中央レポジトリでの該当ブランチの最新状態)のものにしてくれるんですが、その理由としてを2つのシチュエーションがあります。
シチュエーション1
トピックブランチで作業を続けていて、まだしばらく作業時間がかかりそうな時、合流予定の合流先ブランチ(例:master)を最新にして、それをgit mergeでトピックブランチに引っ張って来る場合。
この作業は大切です。なぜなら、自分のトピックブランチ(が含む合流予定先ブランチの内容)が遅れて居ればいるほど、同僚がすでに直したバグを再度治すというヘマ、同僚がすでにリファクターした便利な仕組みを見逃すヘマ、同僚がすでに削除してしまった古いコードのバグをお節介にも直して上げるヘマ、などが発生する確率が上がってしまうからです。
また頻繁にやるほど、コンフリクトが起こった場合の規模が小さくて済みます。
シチュエーション2
新しいタスクのためにコーディングを始める前、つまり新規トピックブランチを作成する直前に、出来るだけ最新の状態(中央レポジトリのそのブランチの最新のコミット)をスタート地点にするため。
Git merge
上の git pull のシチュエーション1の流れで、git merge を使う、というパターンが9割りですね。
たまに、同僚が進めている、完了間近のトピックブランチを自分の作業ブランチに取り込むためにgit mergeすることもあります。この場合、当然同僚が中央レポジトリにgit pushして該当ブランチを公開していることが前提になります。
Git checkout
2つのシチュエーションがあります
シチュエーション1
ブランチの間を行ったり来たりする、つまり作業ディレクトリをいろんなブランチの状態に切り替えるため。特に頻繁に使うのが作業中のトピックブランチを、合流予定ブランチ(例:master)に対して、最新のものにする一連の作業をする時。
git checkout master
git pull
git checkout topics/task2
git merge master
みたいな感じでcheckoutで移動します。
シチュエーション2
新規のトピックブランチを作成するとき。-b を指定して「新しいブランチを作って、そのブランチをチェックアウトする」という意味で
git checkout -b topics/task3
とやります。
Git push
git push
自分のトピックブランチを中央レポジトリにアップロードするために使います。私の一日の仕事は、 git pushで終わることが多いです。中央レポジトリにpushすれば、クラウドストレージでバックアップされたことが確定するので、もしかりに自分のノートパソコンがどこかで水没してしまってもその日の作業内容は失われませんから。
最終的な目的ブランチ(例:master)へは
pushではなく、プルリクエストを使います。プルリクエストはGitのコア機能ではないですが、一般的な方法です
Git remote prune origin
これも安全なGitコマンドの1つです。ブランチのお掃除に使います。
git remote prune origin
からなずgit fetchからやります。これは安全なコマンドなので使ってみれば簡単に理解できます。
手元の中央レポジトリにあるブランチのうち、GitHubなどの中央レポジトリではすでに削除されてしまったブランチもあるかもしれません。というのも、開発者はトピックブランチの役目が終わったらそのブランチを削除してしまうことが一般的だからです。
このコマンドを使うことで、もう存在しない中央レポジトリのブランチを、手元の中央レポジトリでも消してくれます。
この後にgit branch -v でブランチをリスト表示すると
dev/topics/integration 37f95b9f9 [gone] add integration test
dev/topics/topic3 711e639e2 [gone] adds unit test for logout feature
dev/master e8468657b initial poc load test
というように、もう存在しないブランチをトラックしている手元のブランチが見えます。こういった場合、
git branch -d dev/topics/topic3
などでブランチを消してしまいます。
ブランチが
Git branch -v
上の例で挙げたように、すでに存在しない中央レポジトリのブランチをトラックしている手元のブランチが見えるようにするために使います。
Git branch -d
これも既出ですが、手元のブランチは出来るだけお掃除して数が増えすぎるのを防ぎます。
まとめ
私個人の作業ワークフローにおいて、必要としないGitコマンドと、頻繁に使うGitコマンドを紹介しました。