リポジトリのレイアウトは次のとおりです。
- マスターブランチ(本番)
- 統合
- 働く
私が実現したいのは、作業ブランチからコミットの範囲をチェリーピックして、それを統合ブランチにマージすることです。私は 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
。
順序が間違っていると、コマンドは暗黙的に失敗します。
から までの範囲( を含む)B
D
B
を選択する場合は、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
戻します)integration
tmp
その後rebase --onto
、統合ブランチの最後のコミット(つまり「 」ブランチ + 再生されたすべてのコミット)integration
に戻ります。tmp
チェリーピッキングやではrebase --onto
、その後のマージに影響があることを忘れないでください。ここで説明。
純粋な「cherry-pick
」ソリューションはここで議論された、次のような内容になります。
パッチ アプローチを使用する場合は、「git format-patch|git am」と「git cherry」がオプションです。
現在、は単一のコミットのみを受け入れますが、から までのgit cherry-pick
範囲を選択する場合は、 Git 用語では次のようになります(実際は、以下を参照してください)。B
D
B^..D
B~..D
git rev-list --reverse --topo-order B~..D | while read rev
do
git cherry-pick $rev || break
done
しかし、いずれにせよ、コミットの範囲を「再生」する必要がある場合、「再生」という言葉は、rebase
Git の「 」機能を使用するように促すはずです。
^
警告:範囲を包括的にするためにカラット ( ) を使用するという上記の提案に惑わされないでください。
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
、、、B
はC
ブランチ上のコミットD
です。E
master
X
はブランチY
上のコミットですfeature
。M
は、ブランチ上のマージコミットであり、からfeature
の変更を にマージします。ここで、は です。master
feature
M
CommitId1
次のコマンドを実行すると:
git cherry-pick CommitId1^..CommitId99
CommitId1
はM
。CommitId1^
の最初の親を参照しますM
。- この場合、 の最初の親は
M
ですD
。これは、マージ コミットがマージされた順序で親をリストするためです。
したがって、範囲CommitId1^..CommitId99
は に変換されますD..CommitId99
。M ( CommitId1
) は除外されます。
しかし、 を使用する場合git cherry-pick CommitId1~..CommitId99
、 のように範囲のコンテキストで使用すると、CommitId1~..CommitId99
「直前のコミットから始まりCommitId1
、 までを含むCommitId99
」と解釈されます。
は、ブランチのCommitId1~
直前のコミット、つまりを指します( は、からへのマージによって ブランチ上に作成されたため)。範囲は に変換されます。M
feature
Y
M
feature
master
feature
CommitId1~..CommitId99
Y..CommitId99
~
を の範囲内で使用すると、git cherry-pick
範囲の開始が の直前のコミットに効果的にシフトされCommitId1
、それによって がCommitId1
チェリーピックされた範囲に含められます。この動作は、チェリーピック操作にマージコミットを含める場合に特に便利です。