GitリベースとGitマージの使用はいつ推奨されますか?
リベースが成功した後もマージする必要がありますか?
GitリベースとGitマージの使用はいつ推奨されますか?
リベースが成功した後もマージする必要がありますか?
回答:
では、どちらを使用するのですか?
init
新しいリポジトリ、add
ファイル、およびを含むディレクトリを作成しますcommit
。新しい機能ブランチをチェックアウト(checkout -b feature
。)テキストファイルを変更し、コミットして繰り返し、機能ブランチに2つの新しいコミットがあるようにします。その後checkout master
とmerge feature
。ではlog
、マスターでの最初のコミットに続いて、機能からマージされた2つが表示されます。の場合merge --squash feature
、機能はマスターにマージされますがコミットされません。そのため、マスターでの唯一の新しいコミットは自分で作成したものになります。
それは簡単です。リベースでは、作業の新しいベースとして別のブランチを使用すると言います。
たとえば、ブランチmaster
がある場合、新しい機能を実装するブランチを作成し、それに名前を付けます。cool-feature
もちろん、マスターブランチは新しい機能のベースです。
次に、ある時点で、master
ブランチに実装した新しい機能を追加します。ブランチに切り替えてmaster
マージするだけですcool-feature
:
$ git checkout master
$ git merge cool-feature
ただし、この方法では、新しいダミーコミットが追加されます。スパゲッティの歴史を避けたい場合は、リベースできます:
$ git checkout cool-feature
$ git rebase master
そして、それをマージしmaster
ます:
$ git checkout master
$ git merge cool-feature
今回は、トピックブランチに同じマスターのコミットと新しい機能によるコミットがあるため、マージは早送りになります。
but this way a new dummy commit is added, if you want to avoid spaghetti-history
-どのように悪いのですか?
Sean Schofield
が@ アレックスをコメントに含めると、「最終的にurの内容をマスターにマージすると(すでに説明したように簡単です)、urの履歴の「トップ」にあるため、リベースも素晴らしいです。機能が書き込まれる可能性があるが数週間後にマージされるプロジェクトでは、履歴に戻ってマスターに「詰め込まれ」ているため、それらをマスターにマージしたくないだけです。個人的にはgit logを実行して、最近の機能は「トップ」にあります。コミット日は保持されます-リベースはその情報を変更しません。」
merge
、rebase
、fast-forward
、など)有向非巡回グラフの特定の操作を参照しています。それらはそのメンタルモデルを念頭に置いて考えるのがより簡単になります。
マージの前に行うのがリベースであることがよくあります。これは、マージするブランチY
の作業をブランチに統合するためB
です。
しかし、再び、マージする前に、ブランチの競合を解決します(つまり、「ブランチからの最近のポイントから開始して、ブランチでの作業を再生します」のように「リベース」B
)。
正しく実行されると、ブランチからブランチB
は早送りすることができます。
マージは宛先ブランチに直接影響します B
。つまり、マージは簡単です。さもなければ、ブランチB
が安定した状態に戻るまでに時間がかかる可能性があります(すべての競合を解決するための時間)
リベース後のマージのポイント?
私が説明する場合、私はB
自分のブランチにリベースし、自分のブランチにB
とどまっている間に、からのより最近のポイントから自分の作業を再生する機会を得るためにだけです。
この場合でも、「リプレイ」した作業をに取り込むにはマージが必要ですB
。
他のシナリオ(たとえば、Git Readyで説明されています)はB
、リベースを通じて直接作業を取り込むことです(これにより、すてきなコミットがすべて保存されるか、インタラクティブなリベースを通じてそれらを並べ替える機会が与えられます)。
その場合(Bブランチにいるときにリベースする場合)は正しいです。それ以上のマージは必要ありません。
マージもリベースもしていないデフォルトのGitツリー
私たちはリベースして取得します:
その2番目のシナリオはすべてです。新しい機能をマスターに戻すにはどうすればよいですか。
最初のリベースシナリオについて説明する私のポイントは、リベースはその前段階としても使用できることをすべての人に思い出させることです(「新機能をマスターに戻す」ことです)。
リベースを使用して、最初にマスターを新しい機能のブランチに「組み込む」ことができます。リベースは、新しい機能のコミットをから再生しますがHEAD master
、まだ新しい機能のブランチにあり、ブランチの開始点を古いマスターのコミットからに効果的に移動しますHEAD-master
。
これにより、ブランチ内のすべての競合を解決できます(つまり、競合の解決ステージに時間がかかりすぎる場合にマスターが並行して進化し続けることを許可します)。
次に、マスタとマージに切り替えることができますnew-feature
(またはリベースnew-feature
上にmaster
、あなたの中で行わコミットを保持したい場合new-feature
ブランチ)。
そう:
master
。疑問がある場合は、マージを使用してください。
リベースとマージの唯一の違いは次のとおりです。
したがって、簡単な答えは、履歴をどのように見せたいかに基づいてリベースまたはマージを選択することです。
使用する操作を選択するときに考慮すべきいくつかの要因があります。
その場合、リベースしないでください。リベースはブランチを破壊し、それらを使用しない限り、それらの開発者は壊れた/一貫性のないリポジトリを持つことになりますgit pull --rebase
。これは、他の開発者をすぐに混乱させる良い方法です。
リベースは破壊的な操作です。つまり、正しく適用しないと、コミットされた作業が失われたり、他の開発者のリポジトリの一貫性が損なわれたりする可能性があります。
私はチームに取り組んできました。開発者はすべて、企業が分岐とマージに対処するための専任スタッフを雇う余裕があった時代から来ました。これらの開発者は、Gitについてあまり知らないので、知りたくありません。これらのチームでは、何らかの理由でリベースを勧めるリスクを冒しません。
一部のチームは、各ブランチが機能(またはバグ修正、サブ機能など)を表す機能ごとのブランチモデルを使用します。このモデルでは、ブランチは関連するコミットのセットを識別するのに役立ちます。たとえば、そのブランチのマージを元に戻すことで、機能をすばやく元に戻すことができます(公平にするために、これはまれな操作です)。または、2つのブランチを比較して機能を比較します(より一般的)。リベースはブランチを破壊し、これは簡単ではありません。
また、開発者ごとのブランチモデルを使用するチームにも取り組みました(全員が参加しました)。この場合、ブランチ自体は追加情報を伝えません(コミットにはすでに作者がいます)。リベースしても害はありません。
マージを元に戻す場合と比較して、リベースを元に戻す(元に戻す場合と同様)ことは、(リベースに競合がある場合)かなり困難または不可能です。元に戻す可能性があると思われる場合は、マージを使用してください。
リベース操作は、対応するでプルする必要がありますgit pull --rebase
。自分で作業している場合は、適切なときにどちらを使用するかを思い出すことができます。チームで作業している場合、これを調整するのは非常に困難です。これが、ほとんどのリベースワークフローがすべてのマージ(およびgit pull --rebase
すべてのプル)にリベースを使用することを推奨する理由です。
次のマージがあると仮定します。
B -- C
/ \
A--------D
マスターブランチ(A-D)のみのログを見ると、BおよびCに含まれる重要なコミットメッセージを見逃すため、マージによってコミット履歴が「破棄される」と言う人もいます。
これが本当なら、このような質問はありません。基本的に、(-first-parentを使用して)表示しないように明示的に要求しない限り、BとCが表示されます。これは自分で試すのはとても簡単です。
2つのアプローチは異なる方法でマージされますが、一方が常に他方よりも優れていることは明らかではなく、開発者のワークフローに依存する場合があります。たとえば、開発者が定期的にコミットする傾向がある場合(たとえば、仕事から家に移動するときに1日に2回コミットする場合)、特定のブランチに対して多くのコミットが存在する可能性があります。これらのコミットの多くは、最終的な製品のようには見えない可能性があります(機能ごとにアプローチを1回または2回リファクタリングする傾向があります)。他の誰かがコードの関連領域で作業していて、彼らが私の変更をリベースしようとした場合、それはかなり退屈な操作になる可能性があります。
エイリアスrm
したい場合rm -rf
「時間を節約する」ためしは、おそらくリベースが適しています。
私はいつかいつか、Gitリベースが問題を解決する素晴らしいツールであるというシナリオに出くわすといつも思います。Git reflogが私の問題を解決する素晴らしいツールであるというシナリオに出くわすと思います。私はGitで5年以上働いています。それは起こっていません。
散らかった歴史は私にとって本当に問題ではありませんでした。エキサイティングな小説のように自分のコミット履歴を読むだけではありません。歴史が必要なほとんどの場合、とにかくGit BlameまたはGit bisectを使用します。その場合、マージコミットが実際に役立ちます。マージによって問題が発生した場合、それは私にとって意味のある情報であるためです。
私の個人的なアドバイスはまだ残っていますが、私は個人的にリベースの使用をやわらげたことに言及する義務があります。私は最近、Angular 2 Materialプロジェクトとたくさんやり取りしています。彼らはリベースを使用して、非常にクリーンなコミット履歴を維持しています。これにより、特定の不具合を修正したコミットと、そのコミットがリリースに含まれていたかどうかを非常に簡単に確認できました。これは、リベースを正しく使用するための良い例です。
ここでの答えの多くは、マージによってすべてのコミットが1つに変わると言うため、コミットを保持するためにリベースを使用することをお勧めします。これは誤りです。そして、もしあなたがあなたのコミットをすでにプッシュしているなら、悪い考えです。
Mergeはコミットを抹消しません。Mergeは履歴を保存します!(gitkを見てください)Rebaseは、履歴をリライトします。これは、プッシュした後の悪いことです。
マージを使用します-すでにプッシュしたときは常にリベースしません。
これは、Linus(Gitの作者)が引き受けたものです(Wayback Machineによって回収された私のブログでホストされています)。それは本当に良い読み物です。
または、以下の同じアイデアの独自のバージョンを読むことができます。
マスターでブランチをリベースする:
対照的に、トピックブランチをマスターにマージする:
TLDR:それは最も重要なものに依存します-きちんとした履歴または開発のシーケンスの真の表現
きちんとした履歴が最も重要な場合は、最初にリベースしてから変更をマージして、新しいコードが正確に何であるかを明確にします。すでにブランチをプッシュしている場合は、結果に対処できない限り、リベースしないでください。
シーケンスの真の表現が最も重要な場合は、リベースせずにマージします。
マージとは、変更を宛先にマージする単一の新しいコミットを作成することです。注意:この新しいコミットには2つの親があります。コミットの文字列からの最新のコミットと、マージする他のブランチの最新のコミットです。
リベースとは、現在の一連のコミットをヒントとして使用して、まったく新しい一連のコミットを作成することです。言い換えれば、私がリベースしているところから変更を始めた場合、私の変更がどのように見えるかを計算します。したがって、リベースの後、変更を再テストする必要があり、リベース中に、いくつかの競合が発生する可能性があります。
これを踏まえて、なぜリベースするのですか?開発履歴を明確にするためです。機能Xで作業していて、完了したら変更をマージするとします。宛先には、「追加された機能X」の行に沿って何かを言う単一のコミットがあります。これで、マージする代わりに、リベースしてからマージした場合、宛先の開発履歴には、単一の論理的な進行ですべての個別のコミットが含まれます。これにより、後で変更を確認することがはるかに簡単になります。50人の開発者が常にさまざまな機能をマージしている場合、開発履歴を確認するのがどれほど難しいか想像してみてください。
とはいえ、作業中のブランチをすでにアップストリームにプッシュしている場合は、リベースせずにマージする必要があります。アップストリームにプッシュされていないブランチの場合、リベース、テスト、およびマージします。
もう1つ、リベースする必要があるのは、アップストリームにプッシュする前にブランチからコミットを取り除きたいときです。例:早い段階でいくつかのデバッグコードを導入するコミットと、そのコードをさらにクリーンアップする他のコミット。これを行う唯一の方法は、インタラクティブなリベースを実行することです。git rebase -i <branch/commit/tag>
更新:また、Gitを使用して、非線形履歴をサポートしないバージョン管理システム(Subversionなど)にインターフェースする場合にも、リベースを使用する必要があります。git-svnブリッジを使用する場合、Subversionにマージして戻す変更は、トランクでの最新の変更に加えて、変更の順次リストであることが非常に重要です。これを行う方法は2つしかありません:(1)手動で変更を再作成する、および(2)rebaseコマンドを使用する。
更新2:リベースについて考えるもう1つの方法は、開発スタイルから、コミットするリポジトリで受け入れられるスタイルへの一種のマッピングを可能にすることです。小さな小さな塊でコミットしたいとしましょう。タイプミスを修正する1つのコミット、未使用のコードを削除する1つのコミットなどがあります。必要な作業が完了するまでに、長い一連のコミットがあります。ここで、コミットしているリポジトリが大きなコミットを奨励しているとしましょう。そのため、実行している作業では、1つまたは2つのコミットが予想されます。どのようにしてコミットの文字列を取得し、それらを期待されるものに圧縮しますか?インタラクティブなリベースを使用して、小さなコミットを少数の大きなチャンクに押しつぶします。逆が必要な場合も同様です。スタイルがいくつかの大きなコミットであった場合、しかし、リポジトリは小さなコミットの長い文字列を要求しました。リベースを使用しても同じことができます。代わりにマージした場合は、コミットスタイルをメインリポジトリに移植しました。多くの開発者がいる場合、しばらくしていくつかの異なるコミットスタイルの履歴をたどるのがどれほど難しいか想像できます。
更新3:Does one still need to merge after a successful rebase?
はい、そうです。その理由は、リベースには本質的にコミットの「シフト」が含まれるためです。上記で述べたように、これらのコミットは計算されますが、分岐の時点から14のコミットがあった場合、リベースに問題がないと仮定すると、(リベースしているポイントの)14コミット先になります。リベースが行われます。リベースの前にブランチがありました。あなたは後に同じ長さの枝を持つでしょう。変更を公開する前に、まだマージする必要があります。つまり、必要なだけリベースします(これも、変更をアップストリームにプッシュしていない場合に限ります)。リベースした後でのみマージします。
git merge
は、--no-ff
マージコミットを強制するオプションをサポートしています。
マージは間違いなく変更を統合する最も簡単で最も一般的な方法ですが、それだけではありません。リベースは統合の代替手段です。
マージを少し良く理解する
Gitがマージを実行すると、3つのコミットが検索されます。
早送りまたはマージコミット
非常に単純なケースでは、分岐が発生したため、2つのブランチのうちの1つに新しいコミットがありません。最新のコミットが依然として共通の祖先です。
この場合、統合の実行は非常に簡単です。Gitは、共通の祖先コミットの上に他のブランチのすべてのコミットを追加することができます。Gitでは、この最も単純な統合形式を「早送り」マージと呼びます。その後、両方のブランチがまったく同じ履歴を共有します。
ただし、多くの場合、両方のブランチが個別に前進しました。
統合を行うには、Gitはそれらの違いを含む新しいコミット、つまりマージコミットを作成する必要があります。
ヒューマンコミットとマージコミット
通常、コミットは人間によって慎重に作成されます。これは、関連する変更のみをラップし、コメントで注釈を付ける意味のあるユニットです。
マージコミットは少し異なります。開発者によって作成されるのではなく、Gitによって自動的に作成されます。そして、関連する一連の変更をラップする代わりに、その目的は、結び目のように2つのブランチを接続することです。後でマージ操作を理解したい場合は、両方のブランチと対応するコミットグラフの履歴を確認する必要があります。
Rebaseとの統合
一部の人々は、そのような自動マージコミットなしで行くことを好みます。代わりに、彼らはプロジェクトの履歴が単一の直線で進化したかのように見えることを望んでいます。ある時点で複数のブランチに分割された兆候は残っていません。
リベース操作をステップごとに見ていきましょう。シナリオは前の例と同じです。ブランチBからブランチAへの変更を統合する必要がありますが、今はリベースを使用しています。
これは3つのステップで行います
git rebase branch-A // Synchronises the history with branch-A
git checkout branch-A // Change the current branch to branch-A
git merge branch-B // Merge/take the changes from branch-B to branch-A
まず、GitはブランチAでのすべてのコミットを「取り消し」ます。これは、行が分岐し始めた後(共通の祖先コミットの後)に発生しました。ただし、もちろん、それらが破棄されることはありません。代わりに、それらのコミットは「一時的に保存される」と考えることができます。
次に、統合するブランチBからのコミットを適用します。この時点で、両方のブランチはまったく同じに見えます。
最後のステップで、ブランチAの新しいコミットが再適用されます。ただし、ブランチBからの統合されたコミット(それらは再ベース化されています)の上に新しい位置で適用されます。
結果は、開発が一直線に行われたように見えます。結合されたすべての変更を含むマージコミットの代わりに、元のコミット構造が保持されました。
最後に、クリーンなブランチブランチAを取得します不要な自動生成されたコミットのないを。
マージ/リベースの前:
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についてよりよく説明された情報は、Git The Basics Tutorialにあります。
この文はそれを取得します:
一般に、両方の世界を最大限に活用する方法は、行ったがまだ共有していないローカル変更をリベースして、ストーリーをクリーンアップするためにプッシュする前に、どこかにプッシュしたものをリベースしないことです。 。
この回答は、Git Flowを中心に広く方向付けられています。テーブルは、ASCIIテーブルジェネレーターで生成され、履歴ツリーはこの素晴らしいコマンド(別名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 merge
とgit 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
対 git rebase
最初のポイント:機能を常に開発にマージし、開発を機能からリベースしないでください。これは、リベースのゴールデンルールの結果です。
黄金のルール
git rebase
は、それを公共のブランチで使用しないことです。
つまり:
どこかにプッシュしたものをリベースしないでください。
私は個人的に付け加えます:それが機能ブランチであり、かつあなたとあなたのチームが結果を認識していない限り。
したがって、git merge
vs の問題git rebase
はほとんど機能ブランチにのみ適用されます(次の例で--no-ff
は、マージ時に常に使用されています)。もう1つのより良い解決策があるかどうかはわからないので(議論が存在するため)、両方のコマンドの動作のみを提供します。私の場合、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 develop
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 develop
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
基本的に、マージは2つのコミットを取り、それらを結合します。
リベースは、2つの共通の祖先に移動し、変更を互いの上に徐々に適用します。これにより、「よりクリーン」で直線的な履歴が作成されます。
ただし、リベースすると、以前のコミットを破棄して新しいコミットを作成します。したがって、公開されているリポジトリをリベースしないでください。リポジトリで作業している他の人はあなたを嫌います。
その理由だけで、私はほぼ完全にマージします。私のブランチは99%の確率でそれほど大きな違いはないので、競合がある場合は1つか2つしかありません。
Gitリベースを使用して、履歴の分岐パスとリポジトリ構造を線形にします。
また、作成したブランチを非公開にしておくためにも使用されます。変更をリベースしてサーバーにプッシュした後、ブランチを削除すると、作業したブランチの証拠がなくなるためです。したがって、ブランチがローカルの関心事になります。
リベースを実行した後、通常のマージを実行するかどうかを確認するために使用していた追加のコミットも削除します。
そして、はい、rebaseコマンドはリベース中に言及したブランチ(masterなど)の上に作業を置き、マスターブランチの直接の子孫としてブランチの最初のコミットを行うため、リベースが成功した後でもマージを行う必要があります。つまり、このブランチからマスターブランチに変更をもたらすために、早送りマージを実行できます。
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
リベースとマージとは何かについて何度も説明されましたが、いつ何を使用する必要がありますか?
いつリベースを使用すべきですか?
Gitがリベースすると履歴が変更されます。したがって、他の誰かが同じブランチで作業している場合や、それをプッシュした場合は使用しないでください。しかし、ローカルブランチがある場合は、ブランチをマスターにマージする前にマージリベースマスターを実行して、より明確な履歴を維持できます。これを行うと、マスターブランチにマージした後、マスターブランチでブランチを使用したことが表示されなくなります。自動生成された「マージされた..」がないため、履歴は「よりクリーン」ですが、自動生成された「マージされた..」コミットなしで、マスターブランチの完全な履歴。
ただし、git merge feature-branch --ff-only
機能をメインにマージするときに、単一のコミットを作成するときに競合が発生しないことを確認するために使用します。これは、機能ブランチの履歴を取得するときに作業するすべてのタスクに機能ブランチを使用しているが、「マージされたコミット」ではない場合に興味深いです。
2番目のシナリオは、ブランチからブランチし、メインブランチの変更点を知りたい場合です。リベースには、すべてのコミットが含まれているため、情報が提供されます。
マージはいつ使用すべきですか?
機能ブランチのすべての履歴をマスターブランチに保存する必要がない、またはしたくない場合、または他の人が同じブランチで作業している場合/プッシュした場合。それでも履歴が必要な場合は、マスターを機能ブランチにマージしてから、機能ブランチをマスターにマージします。これにより、マスターに機能ブランチの履歴がある高速転送マージになります(マスターをマージしたために機能ブランチにあったマージコミットを含む)。
いつ使用しgit rebase
ますか?履歴を書き換えるため、ほとんどありません。git merge
プロジェクトで実際に起こったことを尊重するため、ほとんどの場合、これが望ましい選択です。