コミットの範囲をチェリーピックして別のブランチにマージするにはどうすればいいですか? 質問する

コミットの範囲をチェリーピックして別のブランチにマージするにはどうすればいいですか? 質問する

リポジトリのレイアウトは次のとおりです。

  • マスターブランチ(本番)
  • 統合
  • 働く

私が実現したいのは、作業ブランチからコミットの範囲をチェリーピックして、それを統合ブランチにマージすることです。私は Git の初心者なので、リポジトリを台無しにせずにこれを正確に行う方法 (マージではなく、コミット範囲のチェリーピックを 1 つの操作で行う) がわかりません。これについて何かアドバイスや考えはありますか? よろしくお願いします!

ベストアンサー1

コミットの範囲に関しては、チェリーピッキング 非現実的でした

として下記参照によるキース・キムGit 1.7.2以降では、コミットの範囲をチェリーピックする機能が導入されました(ただし、将来の統合のためのチェリーピッキングの結果

「git cherry-pick」はコミットの範囲
(「cherry-pick A..B」や「cherry-pick --stdin」など) を選択することを学習したので、「git revert」も同様に選択しました。ただし、これらは「」のより優れたシーケンス制御をサポートしていませんrebase [-i]

ダミアン コメントそして私たちに警告します。

" cherry-pick A..B" 形式では、Aより古い必要がありますB
順序が間違っていると、コマンドは暗黙的に失敗します

から までの範囲( を含む)BDBを選択する場合は、B~..Dではなく になりますB..D
Git は以前のコミットの範囲からブランチを作成しますか?」を参照してください。

としてジュボブス言及コメント欄:

これは、Bルートコミットではないことを前提としています。unknown revisionそうでない場合は、「 」エラーが発生します。

注: Git 2.9.x/2.10 (2016 年第 3 四半期) 以降では、孤立ブランチ (空のヘッド) で直接コミットの範囲をチェリーピックできます。詳細は「Git で既存のブランチを孤立させる方法「」。


元の回答(2010年1月)

Aの方rebase --ontoが良いでしょう。統合ブランチの上で、指定された範囲のコミットを再生します。チャールズ・ベイリーについてはこちら
(また、「あるブランチに基づいてトピックブランチを別のブランチに移植する方法は次のとおりです」を参照してください。git rebase マニュアルページの実例を見るにはgit rebase --onto

現在のブランチが統合の場合:

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

これにより、次の間のすべてが再生されます:

  • の親の後first_SHA-1_of_working_branch_range(つまり~1):再生したい最初のコミット
  • 「 」までintegration(ブランチから再生する最後のコミットを指しますworking

tmp「 」(以前指していた場所を指すintegration)へ

これらのコミットのいずれかを再生したときに競合が発生した場合:

  • それを解決して「git rebase --continue」を実行してください。
  • または、このパッチをスキップして、代わりに「git rebase --skip」を実行します。
  • または「 」ですべてをキャンセルします(そしてブランチをブランチにgit rebase --abort戻します)integrationtmp

その後rebase --onto、統合ブランチの最後のコミット(つまり「 」ブランチ + 再生されたすべてのコミット)integrationに戻ります。tmp

チェリーピッキングやではrebase --onto、その後のマージに影響があることを忘れないでください。ここで説明


純粋な「cherry-pick」ソリューションはここで議論された、次のような内容になります。

パッチ アプローチを使用する場合は、「git format-patch|git am」と「git cherry」がオプションです。
現在、は単一のコミットのみを受け入れますが、から までのgit cherry-pick範囲を選択する場合は、 Git 用語では次のようになります(実際は、以下を参照してください)。BDB^..DB~..D

git rev-list --reverse --topo-order B~..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

しかし、いずれにせよ、コミットの範囲を「再生」する必要がある場合、「再生」という言葉は、rebaseGit の「 」機能を使用するように促すはずです。


プリドモレオブジェクトコメント:

^警告:範囲を包括的にするためにカラット ( ) を使用するという上記の提案に惑わされないでください。

CommitId1たとえば、 の親がCommitId1マージ コミットである場合は、これには含まれません: git cherry-pick CommitId1^..CommitId99
その場合、cherry-pickは引き続き から始まりますCommitId2。理由はわかりませんが、これが私が経験した動作です。

~ただし、の親がCommitId1マージコミットの場合でも、チルダ ( ) を使用すると期待どおりに機能することがわかりましたgit cherry-pick CommitId1~..CommitId99

確かに、これは、特にマージコミットを扱うときに、コミット範囲で Git の cherry-picking コマンドを使用する際の重要なニュアンスを強調しています。

Git では、コミット参照で使用される場合、^およびは特定の意味を持ちます。~

  • ^コミットの親を参照し、範囲で使用すると、特にマージコミットで予期しない結果が生じる可能性があります。
  • ~一方、 は、線形履歴内のコミットの最初の親を示すために使用され、範囲をチェリーピッキングするコンテキストでより予測可能になります。

コミットの範囲をチェリーピックする場合、特にマージコミットを含むシナリオでは、~範囲に意図したコミットが含まれていることを確認するために を使用することをお勧めします。

したがって、 に関してgit cherry-pick CommitId1^..CommitId99: 範囲 を指定するとCommitId1^..CommitId99、Git はこれを「 の親から始まりCommitId1、 までのコミットを含めるCommitId99」と解釈します。

  • CommitId1が通常のコミットである場合、その唯一の親 ( としますCommitId0) が範囲の先頭となり、実質的にCommitId1それ自体は除外されます。
  • CommitId1がマージコミットである場合、CommitId1^依然として最初の親を指します。マージコミットは本質的に 2 つの開発ラインをマージするものであるため、最初の親は直線的な意味で直感的に「前の」コミットではない可能性があるため、これは特に混乱を招く可能性があります。

マージコミットを含む非線形履歴では、マージコミットの最初の親が同じブランチ内の直接の前身ではない可能性があります。

チルダ ( ~) 表記は、 のように使用される場合CommitId1~..CommitId99、実質的には「含めてCommitId1そこから 1 つのコミットに戻る」ことを意味し、ほとんどの場合、CommitId1意図したとおりに範囲に含まれます。

次のコミット履歴を考えてみましょう。ここでは、 はMマージ コミットを表し、各文字は異なるコミットを表します。

A---B---C-------D---E--CommitId99   <- master
     \         /
      X---Y---M                     <- feature (CommitId1 is M)
  • A、、、BCブランチ上のコミットDです。Emaster
  • XはブランチY上のコミットですfeature
  • Mは、ブランチ上のマージコミットであり、からfeatureの変更を にマージします。ここで、は です。masterfeatureMCommitId1

次のコマンドを実行すると:

git cherry-pick CommitId1^..CommitId99
  • CommitId1M
  • CommitId1^の最初の親を参照しますM
  • この場合、 の最初の親はMですD。これは、マージ コミットがマージされた順序で親をリストするためです。

したがって、範囲CommitId1^..CommitId99は に変換されますD..CommitId99。M ( CommitId1) は除外されます。

しかし、 を使用する場合git cherry-pick CommitId1~..CommitId99、 のように範囲のコンテキストで使用すると、CommitId1~..CommitId99「直前のコミットから始まりCommitId1、 までを含むCommitId99」と解釈されます。

は、ブランチのCommitId1~直前のコミット、つまりを指します( は、からへのマージによって ブランチ上に作成されたため)。範囲は に変換されます。MfeatureYMfeaturemasterfeature
CommitId1~..CommitId99Y..CommitId99

~を の範囲内で使用すると、git cherry-pick範囲の開始が の直前のコミットに効果的にシフトされCommitId1、それによって がCommitId1チェリーピックされた範囲に含められます。この動作は、チェリーピック操作にマージコミットを含める場合に特に便利です。

おすすめ記事