私のオフィスでは、ポリシーとして無限ブランチのマージを望んでいます。他にどんなオプションがありますか?


119

私のオフィスは、ブランチの分割とマージをどのように処理するかを理解しようとしており、大きな問題に直面しています。

私たちの問題は、長期的なサイドブランチにあります-マスターから分かれるサイドブランチで数人の人々が働いている種類で、数ヶ月間開発し、マイルストーンに到達すると2つを同期します。

今、私見、これを処理するための自然な方法は、単一のコミットにサイドブランチをつぶすことです。master前進し続けます。当然のことです- master過去数か月の並列開発を過去にさかのぼってダンプしているわけではありません。そして、もし誰かがサイドブランチの歴史のためにより良い解像度を必要とするなら、もちろん、それはすべてまだそこにあります-それはただではなくmaster、サイドブランチにあります。

問題は次のとおりです。私はコマンドラインのみを使用していますが、残りのチームはGUISを使用しています。そして、GUISには他のブランチからの履歴を表示するための合理的なオプションがないことを発見しました。したがって、「この開発はブランチから押しつぶされた」と言って、スカッシュコミットに到達した場合XYZ、何が入っているかを確認するのは非常に苦痛XYZです。

SourceTreeでは、私が見つけることができる限り、それは大きな頭痛の種です:でmaster、からの履歴を見たいmaster+devFeature場合は、チェックmaster+devFeatureアウトする必要があります(異なるすべてのファイルに触れる)、または適切な場所が見つかるまで、リポジトリのすべてのブランチを並行して表示するログをスクロールします。そして、あなたがどこにいるのかを理解してください。

私のチームメイトは、開発の歴史にあまりアクセスできないことを望んでいません。したがって、彼らは、これらの大きくて長い開発側ブランチを、常にマージコミットでマージしたいと考えています。マスターブランチからすぐにアクセスできない履歴は必要ありません。

その考えが嫌いです。それは、並行した開発の歴史が無限に行き来できないことを意味します。しかし、私たちがどのような選択肢を持っているのか見ていません。そして、私はかなり困惑しています。これは、優れたブランチ管理について私が知っているほとんどすべてをブロックしているようであり、解決策が見つからない場合は、常にフラストレーションが溜まります。

ここには、サイドブランチを絶えずマージコミットでマスターにマージする以外のオプションがありますか?または、マージコミットを常に使用するのが私が恐れているほど悪くないという理由はありますか?


84
マージをマージとして記録するのは本当に悪いですか?線形の歴史に押しつぶすことには利点があることがわかりますが、そうしないと「良いブランチ管理について知っているほとんどすべて」に反することに驚きます。あなたが恐れている問題は何ですか?
IMSoP 16

65
わかりましたが、私の考えでは、実行時間の長いブランチは可視性必要な場所です。そのため、非難と二分は「2016年のビッグリライトの一部として導入された」だけではありません。答えとして投稿する自信はありませんが、私の本能は、機能ブランチ内タスクを押しつぶす必要があるため、メイン履歴からアクセスできるプロジェクトの短い履歴を持っていることです孤立したブランチをチェックしてください。
IMSoP

5
そうは言っても、「チームメイトよりも高いレベルでgitを使用しようとしています。どうすればそれを台無しにしないようにできますか」という質問になります。正直言ってgit log master+bugfixDec10th、これがブレークポイントになるとは思っていませんでした:-/
スタンバック

26
「長期的なサイドブランチ-マスターとは別のサイドブランチで作業している少数の人々がいる種類で、数か月間開発し、マイルストーンに到達したら2つを同期します。」マスターからサイドブランチに定期的にプルしませんか?多くの場合、すべての(少数の)マスターにコミットすることで、人生がよりシンプルになります。
TafT 16

4
merge --no-ff何をしたいですか?それmaster自体には、ブランチで何が変更されたかを説明する1つのコミットがありますが、すべてのコミットはまだ存在し、HEADの親です。
CAD97

回答:


243

コマンドラインでGitを使用していますが、同僚に同意する必要があります。大きな変更を1つのコミットにまとめるのは賢明ではありません。履歴を目に見えにくくするだけでなく、その方法で履歴を失います。

