特定のファイルへの変更のみをgit-cherry-pickする方法は?


587

複数のファイルへの変更を含む特定のコミットで変更された一部のファイルのみに加えられた変更をGitブランチにマージしたい場合、どのようにしてこれを達成できますか?

Gitのが呼ばコミットと仮定すると、stuffファイルへの変更があるABC、とD私はマージしたいが、stuffファイルへの変更をAしてB。それはのための仕事のように聞こえるgit cherry-pickが、cherry-pickファイルのみのサブセット、全体のコミットをしませマージする方法を知っています。

回答:


689

cherry-pick -n--no-commit)でそれを行うと、コミットする前に結果を検査(および変更)できます。

git cherry-pick -n <commit>

# unstage modifications you don't want to keep, and remove the
# modifications from the work tree as well.
# this does work recursively!
git checkout HEAD <path>

# commit; the message will have been stored for you by cherry-pick
git commit

変更の大部分が不要な場合は、個々のパスをチェックアウトするのではなく(中央のステップ)、すべてをリセットしてから、必要なものを追加できます。

# unstage everything
git reset HEAD

# stage the modifications you do want
git add <path>

# make the work tree match the index
# (do this from the top level of the repo)
git checkout .

10
それに加えて、チェリーピックされたコミットによって導入された不要で新しいファイルを削除するgit checkout .ことをお勧めしgit clean -fます。
rlat 2015

4
後者の方法に関する追加の注意:ファイルごとgit add -pにインデックスに追加する変更をインタラクティブに決定できるようにするために使用します
matthaeus

6
チェリーピックのコミットが現在の作業コピーに適用されない場合、これは非常に異なるため、これはそれほど大きくありませんが、1つのファイルきれいに適用されます。
限定的な贖罪

3
を使用して選択的にアンステージングすることもできgit reset -p HEADます。これは同等のものですadd -pが、存在することを知っている人はほとんどいません。
PatrickSchlüter2017年

1
非常に便利なトリック。誰かがクイックスクリプトとしてそれを必要とする場合に備えて、それを要旨に入れましたgist.github.com/PiDayDev/68c39b305ab9d61ed8bb2a1195ee1afc
Damiano

146

コミットには多くの変更があり、他の多くのファイルとの競合があったため、他の方法ではうまくいきませんでした。私が思いついたのは単に

git show SHA -- file1.txt file2.txt | git apply -

実際にaddはファイルもコミットもしないので、フォローアップする必要があるかもしれません

git add file1.txt file2.txt
git commit -c SHA

または、追加をスキップする場合は、--cached引数を使用してgit apply

git show SHA -- file1.txt file2.txt | git apply --cached -

ディレクトリ全体に対して同じことを行うこともできます

git show SHA -- dir1 dir2 | git apply -

2
興味深い方法、ありがとう。しかし、しないshow SHA -- file | apply、基本的には同じことを行うcheckout SHA -- fileのようにマークLongairの答え
Tobias Kienzler 2015

4
いいえ、checkout SHA -- fileSHAでバージョンを正確にチェックアウトしshow SHA -- file | applyますが、(cherry-pickと同様に)SHAの変更のみを適用します。(a)ソースブランチにあるファイルを変更するコミットが複数あるか、または(b)現在のターゲットブランチにあるファイルを変更するコミットがあるかが重要です。
マイケルアンダーソン、

9
これの別の優れた使用法が見つかりました:選択的な復元、1つのファイルのみを復元したい場合(git revertコミット全体を元に戻すため)。その場合は単に使用してくださいgit show -R SHA -- file1.txt file2.txt | git apply -
マイケルアンダーソン

2
@RoeiBahumiという意味はかなり異なります。git diff SHA -- file1.txt file2.txt | git apply -は、ファイルの現在のバージョンとSHAのバージョンのすべての違いを現在のバージョンに適用することを意味します。本質的にはと同じgit checkout SHA -- file1.txt file2.txtです。それがgit showバージョンと異なる理由については、以前のコメントを参照してください。
マイケルアンダーソン

5
競合を解決する必要がある場合は、のgit apply -3 -代わりにgit apply -を使用します。競合が発生した場合は、などの標準の競合解決手法を使用できgit mergetoolます。
qwertzguy

87

私は通常-p、他のブランチからのgitチェックアウトでフラグを使用します。これは、他のほとんどの方法よりも簡単で詳細です。

