次のリポジトリ レイアウトがあります。
- マスター ブランチ (本番)
- 統合
- 働く
私が達成したいのは、作業ブランチからさまざまなコミットを厳選し、それを統合ブランチにマージすることです。私はgitにかなり慣れていないので、リポジトリを台無しにせずにこれを正確に行う方法(マージではなく、1回の操作でコミット範囲をチェリーピッキングする)を理解できません。これに関する指針や考えはありますか?ありがとう!
次のリポジトリ レイアウトがあります。
私が達成したいのは、作業ブランチからさまざまなコミットを厳選し、それを統合ブランチにマージすることです。私はgitにかなり慣れていないので、リポジトリを台無しにせずにこれを正確に行う方法(マージではなく、1回の操作でコミット範囲をチェリーピッキングする)を理解できません。これに関する指針や考えはありますか?ありがとう!
コミットの範囲に関しては、チェリーピッキングは 実用的ではありませんでした。
以下で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
は、 より古い必要がありB
ます。
それらが間違った順序である場合、コマンドは黙って失敗します。
( を含む)を介して範囲B
D
B
を選択したい場合、それはB^..D
(の代わりにB..D
)になります。例として、「以前のコミットの範囲からブランチを作成しますか?
」を
参照してください。
B
これは、ルートコミットではないことを前提としています。そうしないと、"unknown revision
" エラーが発生します。
注: Git 2.9.x/2.10 (2016 年第 3 四半期) の時点で、孤立したブランチ (空のヘッド) でコミットの範囲を直接選択できます: 「git で既存のブランチを孤立させる方法」を参照してください。
元の回答 (2010 年 1 月)
チャールズ・ベイリーがここで説明したようにrebase --onto
、統合ブランチの上で特定の範囲のコミットを再生する場合は、より良いでしょう。(また、 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
前に指していた場所を指す)
これらのコミットのいずれかがリプレイされたときに競合が発生した場合:
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 lingo になるので、
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
とにかく、一連のコミットを「再生」する必要がある場合は、「再生」という言葉を聞いてrebase
、Git の「 」機能を使用するよう促す必要があります。
git v1.7.2 以降、cherry pick はさまざまなコミットを受け入れることができます。
git cherry-pick
コミットの範囲を選択することを学びました (例:cherry-pick A..B
andcherry-pick --stdin
)git revert
。ただし、これらはより優れたシーケンス制御をサポートしていませんrebase [-i]
。
実際にブランチをマージしたくありませんか? 作業ブランチに不要な最近のコミットがある場合は、必要な時点で HEAD を持つ新しいブランチを作成できます。
さて、何らかの理由でコミットの範囲を本当に厳選したい場合、これを行うためのエレガントな方法は、パッチセットをプルして新しい統合ブランチに適用することです。
git format-patch A..B
git checkout integration
git am *.patch
これは基本的に git-rebase が行っていることですが、ゲームをプレイする必要はありません。--3way
マージする必要がgit-am
ある場合は、追加できます。指示に従っている場合は、これを行うディレクトリに他の *.patch ファイルが既に存在しないことを確認してください...
I wrapped VonC's code into a short bash script, git-multi-cherry-pick
, for easy running:
#!/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
I'm currently using this as I rebuild the history of a project that had both 3rd-party code and customizations mixed together in the same svn trunk. I'm now splitting apart core 3rd party code, 3rd party modules, and customizations onto their own git branches for better understanding of customizations going forward. git-cherry-pick
is helpful in this situation since I have two trees in the same repository, but without a shared ancestor.
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
の場合は "cherry-picking" で問題ありませんが、それ以上の場合は冗長すぎて、ブランチが複雑になりすぎます。git rebase --onto
上記のすべてのオプションでは、マージの競合を解決するように求められます。チームのためにコミットされた変更をマージする場合、開発者からマージの競合を解決して続行することは困難です。ただし、「git merge」はマージを一発で行いますが、リビジョンの範囲を引数として渡すことはできません。リビジョンの範囲をマージするには、「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"
別のオプションは、私たちの戦略で範囲の前のコミットにマージし、次にその範囲の最後のコミットと「通常の」マージ (または最後のコミットの場合は分岐) することです。したがって、 master の 2345 および 3456 コミットのみが機能ブランチにマージされるとします。
主人: 1234 2345 3456 4567
機能ブランチ:
git merge -s 私たちのもの 4567 gitマージ2345