web-dev-qa-db-ja.com

いつgit mergeの代わりにgit rebaseを使いますか?

git rebasegit mergeの使用が推奨されるのはいつですか?

リベースが成功した後もマージする必要がありますか?

1382
Coocoo4Cocoa

短縮版

  • マージは1つのブランチですべての変更を取得し、それらを1つのコミットで別のブランチにマージします。
  • Rebaseは、分岐点を新しい出発点に移動したいと言っています

それで、あなたはいつどちらを使いますか?

マージ

  • 単一の機能を開発する目的でブランチを作成したとしましょう。これらの変更をマスターに戻したいときは、おそらく merge が必要です(すべての暫定コミットを維持することを気にする必要はありません)。

リベース

  • もう1つのシナリオは、開発を開始してから別の開発者が無関係な変更を加えた場合です。現在のバージョンからの変更をリポジトリから取得するためには、おそらくプルしてから rebase を使用する必要があります。
1048
Rob Di Marco

それは簡単です、あなたがあなたの仕事のために新しい base として別のブランチを使うことをあなたが言うリベースで。

たとえばブランチmasterがあり、新しい機能を実装するためにブランチを作成する場合、それにcool-featureと名前を付けます。もちろん、マスターブランチはあなたの新しい機能のベースです。

さて、ある時点でmasterブランチに実装した新機能を追加したいと思います。 masterに切り替えてcool-featureブランチをマージするだけです。

$ git checkout master
$ git merge cool-feature

しかしこのようにして新しいダミーコミットが追加されます、もしあなたがspaghetti-historyを避けたいのであれば rebase

$ git checkout cool-feature
$ git rebase master

そしてそれをmasterにマージします。

$ git checkout master
$ git merge cool-feature

今回は、トピックブランチはmasterのコミットと新しい機能のコミットを同じにしているので、マージは早送りになります。

自分の答え 言及 TSamperによる を補完するために、

  • マージする前にブランチYの作業をブランチBの作業に統合するという考えがあるため、マージの前にリベースを行うことは非常に良いことです。
    繰り返しますが、マージする前に、yourブランチの競合を解決します(つまり、「最近のブランチから私のブランチで作業をリプレイする」のように「リベース」します)ブランチからのポイントB
    正しく実行された場合、ブランチからブランチBへの後続のマージは早送りできます。

  • マージは、宛先ブランチBに直接影響します。つまり、マージはより簡単になります。そうでない場合、ブランチBは安定状態に戻るまでに長くなる可能性があります(すべての競合を解決する時間)


リベース後のマージのポイント?

説明する場合、Bをブランチにリベースします。これは、Bからのより最近のポイントから作業をリプレイする機会を得るためですが、ブランチにとどまります。
この場合、「リプレイ」された作品をBに持ち込むには、まだマージが必要です。

他のシナリオ( 例:Git Ready )は、リベースを介してBに直接作業を持ち込むことです(これにより、すべてのNiceコミットが保存されるか、それらを並べ替える機会さえ与えられます)インタラクティブなリベースを介して)。
その場合(Bブランチにいる間にリベースする場合)、あなたは正しいです:それ以上のマージは必要ありません:

マージもリベースもしていない場合のデフォルトのgitツリー

rebase1

リベースにより取得:

rebase3

その2番目のシナリオは、新しい機能をマスターに戻す方法です。

私のポイントは、最初のリベースシナリオを説明することで、リベースはその予備ステップ(「新しい機能をマスターに戻す」)としても使用できることを全員に思い出させることです。
リベースを使用して、最初にマスターを新機能のブランチに「入れる」ことができます。リベースは、HEAD masterから新機能のコミットを再生しますが、まだ新機能のブランチにあり、ブランチの開始点を効果的に移動しますHEAD-masterへの古いマスターコミット。
これにより、yourブランチでの競合を解決できます(つまり、単独で、競合解決ステージに時間がかかりすぎる場合にマスターが並行して進化し続けることができます) )。
次に、マスターに切り替えてnew-featureをマージします(またはnew-featureブランチで行われたコミットを保持する場合は、new-featuremasterにリベースします)。

そう:

  • 「リベースとマージ」は、作業をインポートする2つの方法、たとえばmasterと見なすことができます。
  • ただし、「リベースしてからマージ」は、最初に競合を単独で解決してから作業を元に戻すための有効なワークフローです。
