多くのプロジェクトが「git merge」よりも「git rebase」を好むのはなぜですか?


68

DVCSを使用する利点の1つは、edit-commit-mergeワークフローです(多くの場合、CVCSによって施行されるedit-merge-commitを超える)。マージに関係なく、固有の各変更をリポジトリに記録できるようにすることで、DAGがプロジェクトの真の血統を正確に反映するようになります。

なぜこれほど多くのWebサイトが「マージコミットを回避する」ことについて話しているのですか?事前コミットまたはマージ後のリベースをマージすると、リグレッションの分離、過去の変更の取り消しなどが難しくなりませんか?

明確化のポイント: DVCSのデフォルトの動作があるコミットをマージ作成します。なぜそんなに多くの場所が、これらのマージコミットを隠す線形の開発履歴を見たいという願望について語っているのでしょうか?


18
ツールが常に正しいとは限りません。そして、彼らが間違えたとき、ああ、彼らはそれを間違えますか。
-Oded

2
@Odedは、自動マージコミット(などによって作成されたものgit pull)を参照していますか?なぜそれが問題になるのかはわかりますが、私の質問はすべてのマージコミットに関するものです。
ジェイスブラウニング

可能性の重複が頻繁に複雑なマージは、問題のサインコンフリクトていますか?「マージとリベースは、人間が解決しなければならない固有の競合(つまり、2人の開発者が同じコード行を変更する)に対して、まったく同じ競合を引き起こすはずです...」
gnat