原則として:

git checkout <other_branch_name> <files/to/grab in/list/separated/by/spaces> -p

例:

git checkout mybranch config/important.yml app/models/important.rb -p

次に、「ブロブ」でどの変更を希望するかを尋ねるダイアログが表示されます。これは、コードの各チャンクに対してシグナルy(はい)n(いいえ)などを通知できる継続的なコード変更のチャンクすべてに対してほぼうまく機能します。

-pまたはpatchオプションは含めてのgitのコマンドのさまざまな作品git stash save -pあなたがあなたの現在の仕事から隠したいものを選択することができます

多くの作業を行って、それを分離しgit add -p、各コミットに必要なものを使用して選択する、トピックベースのコミットにコミットしたい場合は、このテクニックを時々使用します:)


3
私は定期的にを使用していますが、フラグがあるgit-add -pことgit-checkoutも知りませんでした。これにより、非回答に-pあるマージの問題が修正されますか?-p
Tobias Kienzler 2016年

1
少なくとも-p、このような矛盾するセクションを手動で編集できるようにしますcherry-pick。次回は必要なときにテストします。これは間違いなく興味深いアプローチです
Tobias Kienzler

2
ブランチへの同時変更を殺さない2つの最良の回答の1つ。
akostadinov 2016年

1
適用するハンクかを選択する方法については、この回答を参照してください: stackoverflow.com/a/10605465/4816250 特に「S」オプションは非常に有用でした。
jvd10

1
git reset -p HEADまた-p、インデックスから一部のパッチのみを削除したい場合に、を使用すると便利です。
PatrickSchlüter2017年

42

おそらくJefromiの答えに対するこの方法の利点は、git reset のどの動作が正しいものであるかを覚えておく必要がないことです:)

 # Create a branch to throw away, on which we'll do the cherry-pick:
 git checkout -b to-discard

 # Do the cherry-pick:
 git cherry-pick stuff

 # Switch back to the branch you were previously on:
 git checkout -

 # Update the working tree and the index with the versions of A and B
 # from the to-discard branch:
 git checkout to-discard -- A B

 # Commit those changes:
 git commit -m "Cherry-picked changes to A and B from [stuff]"

 # Delete the temporary branch:
 git branch -D to-discard

2
ご回答有難うございます。今、私は考えるようになりました、スキップしcherry-pickて直接使用してみgit checkout stuff -- A Bませんか?またgit commit -C stuff、コミットメッセージも同じままです
Tobias Kienzler '19

8
@Tobias:それは上の変更されたファイルがいる場合にのみ動作しますstuffあなたの現在のブランチ上またはどこかの共通の祖先の間で変更されていないHEADstuffの先端stuff。彼らが持っている場合は、cherry-pickあなたの方法は、現在のブランチの変更を捨て、そして共通の祖先からの変更のすべてを維持するだろうが、正しい結果(マージの本質的結果)を作成し、stuffその中のものだけでなく-シングルコミット。
Cascabel

2
@トビアス・キエンツラー:私はあなたの出発点が親と十分に異なっていてstuff、チェリーピックの結果が残り、コミットの内容ABは異なる内容であると想定していましたstuff。しかし、それがまったく同じである場合、あなたは正しいです-あなたは言うように行うことができます。
Mark Longair、2011

@ Jeromi、@ Mark:フィードバックに感謝します。私の場合は、完全に分離したファイルを使用してブランチを処理しているため、私の提案につながりました。しかし、確かに私は遅かれ​​早かれそれに問題を抱えていたので、これを取り上げてくれてありがとう
Tobias Kienzler

私が考えるこの他のスレッドでは、私の答えはあなたが後にしているものかもしれません。
Ian

30

チェリーピックは、特定の「コミット」から変更を選択することです。最も簡単な解決策は、特定のファイルのすべての変更を選択して使用することです

 git checkout source_branch <paths>...

例では:

$ git branch
* master
  twitter_integration
