Gitでコミットを部分的に選択


501

私は2つの異なるブランチ(リリース開発)に取り組んでいます。

まだリリースブランチにコミットされたいくつかの変更を開発ブランチに統合する必要があることに気付きました。

問題は、すべてのコミットが必要ではなく、特定のファイルの一部のハンクだけなので、簡単です

git cherry-pick bc66559

トリックを行いません。

私がするとき

git show bc66559

私は差分を見ることができますが、それを現在の作業ツリーに部分的に適用する良い方法を本当に知りません。

回答:


792

ここで必要なのはgit add -p-pの同意語です--patch)です。これにより、コンテンツをインタラクティブにチェックインする方法が提供され、各ハンクを入れるかどうかを決定したり、必要に応じてパッチを手動で編集したりすることもできます。

チェリーピックと組み合わせて使用​​するには:

git cherry-pick -n <commit> # get your patch, but don't commit (-n = --no-commit)
git reset                   # unstage the changes from the cherry-picked commit
git add -p                  # make all your choices (add the changes you do want)
git commit                  # make the commit!

(git-cherry-pickに--no-commitオプションがあることを思い出させてくれたTim Heniganに感謝します。また、リセットする必要があることを指摘してくれたFelix Rabeに感謝します! 、git reset <path>...それらのファイルのみをステージング解除するために使用できます。)

もちろんadd -p、必要に応じて特定のパスを指定できます。パッチから始める場合は、cherry-pickをに置き換えることができますapply


あなたが本当にgit cherry-pick -p <commit>(そのオプションが存在しない)を望んでいるなら、あなたは使うことができます

git checkout -p <commit>

これにより、現在のコミットと指定したコミットが比較され、そのdiffのハンクを個別に適用できるようになります。あなたがの一部にしているマージの競合に引っ張っているコミットあなたに興味を持っていないならコミット場合、このオプションは、より有用である可能性がある(ただし、こと。checkoutから異なりcherry-pickcheckout試行を適用し<commit>、完全に内容の、cherry-pickの差分を適用します親からの指定されたコミット。これは、checkoutそのコミットだけではなく、必要以上に適用される可能性があることを意味します。)


実際、私がgit 1.7.5.4のアドバイスに従うと、 'git add -p'は '変更なし'と表示します。「git add」の前に「git reset HEAD」を実行する必要があります-いくつかのオプションでリセットを回避する方法はありますか?
Felix Rabe、2012年

@FelixRabe:私は実際にいくつかの点で驚いているcherry-pick -n明らかにしませんでした -規則があること間違いなく、段階的変化を残す--no-commitオプションが停止し、右コミットする前に、すなわち、すべての変更が上演で。リセットをリセットに追加します。
Cascabel、2012年

1
あなたの現在のブランチ他のブランチではなく、あるコミットがABCDEF、されている場合、ある@Blixt git checkout -p <F>ではないあなたにFから変更を取得するだけで、それはあなたがABCDEFすべて一緒につぶしますし、あなたが望むことのどの部分を整理することができます。それをFからの変更点のいくつかにまで下げるのは苦痛です。一方、git cherry-pick -n <F>はFからの変更のみを取得します。これらの変更の一部が競合する場合、適切にマージする方法を理解できるように役立ちます。
Cascabel 2014年

変更がファイルの追加であった場合、これに問題がありました。git reset段階的なファイルを削除してしまうと、add -pちょうど「を追加することは何も」言わないでしょう。
Ian Grainger 2016


36

必要な変更が変更元のブランチの先頭にあると想定して、git checkoutを使用します

単一ファイルの場合:

git checkout branch_that_has_the_changes_you_want path/to/file.rb

複数のファイルの場合はデイジーチェーンのみ:

git checkout branch_that_has_the_changes_you_want path/to/file.rb path/to/other_file.rb

5
変更だけでなく、ファイル全体がコピーされます。
ジェレミーリスト

1
この回答には、これまでの他の回答と同様に、大きな欠点があります。変更の元の作成者が保持されず、代わりにあなたの名前でコミットされます。変化が良ければ誰かの信用を盗み、変化が悪ければ自分を燃やしている。git cherry-pick -pを使用する方法はないようですが、残念ながらまだありません。
pfalcon、2015年

15

上の構築マイクMonkiewiczの答えは、あなたはまた、付属のSHA1 /ブランチからチェックアウトまで、単一または複数のファイルを指定することができます。

git checkout -p bc66559 -- path/to/file.java 

これにより、現在のバージョンのファイルに適用したい変更をインタラクティブに選択できます。


5
これは、ファイルの現在のバージョンがそのバージョンと大幅に異なる場合に問題になります。多数の無関係な変更(プロンプトには表示されません)が表示されます。また、本当に必要な変更は、元の違いではなく、現在との違いとして「偽装された形式」で表示される場合があります。元の違いが競合する可能性があるため、適切にマージする必要があります。ここではその機会はありません。
Kaz

1

コマンドラインでファイルのリストを指定し、1つのアトミックコマンドですべてを実行したい場合は、以下を試してください。

git apply --3way <(git show -- list-of-files)

--3way:パッチが正しく適用されない場合、Gitはマージの競合を作成して実行できるようになりますgit mergetool。省略--3wayすると、Gitはきれいに適用されないパッチをあきらめます。


0

「部分的なチェリーピッキング」が「ファイル内で、一部の変更を選択し、他の変更を破棄する」ことを意味する場合は、次のコマンドを実行することで実行できますgit stash

  1. 完全なチェリーピックを行います。
  2. git reset HEAD^ 厳選されたコミット全体をステージングされていない作業上の変更に変換します。
  3. git stash save --patch:不要な材料をインタラクティブに選択して隠しておきます。
  4. Gitは作業コピーから隠しておいた変更をロールバックします。
  5. git commit
  6. 不要な変更の隠し場所を捨てますgit stash drop

ヒント:不要な変更の隠し場所に名前を付けると、git stash save --patch junk(6)を忘れてしまった場合は、後でその隠し場所が何であるかがわかります。


ちょうどチェリーピックする場合は、リセットしてください...隠しておくものは何もありません。上記のように、--no-commitでチェリーピックを実行するか、リセット--hard HEAD〜を実行する必要があります。あなたが否定的に進んでいることに注意してください(あなたが望まないものを選択しています)。上記の受け入れられたソリューションは、ポジティブまたはネガティブなアプローチを可能にします。
Pierre-Olivier Vares 2014年

0

関心git format-patchのあるコミットの一部を切り出し、git am別のブランチに適用するために使用します

git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.