262
VonC

ここでの回答の多くは、マージするとすべてのコミットが1つになるため、リベースを使用してコミットを保存することをお勧めします。 これは誤りです。そして、すでにコミットをプッシュしている場合は悪い考えです

Mergeはコミットを消去しませんnot。 Mergeは履歴を保存します! (gitkを見てください)Rebaseは履歴を書き換えます。これはプッシュした後の悪いことです。

すでにプッシュした場合は、rebaseではなくmergeを使用してください

ここにLinus '(gitの著者)がいます 。本当に良い読み物です。または、以下の同じアイデアの自分のバージョンを読むことができます。

マスターでブランチをリベースする:

  • コミットの作成方法に関する誤った考えを提供します
  • 十分にテストされていない可能性のある多数の中間コミットでマスターを汚染する
  • 元のトピックブランチが作成されてからリベースされるまでの間にマスターに加えられた変更により、実際にこれらの中間コミットにビルドブレークが導入される可能性があります。
  • マスターの良い場所を見つけてチェックアウトするのが難しくなります。
  • コミットのタイムスタンプがツリー内の時系列順に整列しないようにします。そのため、マスターではコミットAがコミットBに先行しますが、コミットBが最初に作成されたことがわかります。 (何?!)
  • トピックブランチ内の個々のコミットには、それぞれ個別に解決する必要のあるマージ競合が含まれる可能性があるため、より多くの競合が発生します(さらに、各コミットで発生したことについての履歴があります)。
  • 歴史の書き直しです。リベースするブランチがどこかにプッシュされた場合(自分以外の人と共有されている場合)、履歴を書き直してからそのブランチを持っている他の全員を台無しにしました。

対照的に、トピックブランチをマスターにマージする:

  • トピックブランチが作成された場所の履歴を保持します。これには、マスターからトピックブランチへのマージを含め、最新の状態を維持するのに役立ちます。開発者がビルド時にどのコードを使用していたのかを正確に把握できます。
  • masterは大部分がマージで構成されるブランチであり、これらのマージコミットは通常、トピックブランチを統合する準備ができているため、チェックアウトしても安全な履歴の「良い点」です。
  • トピックブランチにあったという事実を含め、トピックブランチのすべてのコミットが保持されるため、これらの変更を分離するのは自然であり、必要に応じてドリルインできます。
  • マージの競合は(マージの時点で)1回だけ解決する必要があるため、トピックブランチで行われた中間コミットの変更を個別に解決する必要はありません。
  • 複数回スムーズに行うことができます。トピックブランチをマスターに定期的に統合する場合、人々はトピックブランチ上にビルドを続けることができ、トピックブランチは独立してマージされ続けることができます。
171
Andrew Arnott

TL、DR

疑問がある場合は、マージを使用してください。

短い答え

リベースとマージの唯一の違いは次のとおりです。

  • 結果として得られる履歴のツリー構造(一般にコミットグラフを見たときにだけ顕著になります)は異なります(一方には枝があり、他方にはありません)。
  • マージは通常、追加のコミットを作成します(例:ツリー内のノード)。
  • マージとリベースは競合を異なる方法で処理します。 Rebaseは一度に1つのコミットを提示し、マージはそれらをまとめて提示します。

つまり、簡単な答えは履歴の表示形式に基づいてリベースを選択するかマージするです。

長い答え

どの操作を使用するかを選択するときに考慮する必要があるいくつかの要因があります。

変更を加えたブランチは、チーム外の他の開発者(オープンソース、パブリックなど)と共有されますか?

もしそうなら、リベースしないでください。 Rebaseはブランチを破壊し、それらの開発者はgit pull --rebaseを使わない限り壊れた/矛盾したリポジトリを持つことになります。これは他の開発者を素早く動揺させる良い方法です。

あなたの開発チームはどれほど熟練していますか?

リベースは破壊的な操作です。つまり、正しく適用しないと、 コミットされた作業が失われたり、他の開発者のリポジトリの一貫性が失われたりする可能性があります。

私は、企業が分岐とマージに対処するために専任スタッフを雇うことができる時代から、開発者全員が参加するチームに取り組んできました。それらの開発者はGitについてあまり知らないし、あまり知りたくありません。これらのチームでは、私は何らかの理由でリベースを推奨するリスクはありません。

ブランチ自体は有用な情報を表していますか

