2

ブランチから別のブランチへの単一のコミットをチェリーピックしたいと思います。ファイルの名前変更は非常に一般的だと思いますが、それでも人間の介入なしに変更を適用できるようにしたいと考えています。

組み込みの cherry-pick コマンドは、特に名前が変更されたファイルへの変更と組み合わされた場合に、名前の変更を検出するように縫い付けられていないため (少なくとも私のテスト ケースでは)。

少し試してみたところ、最終的に 2 つのリベース操作を含む解決策にたどり着きました。

チェリーピックを適用したいコミットを指すターゲットという名前のブランチがあるとしましょう。チェリーピックしたいコミットは、sourceという名前のブランチによって指されています。

次に、次のコマンドを実行します。

  1. source と同じコミットを指すブランチ sourceTemp を作成します (ブランチ source を保持したいため)
  2. git rebase --strategy="recursive" --strategy-option="rename-threshold=30" target sourceTemp(おそらく別のしきい値を使用します。テストファイルは非常に小さいため、変更は比較的大きくなります)
  3. git rebase --onto target sourceTemp~ sourceTemp

これは、ブランチsourceからtargetへの最後のコミットによって導入された変更のみを適用します。

また、テストを github に置きました。

https://github.com/fraschfn/cherry-pick

私が知りたいのは、このアプローチが実現可能かどうか、または私の単純なテスト設定でのみ機能するかどうかです!

更新:代替方法

sourcetargetのマージ ベースにパッチをリベースします。

開始状況

    A - B     <--- target
   /
  M 
   \
    C - D     <--- source

DをBにチェリーピックしたい.

  1. 新しいブランチパッチを作成した後、D を M にリベースします

      A - B     <--- target
     /
    M - D'      <--- patch
     \
      C - D     <--- source
    
  2. ソースの置換を取得するために C と D' をマージします

    B と D' をマージして、ターゲットのパッチを適用したバージョンを取得します

       A - B    <--- target
      /     \
     /      E   <--- patched target
    /      / 
    M -  D'     <--- patch
     \    \
      \   F     <--- new source (same snapshot as source different history)
       \ /
        C - D   <--- source (will be discarded)
    

利点は、E と F を問題なくマージできるようになったことです。別の方法: 階層のできるだけ早い段階でパッチを含めて、D を作成せずに直接 D' を作成し、リベースを保存します。

以前のバージョンより上の利点は、「新しいソース」と「パッチを適用したターゲット」の 2 つのブランチをマージでき、それが機能し (もちろんソースとターゲットのマージが機能する場合)、git が認識しているため、同じ変更セットを 2 回導入しないことです。変更セットを両方のブランチに導入したマージ操作が原因です。

4

4 に答える 4

7

あなたのrename-thresholdアプローチは、あなたがやろうとしていることに対して実行可能なものです。ブランチがマージされないフォークされたプロジェクトでない限り、ブランチ間で書かれた定期的なチェリーピックは、決して持続可能なワークフローではありません。その場合は、頑張ってください。ブランチをマージしてまとまりのある全体に戻すことが予想される場合は、コードのフロー方法を変更することをお勧めします。これに関するいくつかの優れたリソースを次に示します。

  1. git プロジェクトのドキュメント。

  2. これらの部分で非常に人気のある gitflow モデル。

  3. 分散ワークフローに関する ProGit の章。

ブランチ間の定期的なチェリー ピッキングにより、異なる SHA1 ハッシュを使用して同一の変更セットが生成されます。十分な期間にわたって十分にスケーリングすると、コードの追跡が難しくなり、履歴を理解することはほぼ不可能になり、ブランチをマージすると、MC エッシャーの絵の中で意識を失い、目が覚めたような気分になります。ちょうど良い時期ではありません。

この回答に対するあなたのコメントに基づいて、あなたのユースケースはチェリーピックの実行可能なもののように思えます. その場合、名前を変更したファイルにパッチ セットを適用するための、労力を少し軽減する方法をお勧めします。

git checkout branchB
git diff <commit>~1 <commit> |
    sed 's:<path_on_branchA>:<path_on_branchB>:g' |
    git apply

どこからに<commit>移動したいコミットです。この方法では、コミット メタデータを取得しません。つまり、変更を適用するだけで、コミットはしません。しかし、との出力を同じように簡単に操作できます。あなたが何をしたいかによって異なります。branchAbranchBgit format-patchsedgit am

これにより、一時ブランチを生成し、正しい名前変更のしきい値を選択し、リベースする手間が省けます。ストレートアップほどきれいになることはありませんがgit merge、以前に使用したことがあり、コツをつかめば非常に簡単です。

于 2012-08-02T12:47:42.543 に答える
2

git rebaseと同じ名前変更検出ロジックを使用し、それを制御するためにgit merge同じオプションさえ使用します。--strategy-option="rename-threshold=30"

ここで何が起こっているかというと、最初にソース ブランチ全体をターゲット ブランチに (as としてsourceTemp) リベースし、基本的に最後のコミットを にチェリー ピックしますtarget。ちなみに、はon でgit rebase -onto target src~ src行うのと同じです: at で 1 つのコミットを取得し、それを に適用します。git cherry-pick srctargetsrctarget

チェリー ピックを直接選択する場合と比べて、ワークフローがどのように変化するかは、ソース ブランチでコミットを徐々に処理することです。直接チェリーピックする場合、 と の違いを見て、名前の変更を認識する必要がありtargetますsrc~。最初にソース ブランチ全体をリベースするとき、名前の変更は小さなステップで処理されます。

于 2014-03-28T13:02:17.310 に答える
0

場合によっては、ファイルが分岐しすぎて名前変更アルゴリズムが名前変更を正確に判断できない場合があるため、「ハンマー アプローチ」を使用します。

git checkout target

# get list of files modified in source branch
git diff source^ source --name-only

# ... (A) rename corresponding target files to match source's ones (possibly write script to automate this)

git add -A
git commit -m "Temporarily rename files for cherry-pick purposes"

# There is no need for rename detection anymore
git cherry-pick source

# ... resolve all conflicts and finish the cherry-pick

# ... rename files back, reverting (A) step. Note that you cannot use `git revert` here

git add -A
git commit -m "Rename files back"

# Get rid of the artificial commits made but preserve file changes
git reset --soft HEAD~3

# Make a commit to store cherry-picked files
git commit -m "Cherry-pick source branch"
于 2015-10-17T13:15:53.340 に答える
0
git checkout target

# Make an artificial merge to link target and source's parent
git merge source^ --strategy=ours

# Equivalent to cherry-pick but includes rename detection
git merge source --no-ff

# ... fix all merge conflicts and finish the merge

# Get rid of those artificial merges preserving file changes
git reset --soft HEAD~2

# Make a commit to store cherry-picked files
git commit -m "Cherry-pick source branch"
于 2015-10-17T10:35:49.623 に答える