ソース管理のポイントは、すべての変更の履歴を追跡することです。何がなぜ変わったのですか?そのため、すべてのコミットには、親コミットへのポインター、差分、およびコミットメッセージなどのメタデータが含まれています。各コミットは、ソースコードの状態と、その状態に至るまでのすべての変更の完全な履歴を記述します。ガベージコレクターは、到達できないコミットを削除する場合があります。

リベース、チェリーピッキング、スカッシュなどのアクションは、履歴を削除または書き換えます。特に、結果のコミットは元のコミットを参照しなくなります。このことを考慮:

  • いくつかのコミットをスカッシュし、コミットメッセージで、スカッシュされた履歴が元のコミットabcd123で使用可能であることに注意します。
  • [1] abcd123を含むすべてのブランチまたはタグは、マージされるため削除します。
  • ガベージコレクターを実行します。

[1]:一部のGitサーバーでは、ブランチを誤って削除しないように保護できますが、すべての機能ブランチを永遠に保持したいとは思わないでしょう。

これで、そのコミットを検索できなくなりました-存在しないだけです。

ブランチ名はリポジトリに対してローカルであるため、コミットメッセージでブランチ名を参照するのはさらに悪いことです。master+devFeatureあなたのローカルチェックアウトにあるものは私のものかもしれませんdoodlediduh。ブランチは、あるコミットオブジェクトを指すラベルを移動するだけです。

すべての履歴を書き換える手法の中で、リベースはすべての履歴で完全なコミットを複製し、親のコミットを置き換えるだけなので、最も良性です。

それmasterそれが現実を表しているので、歴史が、それは良いことであるにマージされたすべてのブランチの完全な履歴が含まれています。[2]並行開発があった場合、ログに表示されるはずです。

[2]:このため、線形化された最終的にはリベースに起因する偽の履歴よりも、明示的なマージコミットを好みます。

コマンドラインでgit log、表示された履歴を簡素化し、表示されたすべてのコミットを関連するように努めます。必要に応じて履歴の単純化を調整できます。コミットグラフをたどる独自のgitログツールを作成したいと思うかもしれませんが、「このコミットはもともとこのブランチでコミットされたのか?」と答えることは一般的に不可能です。マージコミットの最初の親は、以前のHEAD、つまり、マージするブランチのコミットです。しかし、それは、マスターから機能ブランチへの逆マージを行わず、その後、マスターをマージに早送りしたことを前提としています。

私が遭遇した長期ブランチの最良の解決策は、数ヶ月後にマージされるブランチを防ぐことです。変更が最近の小さなものである場合、マージが最も簡単です。理想的には、少なくとも週に1回マージします。継続的インテグレーション(「Jenkinsサーバーを設定しましょう」ではなく、エクストリームプログラミングのように)、1日に複数のマージを提案します。つまり、個別の機能ブランチを維持せず、チームとして開発ブランチを共有します。機能がQAされる前にマージするには、機能が機能フラグの後ろに隠れている必要があります。

代わりに、頻繁に統合することにより、潜在的な問題をより早期に発見でき、一貫したアーキテクチャを維持できます。これらの変更はすべてのブランチにすぐに含まれるため、広範囲にわたる変更が可能です。変更によって一部のコードが破損した場合、数か月ではなく、数日間の作業が中断されます。

数百万行のコードと数百または数千のアクティブな開発者がいる場合、歴史の書き換えは本当に巨大なプロジェクトにとって意味があります。なぜこのような大規模なプロジェクトを個別のライブラリに分割するのではなく単一のgitリポジトリにする必要があるのか​​疑問ですが、その規模では、中央リポジトリに個々のコンポーネントの「リリース」のみが含まれる方が便利です。たとえば、Linuxカーネルでは、主な履歴を管理しやすくするために、スカッシュが採用されています。一部のオープンソースプロジェクトでは、gitレベルのマージではなく、パッチをメールで送信する必要があります。