一部のチームは、各ブランチが機能(またはバグ修正、またはサブ機能など)を表す機能ごとのブランチモデルを使用します。このモデルでは、ブランチは関連するコミットのセットを識別するのに役立ちます。たとえば、そのブランチのマージを元に戻すことで機能をすばやく元に戻すことができます(公平に言えば、これは稀な操作です)。または2つのブランチを比較してフィーチャを比較する(より一般的)。 Rebaseはブランチを破壊するでしょう、そしてこれは簡単ではないでしょう。

私は、開発者一人当たりブランチモデルを使ったチームにも取り組んできました(私たちは皆そこにいました)。この場合、ブランチ自体は追加情報を伝えません(コミットはすでに作者を持っています)。リベースに害はありません。

何らかの理由でマージを元に戻しますか?

差し戻しを元に戻す(元に戻す)のと同様に、差し戻しを元に戻すことは、差し戻しを元に戻すことと比較して、かなり困難および/または不可能です。もしあなたが元に戻ろうとしている可能性があると思うなら、マージを使用してください。

あなたはチームで働いていますか?もしそうなら、あなたはこのブランチで全か無かのアプローチを取る気がありますか?

リベース操作は対応するgit pull --rebaseで引っ張られる必要があります。あなたが自分で仕事をしているなら、あなたは適切な時にあなたがどれを使うべきかを思い出すことができるかもしれません。あなたがチームで作業しているなら、これは調整するのが非常に難しいでしょう。これが、ほとんどのリベースワークフローがすべてのマージにリベース(およびすべてのプルにgit pull --rebase)を使用することを推奨する理由です。

共通の神話

マージにより履歴が破棄されます(コミットを縮小)

次のようなマージがあるとします。

    B -- C
   /      \
  A--------D

マスターブランチ(A - D)のみのログを見るとBとCに含まれる重要なコミットメッセージを見逃すことになるので、マージはコミット履歴を「破壊する」と言う人もいます。

これが本当なら、私たちは this のような質問はないでしょう。基本的に、( - first-parentを使って)明示的に見ないように頼まない限り、BとCを見ることになります。 。

Rebaseはより安全で簡単なマージを可能にします

2つのアプローチは異なる方法でマージされますが、一方が他方より常に優れていることは明確ではなく、開発者のワークフローに依存する可能性があります。たとえば、開発者が定期的にコミットする傾向がある場合(たとえば、仕事から家に移るときに1日に2回コミットするなど)、特定のブランチに対して多くのコミットが発生する可能性があります。これらのコミットの多くは最終製品のようには見えないかもしれません(私は私のアプローチを機能ごとに1、2回リファクタリングする傾向があります)。他の誰かがコードの関連する領域に取り組んでいて、彼らが私の変更をリベースしようとした場合、それはかなり面倒な操作になるかもしれません。

Rebaseは、よりクール/セクシー/プロフェッショナルです

「時間を節約する」ためにrmrm -rfにエイリアスしたい場合は、おそらくリベースが必要です。

私の二セント

いつの日か、私はgit rebaseが問題を解決する素晴らしいツールであるというシナリオに出会うことになると常に思います。 git reflogが私の問題を解決する素晴らしいツールであるというシナリオに出くわすと私が思うのと同じように。私は5年以上前からgitを使ってきました。起きていません。

乱雑な歴史は私にとって本当に問題になったことがない。私は今までの自分のコミット履歴をエキサイティングな小説のように読むだけではありません。私が歴史を必要としている時間の大部分は私がとにかくgit blameかgit bisectを使うつもりです。その場合、マージコミットを持つことは実際に私にとって役に立ちます。マージが私にとって意味のある情報である問題を紹介してくれたからです。

アップデート(4/2017)

私の一般的なアドバイスはまだ残っていますが、私は個人的にリベースを使うことをやめさせたことを述べる義務を負うことを感じます。私は最近 Angular 2 Material プロジェクトと多くのやりとりをしています。彼らは非常にきれいなコミット履歴を保つためにリベースを使いました。これは、リベースを正しく使用するための優れた例として役立ちます。

167
Pace

マージ手段:自分の変更を送り先にマージする単一の新しいコミットを作成します。

Rebase手段:私の現在のコミットのセットをヒントとして使用して、まったく新しい一連のコミットを作成します。言い換えれば、私がリベースした時点から私が行った変更がどうなったかを計算します。そのため、リベース後には変更を再テストする必要があり、リベース中にはいくつかの衝突が発生する可能性があります。

