Gitチェリーピックvsリベース


119

私は最近Gitでの作業を開始しました。

上行くGitの書籍のオンライン私は「Gitのリベース」セクションの下に、次のが見つかりました:

rebaseコマンドを使用すると、1つのブランチでコミットされたすべての変更を取得して、別のブランチで再生できます。

(引用元:http : //git-scm.com/book/en/Git-Branching-Rebasing

これはgit cherry-pickの正確な定義だと思いました(現在チェックアウトされているブランチにコミットまたはコミットオブジェクトのセットを再適用します)。

2つの違いは何ですか?

回答:


165

時間git cherry-pickが複数のコミットを適用できることを学んだので、区別は確かにいくぶん意味がなくなったが、これは収束進化と呼ばれるものである;-)

真の違いは、両方のツールを作成するという本来の意図にあります。

  • git rebaseのタスクは、開発者がプライベートリポジトリで行った一連の変更を、上流ブランチのバージョンXに対して作成した一連の変更を、同じブランチのバージョンY(Y> X)に転送することです。これにより、一連のコミットのベースが効果的に変更されるため、「リベース」されます。

    (また、開発者が任意のコミットに一連のコミットを移植することもできますが、これはあまり明白ではありません。)

  • git cherry-pickある開発ラインから別の開発ラインに興味深いコミットをもたらすためのものです。古典的な例は、不安定な開発ブランチで行われたセキュリティ修正を、安定した(保守)ブランチにバックポートすることです。このブランチでは、merge多くの不要な変更が発生するため、意味がありません。

    初登場以来、git cherry-pick一度に1つずつ複数のコミットを選択することができました。

したがって、これらの2つのコマンドの最も顕著な違いは、作業中のブランチの扱い方です。git cherry-pick通常、別の場所からコミットを取得して現在のブランチの上に適用し、新しいコミットを記録しながらgit rebase、現在のブランチを取得して書き換えます一連の独自のヒントは、何らかの方法でコミットします。はい、これは何git rebaseができるかを非常に詳細に説明したものですが、一般的な考えを理解させようとするのは意図的なものです。

git rebase議論されている使用例をさらに説明するための更新

この状況を考慮して、
リベース前のレポの状態
は述べています:

ただし、別の方法もあります。C3で導入された変更のパッチを取得して、C4の上に再適用することができます。Gitでは、これをリベースと呼びます。rebaseコマンドを使用すると、1つのブランチでコミットされたすべての変更を取得して、別のブランチに適用できます。

この例では、次を実行します。

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

ここでの「キャッチ」とは、この例では、「experiment」ブランチ(リベースの対象)が「master」ブランチから分岐していたため、C0からC2までのコミットを共有するということです。つまり、「experiment」は「マスター」からC2まで、さらにその上にC3をコミットします。(これは可能な限り単純なケースです。もちろん、「実験」には元のベースに加えて数十のコミットが含まれる可能性があります。)