43
@Standback開発者がローカルで何をするかは気にしません…「wip」コミットを使用し、修正された各タイプミスに対して追加のコミットを使用します…。それは結構です、あまりにも頻繁にコミットする方が良いです。理想的には、開発者は、いくつかのタイプミスを修正するすべてのコミットを結合するように、プッシュされる前にそれらのコミットをクリーンアップします。実際の機能と関連するテストのためのコミット、無関係なタイプミスのためのコミットなど、異なることを行う場合は、コミットを個別に保持することをお勧めします。しかし、コミットがプッシュされると、少なくとも私の経験では、履歴を書き換えたり削除したりするのは、それが価値がある以上に面倒です。
アモン

11
「このコミットはもともとこのブランチまたはそのブランチでコミットされたのか?」と答えることは一般に不可能です。「ブランチ」は、gitの最大の設計上の欠陥の1つとして、実際の履歴がないコミットへのポインターにすぎない。私は本当に私のバージョン管理システム「何が日付Y上の分岐Xにあった」と尋ねることができるようにしたい。
ピーター・グリーン

80
を使用してコード内のバグの原因を特定しようとしても、git bisectそれがサイドブランチから10,000行のuber-commitに到達するようにするだけでも、それほどイライラすることはありません。
tpg2114 16

2
@Standback私見では、コミットが小さければ小さいほど読みやすく、フレンドリーです。少数のポイント以上に関係するコミットメントは、最初に把握することは不可能なので、説明は額面どおりに取ります。これは、説明(たとえば「実装された機能X」)の可読性であり、コミット(コード)自体ではありません。「httpをhttpsに変更」のような1文字のコミットを1000回受けたいと思います:)
Agent_L

2
cherry-pickは履歴を書き換えたり削除したりしません。それだけで、既存のコミットから変更を複製し、新たなコミットを作成
Sargeのボルシチ

110

私はAmonの答えが好きですが、私は小さな部分にもっと重点が必要だと感じました: あなたのニーズを満たすためにログを表示しながら簡単に履歴を単純化できますが、他の人はニーズを満たすためにログを表示しながら履歴を追加できません。 これが、発生した履歴を保持することが望ましい理由です。

リポジトリの1つからの例を次に示します。プルリクエストモデルを使用しているため、すべての機能は、通常は1週間以内にしか実行されませんが、履歴内の長期実行ブランチのように見えます。個々の開発者は、マージする前に履歴をつぶすこともありますが、機能の組み合わせが頻繁に行われるため、これは比較的珍しいことです。以下はgitk、gitにバンドルされているguiのコミットのトップです:

標準のgitkビュー

はい、少し複雑ですが、誰がいつ何を変更したかを正確に確認できるため、それも気に入っています。開発履歴を正確に反映しています。一度に1つのプルリクエストマージを行う、より高レベルのビューを表示したい場合、次のビューを見ることができます。これはgit log --first-parentコマンドと同等です。

--first-parentを使用したgitkビュー

git log必要なビューを正確に表示できるように設計されたオプションがさらにあります。 gitk任意のgit log引数を使用してグラフィカルビューを作成できます。他のGUIにも同様の機能があるはずです。git logマージ時にすべてのユーザーに優先ビューを強制するのではなく、ドキュメントを読んで適切に使用することを学んでください。


24
私はこのオプションに慣れていませんでした!これは私にとって大きな助けであり、より多くのログオプションを学習することで、これをより簡単に活用できるように思えます。
スタンドバック16

25
「ニーズに合わせてログを表示しながら履歴を簡単に単純化できますが、ニーズに合わせてログを表示しながら履歴を追加することはできません。」履歴の書き換えは常に疑わしいものです。コミット時に記録することが重要だと思ったら、それは重要でした。あなたがそれが間違っているとわかったとしても、後でそれを歴史の一部である後で再決定しました。いくつかの間違いは、この1行が後の書き直しで残されたと非難できる場合にのみ意味をなします。叙事詩の残りの部分で間違いが折りたたまれている場合、物事がどのように終わったのかをレビューすることはできません。
TafT 16