これを考えて、なぜあなたはリベースしますか?開発履歴を明確にするためだけに。あなたが機能Xに取り組んでいて、それが終わったら、あなたが行った変更をマージしたとしましょう。マージするのではなく、リベースしてマージした場合、宛先の開発履歴には、単一の論理的進行におけるすべての個々のコミットが含まれます。これにより、後で変更を確認するのがはるかに簡単になります。 50人の開発者が常にさまざまな機能をマージしている場合、開発履歴を確認するのがどれほど難しいと思うか想像してみてください。

とは言っても、上流で作業しているブランチをすでにプッシュしているのであれば、リベースするのではなくマージするべきです。上流にプッシュされていないブランチの場合は、リベース、テスト、およびマージを行います。

あなたがリベースしたいと思うかもしれないもう一つの時は上流にプッシュする前にあなたのブランチからコミットを取り除きたい時です。たとえば、次のようにします。初期の段階でデバッグ用コードを紹介するコミットと、それ以降の他のコミットでコードをクリーンアップします。これを行う唯一の方法は、対話式リベースを実行することです。git rebase -i <branch/commit/tag>

UPDATE:Gitを使って非線形履歴をサポートしていないバージョン管理システム(Subversionなど)に接続するときにもrebaseを使いたいと思うでしょう。 git-svnブリッジを使用するとき、Subversionにマージし直す変更がトランクの最新の変更の上にある変更の連続的なリストであることは非常に重要です。これを行うには2つの方法しかありません。(1)変更を手動で再作成する方法、および(2)rebaseコマンドを使用する方法です。

UPDATE2:リベースについて考えるもう一つの方法はそれがあなたの開発スタイルからあなたがコミットしているリポジトリで受け入れられたスタイルへの一種のマッピングを可能にすることです。小さな、小さな塊でコミットするのが好きだとしましょう。タイプミスを修正するためのコミット、未使用のコードを取り除くためのコミットなどがあります。あなたがする必要があることを終えた時までに、あなたは長い一連のコミットを持っています。今コミットしようとしているリポジトリが大きなコミットを奨励しているとしましょう。ですからあなたがしている仕事のためには、1つか2つのコミットを期待するでしょう。どのようにしてコミットの文字列を取得し、それらを予想されるものに圧縮しますか?あなたは対話的なリベースを使って、あなたの小さなコミットをより少ないより大きな塊につぶすでしょう。逆のことが必要な場合も同じです。あなたのスタイルがいくつかの大きなコミットであったとしても、レポは小さなコミットの長い文字列を要求しました。あなたはそれをするのにリベースを使うでしょう。代わりにマージした場合は、コミットスタイルをメインリポジトリに移植しました。開発者が多い場合は、しばらくしてからいくつかの異なるコミットスタイルで履歴をたどるのがいかに難しいかを想像できます。

UPDATE3:Does one still need to merge after a successful rebase?はい、そうです。その理由は、リベースは基本的にコミットの「シフト」を伴うからです。私が上で言ったように、これらのコミットは計算されます、しかしあなたが分岐の時点から14のコミットを持っていたなら、あなたのリベースで何も問題がないと仮定すれば、リベースは完了です。あなたはリベースの前に枝を持っていました。後に同じ長さの枝があります。変更を公開する前に、まだマージする必要があります。言い換えれば、何度でもリベースします(変更を上流にプッシュしていない場合のみ)。あなたがリベースした後にのみマージします。

69
Carl

マージ/リベースの前

A <- B <- C    [master]
^
 \
  D <- E       [branch]

git merge masterの後:

A <- B <- C
^         ^
 \         \
  D <- E <- F

git rebase masterの後:

A <- B <- C <- D' <- E'

(A、B、C、D、E、およびFはコミットです)

この例とgitに関するもっとよく説明された情報はここで見つけることができます: http://excess.org/article/2008/07/ogre-git-tutorial/

60
guybrush

マージは変更を統合するための最も簡単で一般的な方法であることは間違いありませんが、それが唯一の方法ではありません。Rebaseは統合の代替手段です。

より良いマージを理解する