回答を投稿した後、IMOが回答として正確に適合しないと思った:( git pull --rebase == svn up厳密な
等式で

SourceSafeのような一元化されたストアでは、継続的な努力で変更がチェックインされ続けますが、安定したプラトーに達したら、特定の日付の時点ですべてのファイルにラベルを付けます。そうすれば、少なくとも、そのようなバージョンの前または後のいずれかにバグを関連付けるか、またはラインをリベースできます。
アンディズスミス

回答:


64

ログをよりきれいにするため、マージコミットは避けたいと考えています。真剣に。彼らが育った一元化されたログのように見え、ローカルでは単一のブランチですべての開発を行うことができます。これらの美学以外の利点はありません。また、「中央」サーバーを経由せずに同僚から直接引き寄せることが競合しやすいなど、あなたが言及したものに加えていくつかの欠点があります。


5
あなたは私の質問を正しく理解した唯一の人です。どうすればより明確にできますか?
ジェイスブラウニング

12
「線形開発履歴の利点は何ですか?」に言い換えることができます。
カールビーレフェルト

11
「多くの場合、これは、コミットをリモートブランチ(おそらく、貢献しようとしているが維持していないプロジェクト)に確実に適用されるようにするために行います。この場合、作業を行います。メインプロジェクトにパッチを提出する準備ができたら、ブランチで作業をオリジン/マスターにリベースします。そうすれば、メンテナーは統合作業を行う必要がありません。 」git-scm.com/book/en/Git-Branching-Rebasing
Andyz Smith

12
見た目だけのように聞こえますが、実際には実用的です。線が交差してマージする複雑なDAGよりも、線形の履歴で何が起こっているかを理解する方がはるかに簡単です。
ダニエルハーシュコヴィッチ

20
「これらの美学以外に利点はありません」—私はそれについて知りません。あなたの歴史が回路基板のように見えるとき、あなたの歴史で何が起こっているのかを理解するのは本当に難しいです。(それぞれが独自のブランチとワークフローを持つ複数のチームメンバーを想定しています。)
Archagon

46

2つの言葉で: git bisect

線形履歴により、バグの実際の原因を特定できます。


例。これが最初のコードです:

def bar(arg=None):
    pass

def foo():
    bar()

ブランチ1は、arg無効になったリファクタリングを実行します。

def bar():
    pass

def foo():
    bar()

ブランチ2には、使用する必要がある新しい機能がありますarg

def bar(arg=None):
    pass

def foo():
    bar(arg=1)

マージの競合はありませんが、バグが導入されています。幸いなことに、この特定のものはコンパイルの段階でキャッチされますが、私たちは必ずしもそれほど幸運ではありません。バグがコンパイルエラーではなく予期しない動作として現れる場合、1〜2週間は見つからない可能性があります。その時点でgit bisect、救助に!

やばい。これは見たものです:

(master: green)
|             \_________________________
|                \                      \
(master: green)  (branch 1: green)     (branch 2: green)
|                 |                     |
|                 |                     |
(master/merge commit: green)            |
|                         ______________/
|                        /
(master/merge commit: red)
|
...days pass...
|
(master: red)

したがってgit bisect、ビルドを中断したコミットを見つけるために送信すると、マージコミットが特定されます。まあ、それは少し役立ちますが、それは本質的に単一のコミットではなく、コミットのパッケージを指しています。すべての祖先は緑です。一方、リベースでは、線形の履歴が得られます:

(master: green)
|
(master: green)
|
(all branch 1 commits: green)
|
(some branch 2 commits: green)
|
(branch 2 commit: red)
|
(remaining branch 2 commits: red)
|
...days pass...
|
(master: still red)

さて、git bisectビルドを壊した正確な機能のコミットを指し示すつもりです。理想的には、コミットメッセージは、別のリファクタリングを行ってすぐにバグを修正するのに十分な意図を説明します。

メンテナーがすべてのコードを作成しなかった場合、その効果は大規模プロジェクトでのみ悪化するため、特定のコミットが行われた理由/各ブランチの目的を必ずしも覚えていません。そのため、正確なコミットを正確に特定する(そして、その周りのコミットを調べることができる)ことは大きな助けになります。


とはいえ、私は(現在)まだマージを好んでいます。リリースブランチにリベースするとgit bisect、日々の作業の真の履歴を保持しながら、で使用する線形履歴が得られます。


2
この説明をありがとう。まだマージを好む理由を詳しく説明できますか?多くの人がほとんどの状況でリベースよりもマージを好むことを知っていますが、その理由を理解することはできませんでした。
フィリップ

@Philip少し個人的なプロジェクトでgitを使用したのはチームではなく、数か月間だけだったので、そうするのは少しためらっています。しかし、最近やったことを追跡するとき、ブランチ間のコミットの流れを見ることができることgitk --allは非常に便利です(特に私は通常3つのブランチをいつでも持っているので、気分に応じて毎日切り替えます時間)。
イズカタ

16
しかし、マージが問題なので、結果としてマージコミットが必要ですbisect。これは、個人がでコミット見つけることは容易で十分ですblameか異なるbisectあなたは深く掘るしたい場合。履歴が線形であるため、マージは帳簿外で行われるため、問題のあるマージが問題であると判断することはできません。特に、引数が以前のいくつかのコミットで削除された場合、存在しない引数を使用するばかみたいに見えます。あなたが歴史を破壊するとき彼がそれをする理由を理解しようとすることははるかに困難です。
カールビーレフェルト

1
この答えに本当に反対します。リベースは、時々bisectのユーティリティを破壊します。リベースのヘッドコミットを除き、リベースからのコミットは実行できない場合があります。例:Aで分岐し、B、C、Dを作成し、誰かがEをプッシュしました。あなたはBとCであり、Eにリベースしているため、BとCは実行不能または実行可能であり、動作しません。ベスト場合、あなたが実際にそれがDであるとき、BまたはCに問題が含まれていると思うあなたはあきらめている、最悪の場合

4
@Izkataなぜbisectを使用しているのですか?バグ/間違いが起こるからです。それは謙虚な現実です。「マウスを左にドラッグする」ことは、以前は自動化された/手動のテストリストの一部ではなかったかもしれません。
蘭14年

17

要するに、マージは多くの場合、何かがうまくいかない別の場所であり、再度対処することを人々に非常に恐れさせるために一度だけ間違っていればよいからです(一度噛んだら2回は恥ずかしがります)。

そこで、新しいアカウント管理画面で作業しているとしましょう。新しいアカウントのワークフローでバグが発見されました。OK、2つの個別のパスを使用します-アカウント管理を終了し、新しいアカウントでバグを修正します。私たちは両方ともアカウントを扱っているため、非常によく似たコードを扱ってきました。おそらく、同じコードを調整する必要さえありました。

さて、現時点では、ソフトウェアの2つの異なるが完全に機能するバージョンがあります。私たちは両方とも、変更に対してコミットを実行し、コードを忠実にテストしました。そして、独立して、素晴らしい仕事をしたと確信しています。それで?

さて、マージする時が来ましたが......がらくた、今何が起こっていますか?2つのソフトウェアの作業セットから、アカウント管理が機能せず、新しいアカウントが破損し、古いバグがまだ存在するかどうかさえわからない、新しくバグの多いソフトウェアの統合された恐ろしい破損部分に非常にうまく行くことができます。

たぶん、ソフトウェアはスマートで、競合があると言って、ガイダンスを出すように主張したのかもしれません。さて、たわごと-私はそれをするために座って、あなたが私がすぐに理解しない複雑なコードを追加したのを見る 私が行った変更とは矛盾すると思います...私はあなたに尋ねます、そしてあなたが時間を得るとあなたがチェックし、あなたが理解していない私のコードを見ます。私たちのどちらかまたは両方は、座って適切なマージをハッシュし、場合によってはダング全体を再テストして、壊れていないことを確認する必要があります。

一方、他の8人は全員サディストのようにコードをコミットしています。マージの競合が発生する前に、いくつかの小さなバグ修正を行い、それらを提出しました。午後休み、会議などで立ち往生しています。たぶん私は休暇を取るべきです。または、キャリアを変えます。

そして、この悪夢から逃れるために、一部の人々はコミットメントを非常に恐れています(他に何が新しいのですか?)。このようなシナリオでは、自然にリスクを回避します-吸うと考えて、とにかくそれを台無しにしようとしない限り、その場合、人々は無謀な放棄で行動し始めます。はぁ

それであなたは行き​​ます。はい、現代のシステムはこの苦痛を緩和するように設計されており、簡単にバックアウトしてリベースおよびディベース、フリーベースおよびハングライドなどを行うことができるはずです。

しかし、それはすべての作業であり、フォークを見つける時間がある前に、電子レンジのボタンを押して4コースの食事を済ませたいだけで、すべてが非常に満たされていないように感じます-コードは仕事であり、生産的です意味がありますが、マージを適切に処理することは重要ではありません。

プログラマーは、原則として、優れた作業メモリを開発する必要があり、その後、問題を解決し、マージの競合を処理するとすぐに、そのジャンクと変数の名前とスコープをすべて忘れてしまいます。誤って処理されたマージ)は、死亡率を思い出させるための招待状です。