@Standback関連、私のためにこのような構造を維持するのに役立ちます一つのこと、使用しているmerge --no-ff-ではなく、常にコミットマージを作成し、早送りマージを使用していないので、それは--first-parentで動作するように何かを持っている
Izkata

34

私たちの問題は、長期的なサイドブランチにあります-マスターから分かれるサイドブランチで数人の人々が働いている種類で、数ヶ月間開発し、マイルストーンに到達すると2つを同期します。

私の最初の考えは-絶対に必要でない限り、これさえしないでください。あなたのマージは時々挑戦しなければなりません。ブランチを独立させ、できるだけ短命にします。これは、ストーリーを小さな実装チャンクに分割する必要があることを示しています。

これを行う必要がある場合は、gitで--no-ffオプションを使用してマージし、履歴が独自のブランチで区別されるようにすることができます。コミットはマージされた履歴に引き続き表示されますが、機能ブランチで個別に確認することもできるため、少なくともどの開発ラインに属しているかを判断できます。

gitを初めて使い始めたとき、ブランチコミットがマージ後のメインブランチと同じ履歴に表示されることが少し奇妙であることがわかりました。それらのコミットがその歴史に属しているとは思えなかったので、少し戸惑いました。しかし、実際には、統合ブランチがそれだけであると考える場合、それはまったく痛いものではありません-その全体の目的は機能ブランチを結合することです。私たちのチームでは、つぶすことはせず、頻繁にマージコミットを行います。常に--no-ffを使用して、調査したい機能の正確な履歴を簡単に確認できるようにします。


3
私は完全に同意しています。誰もがマスターの近くに固執することを好みます。しかし、それは別の問題であり、私自身の謙虚な影響の下でははるかに大きく、はるかに少ないです:-P
Standback

2
+1git merge --no-ff
0xcaff 16

1
+1もgit merge --no-ff。gitflowを使用する場合、これがデフォルトになります。
デビッドハンメン

10

あなたのポイントに直接明確に答えさせてください:

私たちの問題は、長期的なサイドブランチにあります-マスターから分かれるサイドブランチで数人の人々が働いている種類で、数ヶ月間開発し、マイルストーンに到達すると2つを同期します。

通常、ブランチを数か月間非同期にしたくないでしょう。

機能ブランチは、ワークフローに応じて何かから分岐しています。master単純にするためにそれを呼び出しましょう。これで、マスターにコミットするたびに、できることとすべきgit checkout long_running_feature ; git rebase masterです。これは、設計上、ブランチが常に同期していることを意味します。

git rebaseここでも正しいことです。それはハックや奇妙なものや危険なものではなく、完全に自然なものです。情報の一部が失われます。これは機能ブランチの「誕生日」ですが、それだけです。誰かがそれを重要であると判断した場合、それを他のどこかに保存することで提供できます(チケットシステム内、または必要性が大きい場合は、git tag...)。

今、私見、これを処理するための自然な方法は、単一のコミットにサイドブランチをつぶすことです。

いいえ、絶対にそれを望んではいません。マージコミットを望みます。マージコミットも「単一コミット」です。どういうわけか、個々のブランチコミットをすべて「マスター」に挿入しません。これはmaster、マージ時のヘッドとブランチヘッドという2つの親を持つ単一のコミットです。

--no-ffもちろん、オプションを必ず指定してください。--no-ffあなたのシナリオでは、せずにマージすることは厳しく禁じられています。残念ながら、--no-ffこれはデフォルトではありません。しかし、それを可能にするオプションを設定できると信じています。git help merge何をするのかを見てください--no-ff(要するに、前の段落で説明した動作をアクティブにします)、それは重要です。

数ヶ月にわたる並行開発をマスターの歴史にさかのぼってダンプしているわけではありません。

絶対にそうではありません。特にマージコミットではなく、ブランチの「履歴に」何かをダンプすることはありません。

そして、もし誰かがサイドブランチの歴史のためにより良い解像度を必要とするなら、もちろん、それはすべてまだそこにあります-それはただマスターではなく、サイドブランチにあります。