Gitがマージを実行するとき、3つのコミットを探します。

  • (1)共通の祖先コミットプロジェクト内の2つのブランチの履歴をたどると、それらは常に少なくとも1つのコミットを共有します。
  • (2)+(3)各分岐の終点統合の目的は、2つの分岐の現在の状態を組み合わせることです。したがって、それぞれの最新の改訂は特に興味深いものです。これら3つのコミットを組み合わせることで、私たちが目指している統合が実現します。

早送りまたはマージコミット

非常に単純なケースでは、2つのブランチのうちの1つはブランチが起こって以来新しいコミットを持っていません - その最新のコミットはまだ共通の先祖です。

enter image description here

この場合、統合を実行するのは簡単です。Gitは、他のブランチのすべてのコミットを共通の先祖コミットの上に追加するだけです。 Gitでは、この最も単純な形式の統合は「早送り」マージと呼ばれます。両ブランチは、まったく同じ履歴を共有します。

enter image description here

しかし多くの場合、両方のブランチは個別に前進しました。 enter image description here

統合を行うために、Gitはそれらの間の違いを含む新しいコミット - マージコミットを作成する必要があります。

enter image description here

ヒューマンコミットとマージコミット

通常、コミットは人間によって慎重に作成されます。関連する変更のみをまとめてコメントで注釈を付ける意味のある単位です。

マージコミットは少し異なります。開発者によって作成されるのではなく、Gitによって自動的に作成されます。そして、関連する一連の変更をラップする代わりに、結び目のように2つのブランチを接続することを目的としています。後でマージ操作を理解したい場合は、両方のブランチの履歴とそれに対応するコミットグラフを確認する必要があります。

Rebaseとの統合

このような自動マージコミットをせずに済むことを好む人もいますが、代わりにプロジェクトの履歴を単一の直線で展開したように見せたいと思いますいくつかのポイント。

enter image description here

リベース操作を段階的に見ていきましょう。シナリオは前の例と同じです。ブランチBからブランチAへの変更を統合したいのですが、今度はrebaseを使用します。

enter image description here

これを3つのステップで行います

  1. git rebase branch-A // syncs the history with branch-A
  2. git checkout branch-A // change the current branch to branch-A
  3. git merge branch-B // merge/take the changes from branch-B to branch-A

まず、Gitは、行が分岐し始めた後(共通の先祖コミットの後)に発生したブランチAのコミットをすべて「元に戻します」。しかし、もちろん、それらを破棄することはできません。代わりに、それらのコミットを「一時的に保存される」と考えることができます。

enter image description here

次に、統合したいブランチBのコミットを適用します。この時点では、両方のブランチはまったく同じに見えます。

enter image description here

最後のステップでは、ブランチAの新しいコミットが再適用されます。ただし、ブランチBの統合されたコミットの上に新しい位置に置かれます(これらは再ベースされます)。 結果は開発が一直線に起こったように見えます。結合されたすべての変更を含むマージコミットの代わりに、元のコミット構造が維持されました。

enter image description here

最後に、不要で自動生成されたコミットのないきれいなブランチ branch-A を得ます。

注: git-tower によって素晴らしい post から取得されました。 rebaseデメリットも同じ投稿の中でよく読むことです。

46
Abdullah Khan

この文はそれを得ます:

一般に、両方の長所を活用するには、行ったローカルの変更をリベースすることです。ストーリーを整理するためにそれらをプッシュする前にまだ共有していません。

出典: http://www.git-scm.com/book/en/v2/Git-Branching-Rebasing#Rebase-vs.-Merge

26

この答えは、 Git Flow を中心に広く方向付けられています。テーブルは、Nice ASCII Table Generator とこの素晴らしいコマンド( エイリアス as git lg)で生成されました:

git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

履歴ツリーとの整合性を保つために、表は新しい順に並べられています。 git mergegit merge --no-ffの違いも最初に参照してください(通常、git merge --no-ffを使用すると、履歴が現実に近くなります)。

git merge

コマンド:

Time          Branch "develop"             Branch "features/foo"
------- ------------------------------ -------------------------------
15:04   git merge features/foo
15:03                                  git commit -m "Third commit"
15:02                                  git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

結果:

* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
|           Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
|           Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge --no-ff

コマンド:

Time           Branch "develop"              Branch "features/foo"
------- -------------------------------- -------------------------------
15:04   git merge --no-ff features/foo
15:03                                    git commit -m "Third commit"
15:02                                    git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

結果:

*   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/            Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge vs git rebase

