2015年1月30日金曜日

Gitでpush -fせずにmasterの間違いを修正する方法

そんなものはない (タイトルは釣りだ。ゴメン)。

でもよく聞く「ミスに気づいて、思わず master に push -f しちゃった」という話は、実はある運用方法で簡単に防ぐことができるんだ。 その運用方法とは・・・


「使い捨て統合ブランチを作って、最初はそちらにmergeすること」

これだけ。
どのようなやり方なのか、具体的に見ていこう。

1. まず使い捨て統合ブランチを作る。
使い捨て統合ブランチの名前は何でもいいけど、pu (proposed update) がよく使われる。ここではとりあえず pu という名前を使うことにする。チェックアウトしたら push --set-upstream / -u で push しておこう。
% git checkout -b pu master
% git push -u origin pu


2. 新しいトピックがあれば、まず pu にマージする。
これまではトピックを master (あなたのリポジトリではdevelopという名前かもしれない)にマージしてたよね? でもこれからは master ではなく、まず pu にマージしよう。
% git checkout pu
% git merge --no-ff topic0

マージすべきトピックがなくなるまで merge を繰り返し、マージがおわったら push する。
% git merge topic1
% git merge topic2
% git merge topic3
% git merge topic4
% git merge topic5
% git push origin master pu

# この段階では master は origin と同じなのに push してるが、今は気にしないで良い。


3. master をすすめる前にしばらく時間を置く。
pu にマージしたブランチをいつ master に取り込むかは完全に好みの問題だ。

コーヒーを1杯飲む間かもしれないし、
CIサーバがpushしたコードをテストし終わるまでかもしれないし、
お昼ごはんを食べ終わるまでかもしれないし、
明日の朝までかもしれないし、
1週間後までかもしれない。

これまでに master を push -f したくなったのは、どのくらいの時間たってからだったかを思い出して決めても良い。


4. master をすすめる。
そろそろいいかなと思ったら master を進める。 master を pu のどこまですすめるか、git log を見ながら考えよう。
% git checkout master
% git log --first-parent --oneline ..pu
0169536 Merge branch 'topic5' into pu
18e6619 Merge branch 'topic4' into pu
ace949a Merge branch 'topic3' into pu
ee219d2 Merge branch 'topic2' into pu
bf50ff8 Merge branch 'topic1' into pu
070e0a5 Merge branch 'topic0' into pu

今回は topic4 をマージした 18e6619 まですすめることにする。 pu は master の子孫なので、単に以下のようにすれば、Fast-forward で master が 18e6619 まで進んでくれる。
% git merge 18e6619

このとき、 pu の先端にまで進めてしまうと pu の先端に何か間違いがあったときに master まで push -f するはめになってしまう。

それでは pu を用意した意味がないので、masterを進めるのは 「この位置なら確実にpush -fが必要になることはない」 という自信がある位置までにしておこう。

もちろんそれが pu の先端だというのであれば、そこまで進めても問題ない。


5. ステップ 3 に戻る。
新しいトピックがあれば pu にマージして(以下略


使い捨て統合ブランチを使った運用方法は以上だ。
いかがだっただろうか? いくつかQ&Aを追記しておく。


Q&A

Q: いつ master を push -f するの?
A: しない。
常に push -f 可能な pu という使い捨て統合ブランチを用意することで、 master に push -f するような事態を避けることができるため、そもそもmaster を push -f する必要がないんだ。

その代わり pu に関しては master..pu の間のコミットであれば、修正したくなったらいつでも修正して push -f していい。
 
そのときは pu を master に戻したうえで作り直し、push -f すればOKだ。 例えば以下のような感じになるだろう。
   % git checkout pu
   % git reset --hard master
   % git merge topicx
   % git merge topicy
   ...
   % git push -f pu

 ただし、参加する開発者には pu を元にトピックを作らないよう周知しておこう。 pu はいつ push -f されるかわからないからだ。


Q: ●●● flow でも使える?
A: もちろん。
メインで使う統合ブランチに対して、使い捨て統合ブランチを作ればいい。

使い捨て統合ブランチの概念はもともとGit付属のマニュアル gitworkflows (日本語訳) に記載されているものだ。もっと柔軟なワークフローが必要ならそちらを参考にすると良い。

gitworkflows(7)を図解したスライドもあるのでチェックしてほしい。


Q: マージのコミットメッセージに into pu ってつくのが嫌なんだけど?
A: エディット時に消すか、ローカル環境とリモートのブランチ対応を
master -> pu
realmaster -> master
のようにすればいい。


Q: いつ master を進めたら良いの?
A: いつでも。
もしいちいち考えるのが面倒なら、 「pu の 1 コミット後ろまで進める」 みたいなシステマチックなルールを決めてしまってもいい。  script 化するのもいいだろう。

(2015/02/01追記) master を「pu の <n> コミット後ろまで進める」という操作は git merge pu~<n> と書けばいい。例えば n = 3 の場合
% git checkout master
% git merge pu~3
となる。n が小さい場合は ^ を繰り返し指定してもいい。同じく n = 3 の場合は
% git checkout master
% git merge pu^^^
でもいける。


Q: git push -f origin master pu してたら間違えて master の force push しちゃった!
A: push -f ではなくてブランチ名の前に + をつけるようにしよう。
git push はブランチ名の前に + 記号をつけることで force push であることを git に指示できる。  つまり、 git push origin master +pu すれば、 master は普通の push で、 pu は push -f しろ、って意味になる。


(2015/02/01追記)
Q: 削除していいトピックブランチはどれ?
A: git branch --merged master でリストアップされるブランチは削除していい。
git branch -d はHEADにマージされていないブランチを削除できないので、master を checkout したあと git branch でリストされるブランチを全部 branch -d してもいい。 master にマージされていないものは自動的にエラーになって削除されずに残ることになる。

0 件のコメント:

コメントを投稿