gitのベストプラクティスに関する考察

※独断と偏見です。何かあったら※欄でよろ
##ITSとの連携

- **ITSを使っている場合、トピックブランチの名前にはチケット番号を含める**
<p>するとコミットメッセージにブランチ名が書いてあるのでITSのコマンドを書くのが簡単になる。またフックでコミットメッセージにコマンドを追加することもできる。</p>

##ブランチの構成

- **開発ブランチ**
<p>最初からある。名前はmasterでもdevelopでもいいけどリポジトリを作った時のmasterをそのまま使うのがいいんじゃないかと。また定期ビルドにはこのブランチのスナップショットを使う。</p>
- **リリースブランチ**
<p>リリースのための修正が必要で、かつ開発と並行作業したい場合は切る。でなければ不要。リリースブランチを切る、あるいはリリースブランチへマージするという事は機能固定するという事と同義となる。機能固定してリリースする分全ての修正が完了したらタグを打つ。</p><p>なお、ここに投入されたコミットは開発ブランチへマージされる。タイミングはリリース完了後より随時のほうがいいだろう。</p>
<p>...というのが定番だけど、リリース独自の設定だのコードだのがある場合は逆になるべく開発ブランチの方で修正してリリースブランチをrebaseしていくほうがいいかも知れない。どちらにせよリリース独自の修正と開発ブランチへ反映する修正は別のコミットにするべきなのは確かだ。</p>
- **HotFix**
<p>リリースブランチの一種として扱おうかとも思ったが別にすることにした。このブランチはHotFixと次のリリースを並行作業したい場合に切るのでこれが必要な場合はそう多くはなく、リリースブランチで作業してもいいのではないかと思う。</p><p>これもリリースブランチと同様、完了したらタグを打ち、投入されたコミットは開発ブランチへマージされる。</p>
- **トピックブランチ**
<p>手元で切るブランチ。開発ブランチからもリリースブランチからも切り得る。今やっている一群の修正を全て含む。ITSを使っていればチケットに対応している必要がある。</p><p>面倒かもしれないけど手元でも開発ブランチやリリースブランチの上で修正しないでトピックブランチを切るほうがいい。理由は--no-ffと同じことができないから。<p>

##トピックブランチを開発ブランチやリリースブランチにマージするとき

- **原則的に--squashは指定しない**
<p>--squashを指定してマージするとmasterのコミットブランチとトピックブランチのコミットが結び付けられないため以後のマージに支障をきたす。</p><p>マージした後は必ずそのトピックブランチを捨てるならそのような運用も可能だが、その場合でもそのブランチを消すときに-dではなく-Dを指定する必要があるためちょっと安全ではない。代わりにマージ前にrebase -iでトピックブランチを改変してまとめてしまうほうがいいように思える。</p>
- **普通は--no-ffを指定することが多いだろう**
<p>そうすれば一連のコミットがトピックブランチだったことを記録できる。また--no-ffを指定すると必ずマージコミットが作られるのでそこに「closes」のITSコマンドを書ける。てかRedMineとかだとmerge id/#xxとかをチケットのクローズコマンドに設定できる。個人的にはこの運用はチケットの寿命が長いとマージしなければならないコミットがたまるのでダメだと思うけど(チケットの寿命が長いのもダメなんだけどね)。</p>
- **--ff-onlyしたいよね**
<p>自分一人で使ってるリポジトリならこれは考える必要はないけれどmasterへマージするときに本当にマージになってしまうのはビルド、動作確認の面から考えてちょっと嫌だ。なので--no-ffと--ff-onlyを同時に指定したくなるけど(少なくとも1.7.10.4では)できない。</p>
- **--no-ffしないで--ff-onlyする運用**
<p>一方でトピックブランチがビルドが通らないコミットがあるなどで非常に汚い場合、マージ前にrebase -iしてトピックブランチのコミットをまとめてしまってからmasterへマージする運用もあり得る。その場合masterへマージするときは--ff-onlyを指定する方がいいだろう。</p><p>この場合はrebase -iするときのコミットメッセージにITSのコマンドを書くことになる。</p>
- **上位リポジトリが例えばperforceの場合**
<p>git-p4はマージのあるブランチを扱えないのでmasterは--ff-onlyでマージされている必要がある。なので必然的に「--no-ffしないで--ff-onlyする運用」をせざるを得ない。</p>