最初のポイント:常に機能を開発にマージし、機能から開発をリベースしないでください。これは、 リベースの黄金律 の結果です。

git rebaseの黄金律は、publicブランチで使用しないことです。

つまり

どこかにプッシュしたものをリベースしないでください。

私は個人的に追加します:機能ブランチでなく、あなたとあなたのチームが結果を認識している場合を除きます

したがって、git mergegit rebaseの質問は、機能ブランチにのみ適用されます(次の例では、--no-ffは常にマージに使用されています)。より良い解決策があるかどうかはわかりません( 議論が存在する )ので、両方のコマンドの動作のみを提供することに注意してください。私の場合、git rebaseを使用したほうが良い履歴ツリーを生成できます:)

機能ブランチ間

git merge

コマンド:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

結果:

*   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\            Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | |           Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | |           Fourth commit - Christophe
* | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \            Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

コマンド:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git rebase features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

結果:

*   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

developから機能ブランチへ

git merge

コマンド:

Time           Branch “develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m “Sixth commit"
15:08                                                                    git merge --no-ff development
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m “Fifth commit"
15:05                                                                    git commit -m “Fourth commit"
15:04                                    git commit -m “Third commit"
15:03                                    git commit -m “Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m “First commit"

結果:

*   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\            Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | |           Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ /            Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

コマンド:

Time           Branch “develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m “Sixth commit"
15:08                                                                    git rebase development
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m “Fifth commit"
15:05                                                                    git commit -m “Fourth commit"
15:04                                    git commit -m “Third commit"
15:03                                    git commit -m “Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m “First commit"

結果:

*   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
*   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\            Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

サイドノート

git cherry-pick

特定のコミットが1つだけ必要な場合、git cherry-pickはニースのソリューションです(-xオプションは、「(コミットから選択されたチェリー...)」という行を元のファイルに追加しますメッセージ本文をコミットするので、通常はそれを使用することをお勧めします-git log <commit_sha1>表示するには):

コマンド:

Time           Branch “develop"              Branch "features/foo"                Branch "features/bar"           
------- -------------------------------- ------------------------------- -----------------------------------------
15:10   git merge --no-ff features/bar                                                                            
15:09   git merge --no-ff features/foo                                                                            
15:08                                                                    git commit -m “Sixth commit"             
15:07                                                                    git cherry-pick -x <second_commit_sha1>  
15:06                                                                    git commit -m “Fifth commit"             
15:05                                                                    git commit -m “Fourth commit"            
15:04                                    git commit -m “Third commit"                                             
15:03                                    git commit -m “Second commit"                                            
15:02   git checkout -b features/bar                                                                              
15:01   git checkout -b features/foo                                                                              
15:00   git commit -m “First commit"                                                                              

結果:

*   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| |           Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git pull --rebase

Derek Gourlay ...よりもうまく説明できるかどうか定かではありません。基本的に、git pull --rebaseの代わりにgit pullを使用してください:)ただし、この記事で欠けているのは デフォルト

git config --global pull.rebase true

git rerere

繰り返しますが、 ここ でうまく説明しました。ただし、簡単に言えば、有効にすると、同じ競合を複数回解決する必要がなくなります。

19
sp00m

リベースページの本当に良い説明としてのpro git book -

基本的にマージは2コミットしてそれらを結合します。

リベースは2の共通の祖先に行き、互いの上に徐々に変更を適用します。これは「よりクリーン」でより直線的な歴史になります。

しかし、あなたがリベースするとき、あなたは前のコミットを放棄して新しいものを作成します。だからあなたは公開されているレポをリベースするべきではありません。レポに取り組んでいる他の人々はあなたを憎むでしょう。

その理由だけで、私はほとんど排他的にマージします。私の支店では99%の差はそれほど変わらないので、競合が発生した場合、それは1つか2つの場所に限られます。

15
xero

Git rebaseは、履歴内の分岐パスを明確にし、リポジトリ構造を線形にするために使用されます。

リベースして変更をサーバーにプッシュした後、ブランチを削除しても、それまでに作業したブランチの証拠はないため、これはあなたが作成したブランチを非公開にするためにも使用されます。だからあなたの支店は今あなたの地域の関心事です。

リベースを行った後は、通常のマージを実行するかどうかを確認するために使用していた余分なコミットも削除します。

