次のリポジトリレイアウトがあります。
- マスターブランチ(本番)
- 統合
- ワーキング
私が達成したいのは、作業中のブランチからさまざまなコミットを選択し、それを統合ブランチにマージすることです。私はgitにかなり慣れていないので、リポジトリをめちゃくちゃにせずに、これを正確に行う方法(マージではなく、1つの操作でコミット範囲を簡単に選択する方法)を理解できません。これについてのポインタや考えはありますか?ありがとう!
次のリポジトリレイアウトがあります。
私が達成したいのは、作業中のブランチからさまざまなコミットを選択し、それを統合ブランチにマージすることです。私はgitにかなり慣れていないので、リポジトリをめちゃくちゃにせずに、これを正確に行う方法(マージではなく、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^..D
。
例として「Gitは以前のコミットの範囲からブランチを作成しますか?」を参照してください。
これは
B
ルートコミットではないことを前提としています。unknown revision
そうしないと、「」エラーが発生します。
注:Git 2.9.x / 2.10(2016年第3四半期)以降、孤立したブランチ(空のヘッド)でコミットの範囲を直接選択できます。「既存のブランチをgitで孤立させる方法」を参照してください。
元の回答(2010年1月)
Charles Baileyがここで説明したようにrebase --onto
、統合ブランチの上で特定の範囲のコミットを再生する場合は、Aのほうが良いでしょう。
(また、git rebaseのmanページで「あるブランチに基づくトピックブランチを別のブランチに移植する方法は次のとおりです」の実用的な例を参照してください)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
は以前に指し示していた場所を指します)
これらのコミットの1つが再生されるときに競合がある場合:
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
コミットつだけを受け入れていますが、範囲を選択したい場合はB
経由D
ということでしょうB^..D
ので、gitの専門用語に
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
しかし、とにかく、一連のコミットを「リプレイ」する必要がある場合は、「リプレイ」という言葉rebase
でGitの「」機能を使用するように強制する必要があります。
-m
オプションを必要とする親を持つコミットがある場合、それらのコミットをどのように処理しますか?または、これらのコミットを除外する方法はありますか?
-m
は-m
、このチェリーピック用に選択したパラメーターによって参照されるメインラインを選択することで、それらを処理することになっています。
-m
、コミットの範囲を厳選したときに親コミットにヒットした場合にのみオプションを渡す方法をより適切に表現していると思いますか?現在、私が渡す-m
ようgit cherry-pick a87afaaf..asfa789 -m 1
にすると、それは範囲内のすべてのコミットに適用されます。
error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.
実際にあなたができると実際に気づいたようなものでgit cherry-pick --continue
、それは問題ありません(ただし、親のコミットは含まれません)
git v1.7.2以降、cherry pickはさまざまなコミットを受け入れることができます。
git cherry-pick
コミットの範囲を選択することを学びました(例:cherry-pick A..B
およびcherry-pick --stdin
)git revert
。rebase [-i]
ただし、これらはより良いシーケンス制御をサポートしていません。
cherry-pick A..B
はコミットAを取得しないことに注意してください(必要なA~1..B
場合)。競合が発生した場合、リベースのようにgitは自動的に続行されません(少なくとも1.7.3.1以降)
git cherry-pick A..B C
ことながら、期待どおりに機能しないことにも注意してください。範囲内のすべてを選択してA..B
コミットするわけではありませんC
。これを行うには、最初にgit cherry-pick A..B
、次にの2行に分割する必要がありますgit cherry-pick C
。したがって、範囲がある場合は常に、個別に実行する必要があります。
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
チェリーピックプロセスを続行します。
実際にブランチをマージしたくないですか?作業中のブランチに不要な最近のコミットがある場合は、目的の時点でHEADを使用して新しいブランチを作成できます。
ここで、何らかの理由で本当に一連のコミットを選択したい場合は、エレガントな方法として、パッチセットをプルして新しい統合ブランチに適用するだけです。
git format-patch A..B
git checkout integration
git am *.patch
これは本質的にとにかくgit-rebaseがやっていることですが、ゲームをプレイする必要はありません。マージする必要--3way
があるgit-am
場合は、に追加できます。指示どおりに実行する場合は、これを行うディレクトリに他の* .patchファイルがないことを確認してください...
A^
を含める必要があることに注意してくださいA
。
簡単に実行できるように、私は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ブランチに分割しています。git-cherry-pick
同じリポジトリに2つのツリーがありますが、共有の祖先がないため、この状況で役立ちます。
上記のすべてのオプションでは、マージの競合を解決するように求められます。チームに対してコミットされた変更をマージする場合、開発者からマージの競合を解決して続行することは困難です。ただし、「git merge」は1回でマージを実行しますが、リビジョンの範囲を引数として渡すことはできません。リビジョンのマージ範囲を行うには、「git diff」および「git apply」コマンドを使用する必要があります。パッチファイルの差分が多すぎると「git apply」が失敗するので、ファイルごとにパッチを作成して適用する必要があることを確認しました。スクリプトは、ソースブランチで削除されたファイルを削除できないことに注意してください。これはまれなケースです。そのようなファイルをターゲットブランチから手動で削除できます。パッチを適用できない場合、「git apply」の終了ステータスはゼロではありませんが、
以下はスクリプトです。
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"
私は数日前、Voncの非常に明確な説明を読んだ後、それをテストしました。
dev
:ABCDEFGHIJtarget
:ABCDE
ないH
dev_feature_wo_E_H
git checkout dev
git checkout -b dev_feature_wo_E_H
git rebase --interactive --rebase-merges --no-ff D
どこで置くdrop
のフロントE
とH
リベースエディタでcommit
dev_feature_wo_E_H
をターゲットにコピーする手順。git checkout target
git merge --no-ff --no-commit dev_feature_wo_E_H
commit
cherry-pick
前の日にあまりにも多くのためにそれをやったgit cherry-pick
強力でシンプルですが
merge
最初のコミットと重複するコミットの競合を解決する必要がある場合は、1つまたは2つの場合cherry-pick
は「チェリーピック」でも大丈夫ですが、それ以上の場合は冗長すぎてブランチが複雑になりますgit rebase --onto