マージコミットでは、それはまだ存在しています。マスターではなく、サイドブランチにあり、マージコミットの親の1つとして明確に表示され、必要に応じて永遠に保持されます。

私がやったことを参照してください?あなたがスカッシュのための説明すべてのものは、コミットされている権利がマージして--no-ffコミットします。

問題は次のとおりです。私はコマンドラインのみを使用していますが、残りのチームはGUISを使用しています。

(サイドリマーク:コマンドラインでもほぼ排他的に動作します(まあ、それは嘘です、私は通常emacs magitを使用しますが、それは別の話です-個々のemacsセットアップで便利な場所にいない場合、私はコマンドを好みますラインとしても)。しかし、自分を支持を行うと、少なくとも試してみてくださいgit gui一回。それはあるので、 /取り消しを追加するためのライン、兄貴などを選ぶためにはるかに効率的に追加されます。)

そして、GUISには他のブランチからの履歴を表示するための合理的なオプションがないことを発見しました。

それはあなたがしようとしていることは完全に精神に反しているからgitです。git「有向非循環グラフ」上のコアから構築されます。つまり、コミットの親子関係には多くの情報が含まれます。また、マージの場合、2つの親と1つの子との真のマージコミットを意味します。no-ffマージコミットを使用するとすぐに、同僚のGUIが正常になります。

したがって、「この開発はブランチXYZから押しつぶされた」と言って、スカッシュコミットに到達した場合、XYZの内容を確認するのは非常に苦痛です。

はい、しかしそれはGUIの問題ではなく、スカッシュコミットの問題です。スカッシュを使用すると、機能ブランチの頭がぶら下がり、まったく新しいコミットがに作成されmasterます。これにより、2つのレベルで構造が破壊され、大きな混乱が生じます。

したがって、彼らは、これらの大きくて長い開発側ブランチを、常にマージコミットでマージしたいと考えています。

そして、彼らは絶対に正しい。しかし、彼らは、「マージされません」、彼らはただマージされます。マージは本当にバランスのとれたものであり、他の「に」マージされる優先側がありません(ブランチが入れ替わるなどの視覚的なわずかな違いを除いてgit checkout A ; git merge Bまったく同じです)。git checkout B ; git merge Agit log

マスターブランチからすぐにアクセスできない履歴は必要ありません。

これは完全に正しいです。マージされていない機能がないときは、以前のmasterすべての機能のコミット行をカプセル化した豊富な履歴を持つ単一のブランチがあり、git init時間の初めからコミットに戻ります(特に「その段落の後半にあるブランチ」は、その時点の履歴が「ブランチ」ではなくなったためです。ただし、コミットグラフはかなりブランチになります)。

私はその考えが嫌いです。

使用しているツールに反対して作業しているため、少し苦労します。このgitアプローチは、特に分岐/マージの領域で非常にエレガントで強力です。(特に上記で暗示されているように--no-ff)正しく実行すると、他のアプローチ(たとえば、ブランチ用の並列ディレクトリ構造を持つというsubversionの混乱)よりも飛躍的に向上します。

それは、並行した開発の歴史が無限に行き来できないことを意味します。

無限、並行-はい。

移動不能、もつれ-いいえ。

しかし、私たちがどのような選択肢を持っているのか見ていません。

の発明者のようにgit、あなたの同僚や世界の人々が毎日やっているように、なぜ働きませんか?

ここには、サイドブランチを絶えずマージコミットでマスターにマージする以外のオプションがありますか?または、マージコミットを常に使用するのが私が恐れているほど悪くないという理由はありますか?

他のオプションはありません。それほど悪くない。


10

長期のサイドブランチをつぶすと、多くの情報が失われます。

私がやることは、マスターにサイドブランチをマージする前に、マスターを長期サイドブランチにリベースすることです。そうすることで、コミット履歴を線形で理解しやすくしながら、すべてのコミットをマスターに保持します。

コミットのたびにそれが簡単にできなかった場合、開発コンテキストを明確に保つために、それを非線形にします。私の意見では、マスターからサイドブランチへのリベース中に問題のあるマージがある場合、それは非線形性が現実世界の重要性を持っていたことを意味します。つまり、歴史を掘り下げる必要がある場合に何が起こったのかを理解しやすくなります。また、リベースを行う必要がないという即時の利点も得られます。


