私は次のようなリポジトリレイアウトを持っています。
私が達成したいのは、作業ブランチから一連のコミットを選択して統合ブランチにマージすることです。私はgitを使うのが初めてなので、リポジトリを台無しにしないでこれを正確に実行する方法(マージではなく、コミット範囲の簡単な選択)を理解することはできません。これについての何かポインタや考え?ありがとうございます。
さまざまなコミットに関しては、チェリーピッキング は だった実用的ではなかった。
Keith Kim による 以下で説明 のように、Git 1.7.2+はさまざまなコミットをチェリーピックする機能を導入しました(ただし、 の結果に注意する必要があります将来のマージのためのチェリーピッキング )
git cherry-pick」は、さまざまなコミットを選択することを学びました
(例:「cherry-pick A..B
」と「cherry-pick --stdin
」)、「git revert
」;ただし、これらは「rebase [-i]
」が持っている優れたシーケンス制御をサポートしていません。
「
cherry-pick A..B
」形式では、A
NAME__はB
NAME _より古い必要があります。
順序が間違っていると、コマンドは暗黙的に失敗します。
範囲B
name__からD
name__(包括的)を選択する場合は、B^..D
になります。
「 Gitは以前のコミットの範囲からブランチを作成しますか? 」を図として参照してください。
これは、
B
name__がルートコミットではないことを前提としています。そうしないと、「unknown revision
」エラーが表示されます。
注:Git 2.9.x/2.10(2016年第3四半期)では、孤立したブランチ(空のヘッド)で直接コミット範囲をチェリーピックできます。「 gitで既存のブランチを孤立させる方法 」を参照してください_ "。
元の回答(2010年1月)
Charles Baileyがここで説明します のように、統合ブランチ上で指定されたコミット範囲を再生する場合は、rebase --onto
の方が適しています。
(また、 git rebase man page の「あるブランチに基づくトピックブランチを別のブランチに移植する方法を探してください。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
name__」まで(working
name__ブランチから、リプレイしたい最後のコミットを指します)「tmp
name __」(integration
name__が以前指していた場所を指します)
これらのコミットの1つが再生されるときに競合が発生した場合:
git rebase --continue
」を実行します。git rebase --skip
」を実行しますgit rebase --abort
」ですべてをキャンセルします(そしてintegration
name__ブランチにtmp
name__ブランチを戻します)その後、rebase --onto
、integration
name__は、統合ブランチの最後のコミット(つまり、「tmp
name__」ブランチ+再生されたすべてのコミット)に戻ります。
チェリーピッキングまたはrebase --onto
では、 ここで説明します のように、後続のマージに影響を与えることを忘れないでください。
純粋な "cherry-pick
"ソリューションは ここで説明します であり、次のようなものが含まれます。
パッチアプローチを使用する場合は、「git format-patch | git am」および「git cherry」がオプションです。
現在、git cherry-pick
は単一のコミットのみを受け入れますが、git lingoでB^..D
になるB
name__からD
name__の範囲を選択する場合は、
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
とにかく、コミットの範囲を「リプレイ」する必要がある場合、Wordの「リプレイ」はGitの「rebase
name__」機能を使用するようにプッシュする必要があります。
Git v1.7.2から、cherry pickは様々な範囲のコミットを受け入れることができます。
git cherry-pick
は、ある範囲のコミットを選ぶことを学びました(例:cherry-pick A..B
とcherry-pick --stdin
)、そしてgit revert
;ただし、これらはrebase [-i]
のより優れたシーケンス制御をサポートしていません。
実際にブランチをマージしたくないと思いますか。作業中のブランチに最近必要ないコミットがある場合は、必要な時点でHEADを付けて新しいブランチを作成するだけです。
さて、あなたが本当にいろいろなコミットを選択したいのであれば、何らかの理由で、これを行うための優雅な方法はただパッチセットを引っ張ってあなたの新しい統合ブランチにそれを適用することです:
git format-patch A..B
git checkout integration
git am *.patch
これは本質的にとにかくgit-rebaseがやっていることですが、ゲームをする必要はありません。マージする必要がある場合は、--3way
をgit-am
に追加することができます。逐語的な指示に従っている場合は、これを行うディレクトリに他の* .patchファイルがまだないことを確認してください。
2つの枝があるとします。
"branchA":コピーしたいコミットを含みます( "commitA"から "commitB"へ)。
"branchB":コミットしたいブランチを "branchA"から転送したいブランチ
1)
git checkout <branchA>
2) "commitA"と "commitB"のIDを取得する
3)
git checkout <branchB>
4)
git cherry-pick <commitA>^..<commitB>
5)矛盾がある場合は、それを解決してタイプします。
git cherry-pick --continue
チェリーピックプロセスを続行します。
簡単に実行できるように、 VonCのコード を短いbashスクリプトgit-multi-cherry-pick
にラップしました。
#!/bin/bash
if [ -z $1 ]; then
echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
echo "";
echo "Usage: $0 start^..end";
echo "";
exit 1;
fi
git rev-list --reverse --topo-order $1 | while read rev
do
git cherry-pick $rev || break
done
私は現在、これを使っているのですが、同じsvnトランクの中でサードパーティーのコードとカスタマイズの両方が混在したプロジェクトの歴史を再構築しています。今後のカスタマイズの理解を深めるために、コアのサードパーティコード、サードパーティのモジュール、およびカスタマイズを独自のgitブランチに分割しています。同じリポジトリに2つのツリーがありますが、共有された先祖はいないので、git-cherry-pick
はこの状況で役に立ちます。
上記のすべてのオプションにより、マージの競合を解決するように求められます。チームにコミットされた変更をマージしている場合、開発者からマージの競合を解決して先に進むことは困難です。しかし、 "git merge"はマージをワンショットで行いますが、ある範囲のリビジョンを引数として渡すことはできません。 revのマージ範囲を指定するには、 "git diff"コマンドと "git apply"コマンドを使用する必要があります。パッチファイルに含まれるファイルの数が多すぎると "git apply"が失敗することに気付いたので、ファイルごとにパッチを作成してから適用する必要があります。スクリプトはソースブランチで削除されたファイルを削除することはできません。これはめったにありませんが、手動でターゲットブランチからそのようなファイルを削除することができます。パッチを適用できない場合、 "git apply"の終了ステータスはゼロではありませんが、-3wayオプションを使用すると3ウェイマージにフォールバックするので、この失敗について心配する必要はありません。
以下はそのスクリプトです。
enter code here
#!/bin/bash
# This script will merge the diff between two git revisions to checked out branch
# Make sure to cd to git source area and checkout the target branch
# Make sure that checked out branch is clean run "git reset --hard HEAD"
START=$1
END=$2
echo Start version: $START
echo End version: $END
mkdir -p ~/temp
echo > /tmp/status
#get files
git --no-pager diff --name-only ${START}..${END} > ~/temp/files
echo > ~/temp/error.log
# merge every file
for file in `cat ~/temp/files`
do
git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
if [ $? -ne 0 ]
then
# Diff usually fail if the file got deleted
echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
echo Skipping the merge: git diff command failed for $file
echo "STATUS: FAILED $file" >> /tmp/status
echo "STATUS: FAILED $file"
# skip the merge for this file and continue the merge for others
rm -f ~/temp/git-diff
continue
fi
git apply --ignore-space-change --ignore-whitespace --3way --allow-binary-replacement ~/temp/git-diff
if [ $? -ne 0 ]
then
# apply failed, but it will fall back to 3-way merge, you can ignore this failure
echo "git apply command filed for $file"
fi
echo
STATUS=`git status -s $file`
if [ ! "$STATUS" ]
then
# status is null if the merged diffs are already present in the target file
echo "STATUS:NOT_MERGED $file"
echo "STATUS: NOT_MERGED $file$" >> /tmp/status
else
# 3 way merge is successful
echo STATUS: $STATUS
echo "STATUS: $STATUS" >> /tmp/status
fi
done
echo GIT merge failed for below listed files
cat ~/temp/error.log
echo "Git merge status per file is available in /tmp/status"
別の選択肢は、戦略の範囲内でコミットするために戦略のマージとマージしてから、その範囲の最後のコミットと '通常の'マージを行うことです(または最後のコミットの場合は分岐します)。そのため、masterの2345と3456のコミットだけがfeatureブランチにマージされると仮定します。
マスター: 1234 2345 3456 4567
機能ブランチで:
git merge -s ours 4567 git merge 2345