$ git checkout twitter_integration app/models/avatar.rb db/migrate/20090223104419_create_avatars.rb test/unit/models/avatar_test.rb test/functional/models/avatar_test.rb
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   app/models/avatar.rb
#   new file:   db/migrate/20090223104419_create_avatars.rb
#   new file:   test/functional/models/avatar_test.rb
#   new file:   test/unit/models/avatar_test.rb
#
$ git commit -m "'Merge' avatar code from 'twitter_integration' branch"
[master]: created 4d3e37b: "'Merge' avatar code from 'twitter_integration' branch"
4 files changed, 72 insertions(+), 0 deletions(-)
create mode 100644 app/models/avatar.rb
create mode 100644 db/migrate/20090223104419_create_avatars.rb
create mode 100644 test/functional/models/avatar_test.rb
create mode 100644 test/unit/models/avatar_test.rb

ソースと完全な説明http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-another-branch/

更新:

この方法では、gitはファイルをマージせず、宛先ブランチで行われた他の変更をオーバーライドするだけです。変更を手動でマージする必要があります:

$ git diff HEAD filename


5
私もそう考えましたが、現在のブランチの変更が破棄されるため、両方のブランチでファイルが変更されていると、これはひどく失敗します
Tobias Kienzler

そうです、gitがMERGEではなく、オーバーライドするだけであることを明確にする必要があります。次に、「git diff HEAD filename」を実行して、何が変更されたかを確認し、手動でマージを実行できます。
cminatti 2013

18

状況:

あなたはあなたのブランチにいるとしましょう、masterそしてあなたは他のブランチにコミットしています。その特定のコミットからファイルを1つだけ選択する必要があります。

アプローチ:

ステップ1:必要なブランチをチェックアウトします。

git checkout master

ステップ2:必要なコミットハッシュをコピーしたことを確認します。

git checkout commit_hash path\to\file

ステップ3:これで、必要なファイルの変更が目的のブランチにあります。追加してコミットするだけです。

git add path\to\file
git commit -m "Your commit message"

1
驚くばかり!また、\ path \ to \ directory \を含むディレクトリ内のすべての変更に対応しました
zaggi

13

私はすべてをチェリーピックし、これを行います:

git reset --soft HEAD^

次に、不要な変更を元に戻し、新しいコミットを作成します。


11

git merge --squash branch_nameこれを使用すると、他のブランチからすべての変更が取得され、コミットが準備されます。ここで、不要な変更をすべて削除し、必要な変更を残します。そしてgitはマージがあったことを知りません。


おかげで、私はそのマージオプションについて知りませんでした。ブランチ全体のほとんどをチェリーピックする場合は、実行可能な代替手段です(ただし、共通の祖先がない場合はチェリーピックとは
異なります

4

IMOが覚えやすく理解しやすいチェリーピッキングでの競合のマージを防ぐ別の方法を見つけました。あなたは実際にはコミットをチェリーピッキングではなくその一部なので、まずそれを分割してから、ニーズに合ったコミットを作成してチェリーピックする必要があります。

最初に、分割するコミットからブランチを作成してチェックアウトします。

$ git checkout COMMIT-TO-SPLIT-SHA -b temp

次に、以前のコミットを元に戻します。

$ git reset HEAD~1

次に、チェリーピックするファイル/変更を追加します。

$ git add FILE

そしてそれをコミットします:

$ git commit -m "pick me"

コミットハッシュに注意して、それをPICK-SHAと呼び、メインブランチに戻ります。たとえば、チェックアウトを強制します。

$ git checkout -f master

そしてコミットをチェリーピックします:

$ git cherry-pick PICK-SHA

これで、tempブランチを削除できます。

$ git branch -d temp -f

2

ブランチを新しいブランチ(スカッシュ)にマージし、不要なファイルを削除します。

git checkout master
git checkout -b <branch>
git merge --squash <source-branch-with-many-commits>
git reset HEAD <not-needed-file-1>
git checkout -- <not-needed-file-1>
git reset HEAD <not-needed-file-2>
git checkout -- <not-needed-file-2>
git commit

2

完全にするために、私にとって最も効果的なのは次のとおりです。

git show YOURHASH --no-color -- file1.txt file2.txt dir3 dir4 | git apply -3 --index -

git status

それはまさにOPが望むことを行います。必要に応じて競合解決をmerge行います。新しい変更addは行いcommitますが、変更は行いません。


1

以下を使用できます。

git diff <commit>^ <commit> -- <path> | git apply

表記<commit>^は、の(最初の)親を指定します<commit>。したがって、このdiffコマンド<path>はcommitで行われた変更を選択します<commit>

これはまだ何もコミットしないことに注意してください(git cherry-pickそうです)。したがって、それが必要な場合は、次のことを行う必要があります。

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