##その他
- **add -pは邪道**
<p>add -pした分がテストされているかどうか考えればわかるはずだが、gitに限らずVCSには「手元でチェックしたモノと同じモノをcommitする」というあまり語られない原則がある。</p><p>add -pしてしまうとこの原則が破られてしまうのでトラブルのもとになる。私ゃこの原則を完全に無視した職場にいたことがあるのでそれどれだけ酷いことなのかよーーーーーくわかっているつもりだ。</p><p>とはいえ「トピックブランチにはビルドできないモノを置いてもいい」という運用方針の下ではその限りではない</p>
- **トピックブランチは中央リポジトリへはpushしない**
<p>あとで消さなきゃならないので面倒なのがその理由。それにトピックブランチはITSのチケットに対応しているのでトピックブランチを他人と共有するという状況は正しく開発管理していればあり得ないはずだ。</p>
- **とは言え、やり方によっては...**
<p>継続的インテグレーションなどでビルドやテストを自動化した場合、add -pしても問題なかったりトピックブランチを共通リポジトリに置く意義がある場合もある。きちんとTDDしているなど、場合によってはテストが通ったら自動的に開発ブランチへマージするなどの自動化もあり得る。</p>
<p>また、擬似的にペアプログラミングするためにトピックブランチを共有するみたいな運用もありうる。</p>
- **rebaseは結構危なっかしい**
<p>masterとかみたいな「上位ブランチ」のコミットをトピックブランチみたいな「下位ブランチ」へ取り込む時にrebaseするのは履歴がきれいになって便利なんだけど、rebaseを元に戻すにはORIG_HEADへresetするしかない。別のところでリベースしちゃうともうそう簡単には元には戻せないし、そこでgcが走っちゃうともう絶対に戻せない。</p>
<p>そのあたりを危なっかしいと思う人は履歴が網状になるのを我慢してマージする方がいいかもね。マージなら履歴にちゃんと残ってる「マージ前」へresetすればいいし。</p>
- **本当は怖いcherry-pick**
<p>cherry-pickはrebaseと--squashによるマージを合わせた危険性を持つ。cherry-pickしたコミットは別のコミットになるので覚えておかなきゃうっかり二重に取り込んでファイルがまるごとコンフリクトしてるのにdiffで見るとコンフリクトなんてしてない...なんてこともある。これがまたわかりにくい。</p>
<p>gitのマージは左右が同じなのをコンフリクト扱いにしてくれるから困る。これさえなければもっと柔軟に運用できるのに...マージとかdiffを外部に任せる方法もあるけどね。</p>

##Gitに向かないもの
<p>IDEによる開発はあんまり向かない。IDEがダメなんじゃなくて古いIDEはそもそも連携のことなんか考えてなくて、バイナリファイルをバラ撒いたり絶対パスをデータのあちこちに埋め込んだりファイルが更新されただけとかのつまんねーことで管理ファイルを更新してコンフリクトの原因になってくれたりするのが理由。</p>
<p>一応対策がないわけではなくて、例えば「git update-index --assumed-unchanged」(ワークの変更を無視)とか「git update-index --skip-worktree」(リポジトリの変更を無視)とかで本当に更新が必要なときだけ無視設定を取り消してgit-addして更新を反映させるようにできるけど、自動で更新を認識してくれないので面倒だ。</p>
<p>他には大きいデータを扱うのも苦手だ。容量を節約しようにも過去のデータをsquashするとコミットIDが変わっちゃうからね。</p>