761

次のリポジトリ レイアウトがあります。

  • マスター ブランチ (本番)
  • 統合
  • 働く

私が達成したいのは、作業ブランチからさまざまなコミットを厳選し、それを統合ブランチにマージすることです。私はgitにかなり慣れていないので、リポジトリを台無しにせずにこれを正確に行う方法(マージではなく、1回の操作でコミット範囲をチェリーピッキングする)を理解できません。これに関する指針や考えはありますか?ありがとう!

4

10 に答える 10

935

コミットの範囲に関しては、チェリーピッキング 実用的ではありませんでした。

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

git cherry-pick" は、コミットの範囲
(例: " cherry-pick A..B" と " cherry-pick --stdin") を選択することを学び、" " もそうでしたgit revert; ただし、これらは " " が持っているより優れたシーケンス コントロールをサポートしていませんrebase [-i]

damian はコメントし、次のように警告しています。

" cherry-pick A..B" 形式でAは、 より古い必要がありBます。
それらが間違った順序である場合、コマンドは黙って失敗します。

( を含む)を介して範囲BDBを選択したい場合、それはB^..D(の代わりにB..D)になります。例として、「以前のコミットの範囲からブランチを作成しますか?
」を 参照してください。

Jubobsがコメントで言及しているように:

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戻します)integrationtmp

その後rebase --ontointegration統合ブランチの最後のコミット (つまり、" 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 の「 」機能を使用するよう促す必要があります。

于 2010-01-03T10:08:04.123 に答える
150

git v1.7.2 以降、cherry pick はさまざまなコミットを受け入れることができます。

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

于 2010-09-08T03:57:31.417 に答える
33

実際にブランチをマージしたくありませんか? 作業ブランチに不要な最近のコミットがある場合は、必要な時点で HEAD を持つ新しいブランチを作成できます。

さて、何らかの理由でコミットの範囲を本当に厳選したい場合、これを行うためのエレガントな方法は、パッチセットをプルして新しい統合ブランチに適用することです。

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

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

于 2010-01-04T09:32:52.277 に答える
9

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.

于 2010-03-31T14:20:56.950 に答える
4

Vonc の非常に明確な説明を読んだ後、私は数日前にそれをテストしました。

私の歩み

始める

  • 支店dev: ABCDEFGHIJ
  • 支店target:ABCD
  • 私も欲しくEないH

分岐でステップ E および 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リベースエディターのdropEと中に置いた場所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
于 2020-02-15T09:36:15.167 に答える
3

上記のすべてのオプションでは、マージの競合を解決するように求められます。チームのためにコミットされた変更をマージする場合、開発者からマージの競合を解決して続行することは困難です。ただし、「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"
于 2016-09-07T06:48:01.473 に答える
1

別のオプションは、私たちの戦略で範囲の前のコミットにマージし、次にその範囲の最後のコミットと「通常の」マージ (または最後のコミットの場合は分岐) することです。したがって、 master の 2345 および 3456 コミットのみが機能ブランチにマージされるとします。

主人:
1234
2345
3456
4567

機能ブランチ:

git merge -s 私たちのもの 4567
gitマージ2345
于 2015-08-16T12:47:03.700 に答える