続・Gitリポジトリ運用の最適解

このエントリは過去に書いたエントリの続編として位置づけています。

この記事を書いた当時、いや少なくとも少し前までは本気でマージコミットがないGit運用が最高だと考えていました。
だからこそ、何故A successful Git branching modelがNon-Fast-Forwordマージを推奨しているか疑問であり、昨今のgit-mergeには--no--ffオプションを設定。むしろgit-configでデフォルトの挙動を--no-ffにできるから設定しとけお前らという話に対してよく理解ができていませんでした。

マージコミットがないGit運用が最高だというのは限られた場合の話ではないか

ある特定の場合においてのみこの話は適応されるのではないかということに気づきはじめたような気がします。
これは例えばリリース前の開発版のGitリポジトリであったとしましょう。
masterブランチには最新のコミットが追加されていき、リモートリポジトリのmasterブランチにもpushされていきます。
このような場合に関してはマージコミットがなくてもさほど悪い要素はないように思います。

masterとはどういう立ち位置なのか

例えば開発が一段落してmasterブランチの内容がリリースされたものとしましょう。
しかし、リリースした後も開発は続きます。その場合はどこにコミットを蓄積していくべきでしょうか。


masterは安定版といういつでもリリースできる状態に位置づけたいものです。
となると新たなリモートブランチが必要ですね。developmentとでもしておきましょう。


リリース以後のコミットについてはリモートブランチのdevelopmentに蓄積していきます。
さて、また一段落しました。developmentの内容をmasterにマージしたい。
この時のmergeはNon-Fast-Forwordマージのほうが優れていることが感覚的に理解できてきました。
(tagをつけることでも別に区別できるんじゃねえかと思うのですが、tagはcommitのaliasでしかないのでここでマージコミットの代役を担えるわけではありません。ハッシュ値を覚えておいてリリース時のコミットをがんばって探す行為と大差ありません。むしろtagとマージコミット両方あるとよいのでしょうか?)


一段落しているブランチに対して、新たな変更を加えようとしているわけです。
一段落しているブランチの口を広げてまたコミットを伸ばそうとしているようなイメージなのではないでしょうか。
私達がdevelopmentブランチに蓄積していたコミットは、広義的に言えばリリース時のコミットの延長線上にあるものなのですが、ここは区別をするべきです


そこで、ひとつ懸念が思いつきます。いきなり、masterの扱いを変更してしまうと少なからず混乱が発生し、誤ってmasterにコミットしてしまうなどということも十分考えられます。
そのため、予めリモートリポジトリにdevelopmentブランチを切って開発を進めていき、一段落したらmasterにNon-Fast-Forwordマージをしていく運用がすっきりする気がします。


というのが思いついた具体例なのですが何が言いたいかというとリモートブランチの運用まで含めて考えるとマージコミットの有用性が見えてくるのではないでしょうか。


そこでの、A successful Git branching modelなんです。リリース後の運用についてもちゃんと考えてブランチの役割を明確に定めている為mergeする際にはかならず視覚的にわかるようにすべきなんだと思います。

つまりどういうことだったん

マージコミットが無いGitリポジトリ運用とは適切にリモートリポジトリを活用できていない運用になってしまっているのではないかと思っております。

rebaseが悪者扱いされている

当時というか今でもgit-rebaseは溺愛しているコマンド(次点はgit-stash)でしょう。
直近で参考にさせていただいたgitに関しての資料。下記2つの中でのrebaseは悪者です。(語弊があるかもしれませんがそう読めました

まず、git-push --forceを使用しなければ前に進むことのできない状況にgit-rebaseを使い陥ることがある。
複数人運用でgit-pish --forceは忌むべき存在であるのでgit-rebaseも悪者だ。
こう読めました。たしかに複数人で運用しているリポジトリに対してgit-push --forceが実行されるべき自体は避けるべきで絶対に止めなければいけません。


なぜ、git-push --forceを実行しなければいけない状況に陥るかさえ抑えておけばいい話だと思うのですが。
リモートリポジトリにpushされているコミットに対しての操作をgit-rebaseを使って行うから行けないんですね。
これをやらなければいい話ではあると思うんですが論点はずれているかもしれません。
(git初心者が対象であるならば排除できる可能性は排除しておくべきであるし、git-rebaseを使ってややこしいことをする必要も全く無いので)
ちなみに、これはgit-rebaseに限った話ではないのでgit-resetでも同じことです。


複数人で使用しているリポジトリに対して過去のコミットの操作を行う場合はまずgit-revertしてコミットの内容を打ち消すコミットを作成してからはじめるべきです。
git-revertを行えば歴史改変することもなく作業することができると思います。


こまめにコミットをしたものを整理する操作はgit-rebase -iによって柔軟に行えます。
squash mergeでブランチでのコミットを1つにまとめてmergeすることはできますが
git-rebase -iを使うともっと柔軟にいろいろできます(squash mergeでもいろいろできるのであればごめんなさい)


git-rebaseは悪だ。というイコールの印象が強烈ですが中身を少し知るとおもしろいのかもしれません。

過去を書き換えるならお前の世界だけにしとけ他人を巻き込むな

抽象的に言うとそんな感じなんですかねえ。
pushしていないコミットについてはどんな操作をしても許容されます。それはあなたの世界だから。
push済みのコミットに干渉しようとするとそれは、他人の世界に干渉していることになるのです。
他人の世界に干渉する権限は与えられていないのです。だからpushしてもrejectされる。
その世界の歪みそれこそが--force。刹那「見つけたぞ、世界の歪みを!」


完全に答えがでているわけではないのですが自分の中で少し前進があったので書きました。

[asin:4873114403:detail]