今度git rebaseは、「実験」を「マスター」の現在のヒントにリベースするように指示され、git rebase次のようになります。

  1. git merge-base「実験」と「マスター」の両方で共有された最後のコミットは何か(つまり、転換のポイントは何か)を確認するために実行されます。これはC2です。
  2. 転換点以降に行われたすべてのコミットを保存します。私たちのおもちゃの例では、それは単なるC3です。
  3. HEAD(操作が実行を開始する前の「experiment」のヒントコミットを指す)を巻き戻して、「マスター」のヒントを指すようにします。
  4. 保存された各コミットを(と同じようにgit apply)順番に適用しようとします。私たちのおもちゃの例では、これは1つのコミット、C3です。そのアプリケーションがコミットC3 'を生成するとします。
  5. すべてが順調に進んだ場合、「実験」参照は、最後に保存されたコミット(この場合はC3 ')を適用した結果のコミットを指すように更新されます。

さてあなたの質問に戻りましょう。ご覧のとおり、ここでは技術的に git rebase実際に一連のコミットを「実験」から「マスター」の先端に移植しているため、プロセスの「別のブランチ」が実際にあることを正しく認識できます。しかし、要点は、「experiment」からのティップコミットが「experiment」での新しいティップコミットになってしまったということです。ベースが変更されただけです。
マージ後の状態

繰り返しますが、技術的には、git rebaseここに「マスター」からの特定のコミットが組み込まれていることがわかります。これは完全に正しいことです。


2
ありがとう。私はまだあなたがここで何を意味するのか完全には理解していませんでした。本では、リベースが別のブランチからの一連のティップコミットを適用する例が示されていますが、「同じブランチ」からのものであると言います。それとも、それがどのように機能するかについていくつかのケースがありますか?
リゼルグ酸

1
私の答えを更新して問題を説明しようとしました。
kostix

98

チェリーピックを使用すると、元のコミット/ブランチが残り、新しいコミットが作成されます。リベースでは、ブランチ全体が移動され、ブランチは再生されたコミットをポイントします。

あなたが始めたとしましょう:

      A---B---C topic
     /
D---E---F---G master

リベース:

$ git rebase master topic

あなたが得る:

              A'--B'--C' topic
             /
D---E---F---G master

チェリーピック:

$ git checkout master -b topic_new
$ git cherry-pick A^..C

あなたが得る:

      A---B---C topic
     /
D---E---F---G master
             \
              A'--B'--C' topic_new

gitの詳細については、この本のほとんどを参照してください(http://git-scm.com/book)


3
よく答えました。また、AとBのコミットだけをチェリーピックしたいが、Cはそのままにして、ブランチを維持したい場合や、同僚が見る必要のある変更をチェリーピックしたい場合もよくあります。Gitは人と連携するように作られているため、単独で作業するときに何かのメリットが見られない場合は、大規模なグループで作業するときによく使用されます。
Pablo Jomer

代わりにインタラクティブなリベースが行われ、1つまたは複数のコミットが除外された場合、最後にどのブランチを用意しますか?の上にのみtopicリベースした場合master、残ったコミットは含まれていないので、それらはどのブランチに属しますか?
Anthony、

もう1つ追加したいことがあります。チェリーピッキングの後git checkout topicとその後の場合git reset --hard C'、リベース後とまったく同じ結果になります。共通の祖先がずっと前に戻っていたので、リベースよりもチェリーピッキングを使用して、多くのマージ競合から身を守りました。
sorrymissjackson 2016

@anthony - stackoverflow.com/questions/11835948/...:限り、私は理解してそれらが失われます。私はgit第一人者ではありませんが、このrebase/ cherry-pickは、git理解に問題があったすべての詳細について説明しています。
thoni56

1
グラフは機能的に同一であるため、グラフは良いというよりは害があります。唯一の違いは、によって作成されるブランチgit checkout -bであり、とは何の関係もありませんgit cherry-pick。あなたが言おうとしていることを説明するより良い方法は、「ブランチを実行git rebaseしてtopicそれを渡すことmasterです。ブランチで実行git cherry-pickし、masterそれを渡します(コミット元)topic。」
Rory O'Kane

14

個々のコミットでチェリーピッキングが機能します。

リベースすると、履歴のすべてのコミットがブランチのHEADに適用され、そこにありません。


ありがとう。これらが内部で同じように動作するかどうか知っていますか?(それらの中間出力を「パッチ」ファイルなどに保存します)。
リゼルギン酸

Afaikはい。すべてのパッチを1つずつ適用します。これが、リベースの途中で続行する前にマージの競合を解決する必要がある場合がある理由です。
iltempo 2012

6
@iltempo、それは古いバージョンのGitでのみ個々のコミットで機能しました。現時点でgit cherry-pick foo~3..fooは、「foo」からツリートップコミットを1つずつ選択して取得できます。
kostix

1
git-rebaseはコードベースのiircでチェリーピッキングが行うのと同じAPIを使用します
代替案

実際には同じように動作するとは思いません。私は何千ものコミットをリベースしてみましたが、gitは巨大なメールボックスファイルを作成し、そのgit am上で実行すると思います。一方、チェリーピックはコミットごとにコミットを適用します(おそらく、パッチごとに単一メッセージのメールボックスを作成することによって)。作成しているメールボックスファイルがドライブのスペースを使い果たしたため、リベースが失敗しましたが、同じリビジョン範囲のチェリーピックは成功しました(そしてより高速に実行されているようです)。
onlynone 2015年

11

短い答え:

  • git cherry-pickはより「低レベル」です
  • そのため、git rebaseをエミュレートできます

上記の答えは良いです、私はそれらの相互関係を実証する試みの例を挙げたかっただけです。

「git rebase」をこの一連のアクションに置き換えることはお勧めしません。これは、「概念の証明」にすぎず、物事の仕組みを理解するのに役立つと思います。

次のおもちゃリポジトリがあるとします。

$ git log --graph --decorate --all --oneline
* 558be99 (test_branch_1) Test commit #7
* 21883bb Test commit #6
| * 7254931 (HEAD -> master) Test commit #5
| * 79fd6cb Test commit #4
| * 48c9b78 Test commit #3
| * da8a50f Test commit #2
|/
* f2fa606 Test commit #1

たとえば、test_branch_1に含めたい非常に重要な変更(コミット#2から#5まで)がマスターにあるとします。通常、ブランチに切り替えて「git rebase master」を実行します。しかし、私たちは "git cherry-pick"しか装備していないふりをしているので、次のようにします。

$ git checkout 7254931                # Switch to master (7254931 <-- master <-- HEAD)
$ git cherry-pick 21883bb^..558be99   # Apply a range of commits (first commit is included, hence "^")    

これらすべての操作の後、コミットグラフは次のようになります。

* dd0d3b4 (HEAD) Test commit #7
* 8ccc132 Test commit #6
* 7254931 (master) Test commit #5
* 79fd6cb Test commit #4
* 48c9b78 Test commit #3
* da8a50f Test commit #2
| * 558be99 (test_branch_1) Test commit #7
| * 21883bb Test commit #6
|/
* f2fa606 Test commit #1

ご覧のとおり、コミット#6と#7は7254931(マスターのチップコミット)に対して適用されました。HEADは移動され、本質的にはリベースされたブランチの先端であるコミットを指します。ここで必要なのは、古いブランチポインターを削除して新しいブランチポインターを作成することだけです。

$ git branch -D test_branch_1
$ git checkout -b test_branch_1 dd0d3b4

test_branch_1が最新のマスターポジションからルート付けされました。できた!


しかし、リベースはgit cherry-pickもシミュレートできますか?
Number945

cherry-pickはさまざまなコミットを適用できるので、そうだと思います。これは少し奇妙な方法ですが、機能ブランチのすべてのコミットをの上にチェリーピックしてから、機能ブランチをmaster削除して、の先端を指すように再作成することを妨げるものはありませんmaster。あなたは考えることができますgit rebaseのシーケンスのようにgit cherry-pick feature_branchgit branch -d feature_branchgit branch feature_branch master
レイク

7

これらはどちらも、あるブランチのコミットを別のブランチの上に書き直すためのコマンドです。違いは、「yours」(現在チェックアウトされているHEAD)または「theirs」(コマンドへの引数として渡されるブランチ)の違いです。この書き換えのベース

git rebase開始コミットを取り、それらのコミット(開始コミット)の後に来るようにコミット再生します

git cherry-pickかかるコミットのセットをして再生し、その後にくるようコミットをあなた(あなたHEAD)。

言い換えると、2つのコマンドは、そのコア動作(発散するパフォーマンス特性、呼び出し規約、および拡張オプションを無視)で対称的です:ブランチbarをチェックアウトして実行git rebase fooすると、barブランチをチェックアウトしfooて実行git cherry-pick ..barすると同じ履歴にブランチが設定されますfooto(からの変更foo、その後のからの変更bar)。

名前付けに関して、2つのコマンドの違いは、それぞれが現在のブランチに対して何を行うかを説明していることを覚えておくことができます。rebaseもう一方のヘッドを変更の新しいベースcherry-pickし、もう一方のブランチから変更を選択してそれらの上にHEAD置きますあなたの(サンデーの上にさくらんぼのような)。


1
私はあなたの答えを除いて、どれも理解できませんでした!それは簡潔で、余分な言葉遣いなしで完璧な意味があります。
neoxic

4

どちらも非常によく似ています。主な概念上の違いは、(簡略化した用語で)次のとおりです。

  • rebaseは、現在のブランチから別のブランチにコミットを移動します。

  • cherry-pickコピーは、別のブランチから現在のブランチにコミットします。

@Kenny Hoの答えに似た図を使用する:

この初期状態を考えると:

A---B---C---D master
     \
      E---F---G topic

...そしてtopic現在のmasterブランチの上に再生されたブランチからコミットを取得したい場合、2つのオプションがあります:

  1. リベースの使用:最初にを実行topicしてgit checkout topicに移動し、次にを実行してブランチを移動しgit rebase master、以下を生成します。

    A---B---C---D master
                 \
                  E'---F'---G' topic
    

    結果:現在のブランチtopicがにリベース(移動)されましたmaster。一方でブランチは、更新された分岐が所定の位置に残りました。
    topicmaster

  2. cherry-pickを使用する場合:最初にを実行masterしてアクセスしgit checkout master、次に実行git cherry-pick topic~3..topic(または同等の方法でgit cherry-pick B..G)してブランチをコピーし、次のように生成します。

    A---B---C---D---E'---F'---G' master
         \
          E---F---G topic
    

    結果:からのコミットtopicがにコピーされましたmaster。一方でブランチは、更新された分岐が所定の位置に残りました。
    mastertopic


もちろん、ここでは範囲表記 を使用して、コミットのシーケンスを選択するよう明示的にcherry-pickに指示する必要がありましたfoo..bar。のように単にブランチ名を渡した場合、ブランチgit cherry-pick topicの先端にあるコミットのみが取得され、次の結果になります。

A---B---C---D---G' master
     \
      E---F---G topic
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.