web-dev-qa-db-ja.com

コミットの範囲を選択して別のブランチにマージする方法

私は次のようなリポジトリレイアウトを持っています。

  • マスターブランチ(製造)
  • 統合
  • ワーキング

私が達成したいのは、作業ブランチから一連のコミットを選択して統合ブランチにマージすることです。私はgitを使うのが初めてなので、リポジトリを台無しにしないでこれを正確に実行する方法(マージではなく、コミット範囲の簡単な選択)を理解することはできません。これについての何かポインタや考え?ありがとうございます。

557
crazybyte

さまざまなコミットに関しては、チェリーピッキング  だった実用的ではなかった。

Keith Kim による 以下で説明 のように、Git 1.7.2+はさまざまなコミットをチェリーピックする機能を導入しました(ただし、 の結果に注意する必要があります将来のマージのためのチェリーピッキング

git cherry-pick」は、さまざまなコミットを選択することを学びました
(例:「cherry-pick A..B」と「cherry-pick --stdin」)、「git revert」;ただし、これらは「rebase [-i]」が持っている優れたシーケンス制御をサポートしていません。

ダミアンコメント そして警告します:

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

範囲Bname__からDname__(包括的)を選択する場合は、B^..Dになります。
Gitは以前のコミットの範囲からブランチを作成しますか? 」を図として参照してください。

Jubobsコメント内の に言及しているように:

これは、Bname__がルートコミットではないことを前提としています。そうしないと、「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):リプレイする最初のコミット
  • integrationname__」まで(workingname__ブランチから、リプレイしたい最後のコミットを指します)

tmpname __」(integrationname__が以前指していた場所を指します)

これらのコミットの1つが再生されるときに競合が発生した場合:

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

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

チェリーピッキングまたはrebase --ontoでは、 ここで説明します のように、後続のマージに影響を与えることを忘れないでください。


純粋な "cherry-pick"ソリューションは ここで説明します であり、次のようなものが含まれます。

パッチアプローチを使用する場合は、「git format-patch | git am」および「git cherry」がオプションです。
現在、git cherry-pickは単一のコミットのみを受け入れますが、git lingoでB^..DになるBname__からDname__の範囲を選択する場合は、

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

とにかく、コミットの範囲を「リプレイ」する必要がある場合、Wordの「リプレイ」はGitの「rebasename__」機能を使用するようにプッシュする必要があります。

735
VonC

Git v1.7.2から、cherry pickは様々な範囲のコミットを受け入れることができます。

git cherry-pickは、ある範囲のコミットを選ぶことを学びました(例:cherry-pick A..Bcherry-pick --stdin)、そしてgit revert;ただし、これらはrebase [-i]のより優れたシーケンス制御をサポートしていません。

126
Keith Kim

実際にブランチをマージしたくないと思いますか。作業中のブランチに最近必要ないコミットがある場合は、必要な時点でHEADを付けて新しいブランチを作成するだけです。

さて、あなたが本当にいろいろなコミットを選択したいのであれば、何らかの理由で、これを行うための優雅な方法はただパッチセットを引っ張ってあなたの新しい統合ブランチにそれを適用することです:

git format-patch A..B
git checkout integration
git am *.patch

これは本質的にとにかくgit-rebaseがやっていることですが、ゲームをする必要はありません。マージする必要がある場合は、--3waygit-amに追加することができます。逐語的な指示に従っている場合は、これを行うディレクトリに他の* .patchファイルがまだないことを確認してください。

25
djs

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

チェリーピックプロセスを続行します。

17
KostasA

簡単に実行できるように、 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はこの状況で役に立ちます。

8
Adam Franco

上記のすべてのオプションにより、マージの競合を解決するように求められます。チームにコミットされた変更をマージしている場合、開発者からマージの競合を解決して先に進むことは困難です。しかし、 "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"
3

別の選択肢は、戦略の範囲内でコミットするために戦略のマージとマージしてから、その範囲の最後のコミットと '通常の'マージを行うことです(または最後のコミットの場合は分岐します)。そのため、masterの2345と3456のコミットだけがfeatureブランチにマージされると仮定します。

マスター:
 1234 
 2345 
 3456 
 4567 

機能ブランチで:

 git merge -s ours 4567 
 git merge 2345 
1
Koos Vriezen