Rebaseコマンドは単にrebase say masterで述べたブランチの上に作業を置き、masterブランチの直接の子孫としてあなたのブランチの最初のコミットをするので、そうです。つまり、このブランチからマスターブランチに変更を反映するために早送りマージを実行できるようになりました。

4
cvibha

いくつかの実用的な例は、gerritがレビューと配信の統合に使用される大規模開発に関連しています。

私は自分の機能ブランチを新鮮なリモートマスターに引き上げるときにマージします。これは最小限の作業量の増加をもたらし、例えばgitkの機能開発の歴史をたどるのは簡単です。

git fetch
git checkout Origin/my_feature
git merge Origin/master
git commit
git Push Origin HEAD:refs/for/my_feature

配送コミットを作成するときにマージします。

git fetch
git checkout Origin/master
git merge --squash Origin/my_feature
git commit
git Push Origin HEAD:refs/for/master

配達のコミットが何らかの理由で統合に失敗したときはリベースし、新しいリモートマスターに更新する必要があります。

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase Origin/master
git Push Origin HEAD:refs/for/master
3
Martin G

何をリベースし、何をマージするのかを何度も説明しましたが、いつ何を使うのか?

いつリベースを使うの?

  • あなたがそのブランチをプッシュしていないとき/他の誰もそのブランチに取り組んでいないとき
  • あなたは完全な歴史が欲しい
  • 自動生成された「マージされた..」コミットメッセージをすべて避けたい

Git rebaseが歴史を変えるように。したがって、他の誰かが同じブランチに取り組んでいるときや、プッシュしているときは、使用しないでください。しかし、ローカルブランチがある場合は、ブランチをマージしてマスターに戻す前にマージリベースマスターを実行して、履歴をきれいにすることができます。こうすると、マスターブランチにマージした後、マスターブランチでブランチを使用したことが見えなくなります - 自動生成された「マージされた..」を持っていなくても履歴は「よりきれい」ですが自動生成された "マージされた.."のコミットなしであなたのマスターブランチの履歴。ただし、git merge feature-branch --ff-onlyを使用して、機能をメインにマージするときに競合が発生しないようにして単一のコミットを作成してください。 機能ブランチの履歴を取得するときに作業しているすべてのタスクに機能ブランチを使用しているが、 "マージされた.."コミットではない場合、これは面白いです

2つ目のシナリオは、ブランチから分岐して、メインブランチで何が変わったのかを知りたい場合です。それはすべての単一のコミットが含まれているのでrebaseはあなたに情報を提供します。

いつマージを使うの?

  • ブランチをプッシュしたとき/他の人もブランチに取り組んでいるとき
  • あなたは全履歴を必要としません
  • 単にマージするだけで十分です

あなたがあなたのマスターブランチにフィーチャーブランチの全ての履歴を持っている必要がないか欲しいとき、あるいは他の人が同じブランチに取り組んでいるとき/あなたはそれをプッシュしました。それでも履歴を残したい場合は、featureブランチをmasterにマージする前にmasterをfeatureブランチにマージしてください。これにより、マスターに機能ブランチの履歴がある場合は早送りマージが行われます(マスターをマージしたために機能ブランチにあったマージコミットを含む)。

1
Jeremy Benks

例を見てみましょう。 loginブランチに基づいてmasterという名前のブランチで作業中に、チームメンバーの1人がmasterにいくつかの変更をプッシュしました。ブランチのlogin機能を完了するには、これらの変更が必要です。

enter image description here 図1. masterブランチでの作業を完了するには、loginブランチの新しいコミット(EおよびF)が必要です。

Mergingmasterブランチを元に戻すと、マージコミットが発生します。これには、両方のブランチ間の変更が含まれ、マージの場所を示すために存在します起こった。

enter image description here 図2. 2つのブランチをマージすると、マージコミットになります。

masterをいつloginブランチにマージしたかを知る必要はありません。代わりに、loginブランチのすべてのコミットがmasterブランチの新しい状態に基づいて行われたふりをしたいと思います。

Gitのrebaseコマンドは、現在のブランチでコミットを一時的に巻き戻し、他のブランチからコミットをプルし、巻き戻されたコミットを先頭に再適用します。電流を切り替えることにより、現在のブランチのベースが他のブランチになります。

enter image description here

図3. loginブランチの上にあるmasterブランチからコミットをリベースして適用します。

ソースは here です

0
yoAlex5