5
驚いたことに、先生!
サウスポーヘア

10
これは、なぜ人々がマージを恐れているのかを答えており、多くの人がマージコミットを悪いと考える理由ではありません。
ジェイ

23
この答えの問題は、これがリベースにも当てはまることです。結局、リベースはまだ、既に変更されたコード行を変更したマージを行っています。これは、質問が述べられた方法の問題だと思います。
-deworde

1
私はこの答えを否定しました。なぜならそれはまだ雄弁であるので、それが意味を超えているからです。
ユージン

5
これは、リベースとマージ/マージではまったくヒットしないようです。
アンディズスミス

5

リベースは、変更をベースラインに戻すプロセスを簡素化する移動分岐点を提供します。これにより、長時間実行されているブランチをローカルの変更であるかのように扱うことができます。リベースせずに、ブランチはベースラインからの変更を蓄積し、ベースラインにマージされる変更に含まれます。

マージすると、ベースラインは元の分岐点に残ります。分岐した行から数週間分の変更をマージすると、分岐点から多くの変更があり、その多くがベースラインに含まれます。これにより、ブランチでの変更を特定することが難しくなります。変更をベースラインに戻すと、変更とは無関係の競合が発生する場合があります。競合の場合、一貫性のない変更をプッシュすることができます。進行中のマージは管理に手間がかかり、変更を緩めるのは比較的簡単です。

Rebaseは、分岐点をベースラインの最新リビジョンに移動します。衝突が発生した場合は、変更のみが原因となります。変更のプッシュははるかに簡単です。競合は、追加のリベースを行うことでローカルブランチで処理されます。競合するプッシュの場合、変更をプッシュする最後のプッシュは、変更に関する問題を解決する必要があります。


1
そのため、新しい変更をレベルに降格するだけでそれらが唯一の変更であるため「間違ったもの」と見なされます。すべての古い変更がマージに含まれている場合、すべての変更は、今日の「正しいこと」であるかどうかに関して、一定の立場にあります。
アンディズスミス

