web-dev-qa-db-ja.com

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

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

私たちの問題は、長期的なサイドブランチにあります-マスターから分岐したサイドブランチを数人で作業しているような場合、私たちは数か月間開発し、マイルストーンに達すると、2つを同期します。

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

ここに問題があります。私はコマンドラインのみを使用して作業していますが、チームの他のメンバーはGUIを使用しています。また、他のブランチからの履歴をdisplayするための合理的なオプションがGUISにないことを発見しました。したがって、「この開発はブランチXYZから押しつぶされました」と言ってスカッシュコミットに到達した場合、何が行われているかを確認するのは大変ですXYZ

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

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

嫌いその考え;それは、並行した開発の歴史の無限の、ナビゲートできないもつれを意味します。しかし、私にはどのような選択肢があるのか​​わかりません。そして、私はかなり困惑しています。これは、私が優れたブランチ管理について知っているほとんどすべてを遮断するようであり、解決策を見つけることができない場合、私は常に不満を感じるでしょう。

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

120
Standback

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

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

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

  • いくつかのコミットを押しつぶし、押しつぶされた履歴が元のコミットabcd123で利用できることをコミットメッセージで確認します。
  • 削除します[1] マージされるため、abcd123を含むすべてのブランチまたはタグ。
  • ガベージコレクターを実行します。

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

現在、そのコミットを検索することはできません。単に存在しません。

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

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

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

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

コマンドラインで git logは、表示された履歴を簡略化するために懸命に努力します と表示されたすべてのコミットを適切に保ちます。ニーズに合わせて履歴の簡略化を調整できます。コミットグラフをたどる独自のgitログツールを作成したくなるかもしれませんが、「このコミットはこのブランチまたはそのブランチで最初にコミットされたのですか?」と答えることは一般に不可能です。マージコミットの最初の親は、以前のHEAD、つまりマージ先のブランチのコミットです。ただし、マスターから機能ブランチへのリバースマージを行わず、その後マスターをマージに早送りしなかったと想定しています。

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

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

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

245
amon

私は アモンの答え が好きですが、1つの小さな部分をさらに強調する必要があると感じました:ニーズを満たすためにログを表示しながら履歴を簡単に簡略化できますが、他のユーザーはログを表示しながら履歴を追加できません彼らのニーズ。 これが、発生した履歴を保持することが望ましい理由です。

以下は、私たちのリポジトリの1つからの例です。プルリクエストモデルを使用しているため、every機能は、通常1週間以内しか実行されない場合でも、履歴内で長時間実行されているブランチのように見えます。個々の開発者は、マージする前に履歴を潰すことを選択することがありますが、多くの場合、機能を組み合わせて使用​​するため、これは比較的珍しいことです。以下は、gitにバンドルされているgitkのトップコミットです。

standard gitk view

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

gitk view with --first-parent

git logには、必要なビューを正確に提供するために設計された多くのオプションがあります。 gitkは、任意のgit log引数を使用して、グラフィカルビューを構築できます。他のGUIにも同様の機能があると思います。マージ時に全員に優先git logビューを強制するのではなく、ドキュメントを読んで適切に使用する方法を学びます。

110
Karl Bielefeldt

私たちの問題は、長期的なサイドブランチにあります-マスターから分岐したサイドブランチを数人で作業しているような場合、私たちは数か月間開発し、マイルストーンに達すると、2つを同期します。

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

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

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

34
Brad Thomas

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

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

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

10

ポイントを直接かつ明確に回答させてください:

私たちの問題は、長期的なサイドブランチにあります-マスターから分岐したサイドブランチを数人で作業しているような場合、私たちは数か月間開発し、マイルストーンに達すると、2つを同期します。

通常、ブランチを数か月間同期させないようにします。

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

git rebaseもここで行うのに適切です。それはハックや奇妙な、または危険なものではありませんが、完全に自然です。機能ブランチの「誕生日」である1ビットの情報を失いますが、それだけです。誰かがそれが重要であるとわかった場合、それを別の場所(チケットシステム内、または、必要に応じてgit tag...)に保存することで提供できます。

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

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

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

私たちは、何ヶ月にもわたる並行開発をさかのぼってマスターの履歴に捨てているわけではありません。

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

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

マージコミットを使用しても、まだ存在します。マスターではなく、サイドブランチで、マージコミットの親の1つとして明確に表示され、本来あるべき状態で永遠に保持されます。

私がやったことを見ますか?スカッシュコミットについて説明するのは、マージ--no-ffコミットを使用したすぐそこです。

ここに問題があります。私はコマンドラインのみを使用して作業していますが、チームの他のメンバーはGUIを使用しています。

(サイドリマーク:私はほぼ独占的にコマンドラインでも作業します(まあ、それは嘘です、私は通常emacs magitを使用しますが、それは別の話です-私が個々のemacsセットアップで便利な場所にいない場合は、コマンドを好みます行も同様です。ただし、好意的にやり、少なくともgit guiを1回は試してください。追加や追加の取り消しを行うために、行やハンクなどを選択する場合はsoより効率的です。)

また、GUIには他のブランチからの履歴を表示するための適切なオプションがないことがわかりました。

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

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

はい、しかしそれはGUIの問題ではなく、スカッシュコミットの問題です。スカッシュを使用するということは、機能ブランチのヘッドをぶら下げたままにし、まったく新しいコミットをmasterに作成することを意味します。これは2つのレベルで構造を壊し、大きな混乱を引き起こします。

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

そして、それらは完全に正しいです。しかし、それらは「マージされたin」ではなく、単にマージされただけです。マージは本当にバランスの取れたものであり、もう一方に「マージ」される優先サイドはありません(git checkout A ; git merge Bは、git checkout B ; git merge Aなどでブランチが交換されるなどの小さな視覚的な違いを除いて、git logとまったく同じです)。

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

それは完全に正しいです。マージされていない機能がない場合、単一のブランチmasterに豊富な履歴があり、これまでに存在したすべての機能コミット行をカプセル化し、最初からgit initコミットに戻ります(注意:コミットグラフはかなり枝分かれしますが、その時点での履歴はもはや「ブランチ」ではないため、その段落の後半で「ブランチ」という用語を使用することは特に避けました。

その考えは嫌いです。

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

それは、並行した開発の歴史の無限の、ナビゲートできないもつれを意味します。

エンドレス、パラレル-はい。

ナビゲートできない、もつれ-いいえ。

しかし、私にはどのような選択肢があるのか​​わかりません。

gitの発明者のように、同僚や世界中の人々が毎日同じように働いてみませんか?

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

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

10
AnoE

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

つまり、変更をマスターの上にリベースしたい場合、またはWIPコミットをスカッシュしたい場合、それを完全に実行できます。または、私の履歴全体をマージするように要求することもできます。

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

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

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

はい-ブランチごとに1つマージできます(機能または修正が「完了」した場合)。または、履歴にマージコミットを保存したくない場合は、最後のリベースを実行した後、マスターで早送りマージを実行できます。

1
Wayne Werner