既存の戦略を知る

まず、よく知られた既存のルールである git-flowgithub-flow を紹介します。

git-flow

git-flowは、世の中がGitに移行し始めた直後の時期に、やはり同じようにGitの使い方に迷った人のために作られた、ブランチ運用ルールです。

git-flowは次のようなルールになっています。

  • 開発の本流は development ブランチで行う
  • 機能ごとに development ブランチから派生させた features/XXXX ブランチで開発する
  • 機能が完成したら features/XXXX ブランチを development ブランチにマージする
  • 緊急の修正などは hotfix/XXXXX というブランチを作り、 masterdevelopment 両方にマージする
  • development ブランチから releases/XXXXX ブランチを切り、 帳尻を合わせた後 master ブランチにマージする

おそらくこれは、パッケージアプリケーション 開発の世界観から生まれたワークフローだと思われます。 リリースという作業にずいぶん意識が割かれていることからも明らかです。

一方Webの世界では毎日何度もデプロイすることは当たり前で、リリースという作業にコストを割いている場合ではありません。 実際にWeb開発とgit-flowは相性が悪く、学習コストも高く別の複雑さを抱えることもあり、早々に廃れていきました。

一番の問題は、 master ブランチと development ブランチの乖離が激しくなりすぎることで、 git-flowを実践した人の多くは、リリースブランチがいつも地獄だと不満を漏らしていました。 実際地獄でした。

github-flow

github-flowは、その名の通りGithub社が作ったワークフローで、Web開発と相性がよくそれほど複雑でもありません。 github-flowは、単なるブランチ運用ルールにとどまらず、Git以外の要素も考慮された開発全体のワークフローと言える性質を持っています。

github-flowは次のようなルールになっています。

  • 開発の本流は master ブランチで行い、常にデプロイできる状態を保つ
  • 機能ごとに master ブランチから派生させたブランチで開発する
  • 機能が未完成の段階から プルリクエスト し、レビューを受けながら開発する
  • 機能が完成したら master ブランチにマージする
  • プルリクエストがマージされたらすぐにデプロイする (Webアプリケーションの場合)

上記のようにgithub-flowは、Gitというよりむしろデプロイを物語の焦点として捉え、そこから逆算してGitの使い方を規定しているように見えます。 いやむしろGitの使い方は全く規定していないと言ってもいいかもしれません。 masterブランチは常にデプロイ可能というたったひとつのルールだけを厳格に守れば、基本的に何をしても上手くいくようになっています。

Githubで普通にリポジトリを作り、普通にプルリクエストを受け付けると、自然とこのgithub-flowに従った形になります。 Github上で開発されているほとんどのソフトウェアは、無意識にgithub-flowを実践していると言っていいでしょう。

git-flowの失敗は、複雑なプロセスのせいで、手段であるはずのGitそれ自体が目的となってしまったことです。 一方github-flowは、最初から目的をデプロイに置いているため、開発者は手段と目的を履き違えることがありません。 ソフトウェア開発の目的はユーザに価値を届けることであり、偉い人たちが設計した難解なプロセスを盲目的に遂行することではないのです。1

その代わり、Git部分には相当な裁量があり、つまり人々はここを正しく実現する責任があるのです。 そして多くの人はここに迷っているのです。

1. ダメなアジャイルとかダメなDDDとか全部このパターンだと思う

野良ルール

Githubでプルリクエストをする際によく使われる用語を解説していきます。

rebase

rebaseはブランチの分岐元を変更することです。 他のプルリクエストをマージするなどして master ブランチが進んでしまうと、 変更点が重なってしまいコンフリクトを起こすことがあります。 そのような場合はマージできないので、ブランチの分岐元を最新の master ブランチに変更し、 衝突箇所を正しい形に修正してpushし直す必要があります。 これを一言で rebaseしてください と表現します。

squash

squashはコミットをひとつにまとめることです。 開発中は大いに悩んでいただいて結構なのですが、リポジトリに入れるときには、その悩んだ軌跡というのは全く必要ない情報です。 また、 master ブランチを常にデプロイできる状態にするということは、悩んでる最中の正しくない状態がコミットログに残ってしまうのは不都合です。 これらの観点から、一定数のメンテナがコミットをひとつにまとめることを求めます。 特に大きく重要なプロジェクトになると、ゴミコミットは変更点の可読性を下げ、致命的なミスに繋がる恐れもあり、squash必須のルールを作っていることが多々あります。 なお、 squashしてください と言われたら、rebaseとsquashを両方行うことを意味していることが普通です。

WIP

WIPは、 Work In Progress の略で、つまり作業中ですという意味です。 プルリクエストのタイトルの頭に [WiP] と付けておけば、まだ完成してないけどレビューして欲しいといった意味になります。 リポジトリのメンテナは、 [WIP] が外れたらマージして良いんだなと判断できます。

hub コマンド と ghq コマンド

ここでもうひとつ、重要なツールとして hub コマンド と ghq コマンドを紹介します。

hub コマンドはGithub社が配布している git コマンドのラッパーで、hub pull-request などのGithub向けのコマンドが多数追加されています。 この hub コマンドのデフォルト動作は、Github社が想定しているGithubの使い方に添った動作と言えるでしょう。

ghq コマンドは、gitのリポジトリを clone するためだけのソフトウェアですが、 ルールに従ってコードをダウンロードする2ので、リポジトリの管理が非常に簡単になります。 ghq コマンドは、Github公式ではないものの、github-flow を実行するのに非常に相性がよいツールになっています。

例えば自分の環境では、

$ ghq get dwango/mastodon

を実行すると、~/src/github.com/dwango/mastodonorigin というリモート名で https://github.com/dwango/mastodon (書き込み権限がないもの)が clone されます。

さらにこのディレクトリで、

$ hub fork

を実行すると、 masarakki というリモート名で git@gitub.com:masarakki/mastodon (書き込み権限があるもの) を設定します。 もちろんGithub上でもforkされています。

このように、 hubghq を使うとgitの運用ルールに強い制約を課すことができます。

2. おそらく go get の思想から来ている

まとめ

results matching ""

    No results matching ""