@AndyzSmith新しい変更を間違っているとは思わないでしょう。何が変更され、プッシュされるべきかを判断しようとするとき、それらは正しいことです。リベースしたら、ベースラインの一部である古い変更を無視できます。
BillThor

正しいので、ベースラインが何であるかわからない場合、つまり、マージする準備がすでに整っているのが「正しいこと」である場合は、リベースしないでください。
アンディズスミス

そして、誰かから変更のプッシュを受け取り、それらの関数プロトタイプが既存のプロトタイプと一致しないためにビルドが中断した場合、リベースのために、新しい人が間違っていることがすぐにわかります。おそらく新しい人が正しいプロトタイプを持っていると推測する必要はありません。以前の、リベースされていない、適用されていない変更セットは間違ったプロトタイプを持っているものです。
アンディズスミス

4

自動化ツールは、マージコードのコンパイルと実行を確実にすることで改善されており、構文の競合を回避していますが、マージによって生じる可能性のある論理的な競合がないことを保証できません。したがって、「成功した」マージは、実際には何も保証しない場合に、あなたに誤った自信の感覚を与えます。そして、すべてのテストをやり直さなければなりません。

私が見るように、分岐とマージの本当の問題は、それがことわざを追い払うことです。1週間「自分の小さな世界で仕事をする」と言って、後で発生する問題に対処できます。ただし、バグの修正は、新しい場合は常に高速/安価です。すべてのコードブランチがマージされ始める頃には、すでに行われた作業のニュアンスの一部を忘れているかもしれません。

上記の2つの問題をまとめると、アクティブな開発が少し遅くなったとしても、同じトランクで作業をし、競合が発生するたびに継続的に解決する方が簡単で簡単な状況に陥ることがあります。


4
これは、なぜ人々がマージを恐れているのかを答えており、多くの人がマージコミットを悪いと考える理由ではありません。
ジェイ

それで、あなたが参照するこのトランクは、リベースを伴いますか?説明してください。
アンディズスミス

@AndyzSmith「トランク」はgitの「マスター」ブランチに相当するsvnの用語です
-Izkata

@Izkataだから、この回答は、マージもリベースも使用しないことを主張していますか?
アンディズスミス

@AndyzSmithええ、それも私が読んだ方法です。そして、私はそれに反対します。提案された方法で他の7人の開発者とチームで作業した後、私は提案しません。機能ブランチは私たちが使用した以上に使用する必要がありましたが、それらのほとんどはマージを恐れています(BrianDHallが彼の答えで説明しているように)。
イズカタ

0

追加の関連ポイントは次のとおりです。リベースすると、リリースブランチの機能を簡単に選択または元に戻すことができます。


1
これについて詳しく説明しますか?
アカヴァル

確かに:-) gitフローでは、定期的に新しいリリースブランチを作成します。作成後、開発ブランチでバグが修正された場合、それを使用git cherry-pick -x <commit>してリリースブランチに適用します。または、リリースで何かを元に戻したい場合がありgit revert <commit>ます。<commit>マージの場合、毛が生えます。それはある私の知る限りでは可能、しかし行うのは簡単ではありません。リベースを使用する場合、すべての「マージ」は1つの巨大でありながら定期的なコミットであり、簡単にcher-pickableおよびrevertableです。
ブルーノシェーパー

0

いずれの回答でも次の3つのことは言われていません。

  • ブランチ内での違い:

    • マージコミットがある場合、ブランチ内の任意のコミットペアを区別することは非常に困難になります。
  • 一度に1つのアイテムをマージする:

    • 2つのブランチの違いを解決する場合、通常、マージは一度にすべて発生し、マージの競合は、特定のコミットが競合の原因となったコンテキストなしで行う必要があります。リベースすると、競合が発生した時点でリベースが停止し、そのコンテキストで解決できます。
  • プッシュ前のクリーンアップ:

    • 後で修正する必要があるコミットを間違えた場合、インタラクティブリベースをプッシュしていない場合は、コミットを結合/分割/変更/移動できます。マージした場合でもそれを行うことができますが、マージ境界を越えて結合/分割/変更/移動したい場合は非常に困難になります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.