:+言及するためrebase。ここでの最善のアプローチであるかどうか(私は個人的にはあまり経験していませんが、あまり使いませんでした)、それは間違いなくOPが本当に望んでいるように見えるものに最も近いものであるようです-具体的には、異常な履歴ダンプとその履歴を完全に隠すことの間の妥協。
カイルストランド

1

個人的には、開発をフォークで行い、次にリクエストをプルしてプライマリリポジトリにマージすることを好みます。

つまり、マスターの上で変更をリベースする場合、またはいくつかのWIPコミットを押しつぶす場合、完全にそれを行うことができます。または、履歴全体をマージするようにリクエストすることもできます。

したいのは、ブランチで開発を行うことですが、頻繁にmaster / devに対してリベースします。こうすることでブランチへのマージコミットの束を持たずに、マスターから最新の変更を取得したり、マスターにマージするときにマージ競合の負荷全体を処理したりする必要がなくなります。

質問に明示的に回答するには:

ここには、サイドブランチを絶えずマージコミットでマスターにマージする以外のオプションがありますか?

はい-ブランチごとに1回マージできます(機能または修正が「完了」の場合)。または、履歴にマージコミットがあるのが気に入らない場合は、最終リベースを行った後、マスターで単純に早送りマージを実行できます。


3
ごめんなさい-私は同じことをしますが、これが質問にどのように答えるかわかりません:-/
スタンバック

2
記載されている質問に対する明確な回答を追加しました。
ウェインワーナー

したがって、それは私自身のコミットにとっては問題ありませんが、私(および私のチーム)はすべての人の作業を処理します。自分の履歴をきれいに保つことは簡単です。乱雑な開発履歴での作業がここの問題です。
スタンドバック16

他の開発者に...何を要求するのですか?リベースせず、マージ時にスカッシュしますか?または、同僚にgit規律の欠如について不満を抱くために、この質問を投稿しただけですか?
ウェインワーナー

1
複数の開発者がその機能ブランチで作業している場合、定期的なリベースは役に立ちません。
パエロエベルマン

-1

リビジョン管理はガベージインガベージアウトです。

問題は、機能ブランチで進行中の作業が多くの「これを試してみましょう...それは機能しませんでした、それでそれを置き換えましょう」と最後の「それ」を除くすべてのコミットがちょうど終わることができるということです履歴を無駄に汚染してしまう。

最終的に、履歴は保持する必要があります(将来の一部は将来使用される可能性があります)が、「クリーンコピー」のみをマージする必要があります。

Gitを使用すると、最初に機能ブランチを分岐し(すべての履歴を保持するため)、次に(インタラクティブに)機能ブランチのブランチをマスターからリベースし、リベースされたブランチをマージします。


1
明確にするために:あなたが言っているのは、機能ブランチ(のコピー)の履歴を書き換えることです。それにより、短くてきれいな履歴ができます-初期開発のすべてのやり取りを排除します。そして、書き直されたブランチをmasterにマージする方が簡単できれいです。私はあなたを正しく理解しましたか?
スタンドバック16

1
はい。そして、マスターにマージするとき、それはただ早送りになります(マージする前に最近リベースしたと仮定します)。
DepressedDaniel

しかし、この歴史はどのように保持されますか?元の機能のブランチを横たわらせますか?
パエロエベルマン

@PaŭloEbermannビンゴ。
DepressedDaniel

さて、他の誰かがコメントで言ったように:ブランチはの履歴グラフへの単なるポインタgitであり、ローカルです。何という名前のfoo1つのレポにすると命名することができるbar別の1に。そして、それらは一時的なものであることを意図しています:履歴を失うことを避けるために生き続ける必要がある何千もの機能ブランチでレポを散らかしたくありません。そして、参照を履歴に保持するためにこれらのブランチを保持する必要がありgitます。ブランチによって到達できなくなったコミットは最終的に削除されます。
cmaster 16
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.