특정 파일의 변경 사항 만 git-cherry-pick하는 방법은 무엇입니까?


586

Git 브랜치에 병합하려면 여러 파일에 대한 변경 사항을 포함하여 특정 커밋에서 변경된 일부 파일에 대한 변경 사항 만 어떻게 할 수 있습니까?

힘내을 가정하는 것은이라고 커밋 stuff파일에 대한 변경 사항을 가지고 A, B, C, 그리고 D하지만 난 단지 병합 할 stuff파일의 변경 A과 B. 그것은을위한 작업 같은 소리 git cherry-pick그러나 cherry-pick단지 전체 커밋, 파일이 아닌 부분 집합을 병합하는 방법을 알고있다.

답변:


688

커밋하기 전에 결과를 검사하고 수정할 수 있는 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
또한 Cherry-picked 커밋에 의해 도입 된 새롭지 만 원하지 않는 파일을 제거하는 git checkout .것이 좋습니다 git clean -f.
— rlat

4
후자의 방법에 대한 추가 참고 사항 : 파일 당git add -p 인덱스에 추가 할 변경 사항을 대화식으로 결정할 수있는 사용
— matthaeus

6
이것은 체리-선택 커밋이 현재 작업 복사본에 적용되지 않기 때문에 그다지 좋지는 않지만 하나의 파일 이 깨끗하게 적용됩니다.
— 한정된 속죄

3
로 선택적으로 스테이지를 해제 할 수도 있습니다 git reset -p HEAD. 그것은 add -p존재한다는 것을 거의 알지 못합니다.
— Patrick Schlüter

1
매우 유용한 트릭입니다. 나는 누군가가 빠른 스크립트로 필요로하는 경우에 요점에 넣어 한 gist.github.com/PiDayDev/68c39b305ab9d61ed8bb2a1195ee1afc
— 다미아노

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

4
아니요, checkout SHA -- fileSHA에서 버전을 정확하게 체크 아웃하고 show SHA -- file | apply체리의 선택과 마찬가지로 SHA의 변경 사항 만 적용합니다. (a) 소스 브랜치에서 지정된 파일을 변경하는 커밋이 둘 이상인지 또는 (b) 현재 대상 브랜치에서 파일을 변경하는 커밋이 있는지 여부는 중요합니다.
— Michael Anderson

9
하나의 파일 만 되돌리고 싶을 때 ( git revert전체 커밋을 취소하기 때문에) 선택적 되돌리기는 또 다른 용도로 유용합니다 . 이 경우에는 다음을 사용하십시오git show -R SHA -- file1.txt file2.txt | git apply -
— Michael Anderson

2
@RoeiBahumi는 다른 의미를 가지고 있습니다. git diff SHA -- file1.txt file2.txt | git apply -파일의 현재 버전과 SHA의 버전 간의 모든 차이점을 현재 버전에 적용합니다. 본질적으로 그것은 동일합니다 git checkout SHA -- file1.txt file2.txt. 그것이 왜 git show버전 과 다른지에 대한 내 이전 의견을 참조하십시오 .
— Michael Anderson

5
충돌을 해결해야하는 경우을 git apply -3 -대신 사용 git apply -하고 충돌이 발생하면을 사용하여 표준 충돌 해결 기술을 사용할 수 있습니다 git mergetool.
— qwertzguy

87

나는 일반적으로 -p다른 지점에서 git checkout과 함께 플래그를 사용합니다.이 지점은 내가 본 다른 대부분의 방법보다 쉽고 세분화되어 있습니다.

원칙적으로:

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

그런 다음 "blobs"에서 원하는 변경 사항을 묻는 대화 상자가 표시됩니다. 이는 연속 된 코드 변경의 모든 덩어리에 적용되며 각 코드 덩어리에 대해 y(예) n(아니요) 등을 표시 할 수 있습니다 .

-p또는 patch옵션을 포함하여 자식의 명령의 다양한 작동 git stash save -p당신이 현재 직장에서 숨기고 싶은 선택할 수 있습니다

나는 종종 많은 작업을 수행 할 때이 기술을 사용하고 그것을 분리하고 git add -p각 커밋에 대해 원하는 것을 사용 하고 선택 하여 더 많은 주제 기반 커밋을 커밋 하고 싶습니다 :)


3
나는 정기적으로 사용하는 git-add -p,하지만 난 몰랐 git-checkout또한이 -p플래그를 - 않는 수정이 병합 문제 비 -p답이 있다?
— Tobias Kienzler

1
적어도 -p충돌하는 섹션을 수동으로 편집 cherry-pick하면 어쨌든 결과가 나올 것입니다. 나는 그것을 필요이 다음 번에, 확실히 흥미로운 접근 방식을 테스트 할 수 있습니다
— 토비아스 Kienzler

2
지점에 대한 동시 변경을 중단시키지 않는 두 가지 최고의 답변 중 하나입니다.
— akostadinov

1
적용 할 덩어리를 선택하는 방법은이 답변을 참조하십시오. stackoverflow.com/a/10605465/4816250 특히 's'옵션이 매우 유용했습니다.
— jvd10

1
git reset -p HEAD또한 -p인덱스에서 일부 패치 만 제거하려고 할 때 편리하게 종료 할 수 있습니다.
— Patrick Schlüter

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커밋 메시지 같은뿐만 아니라 남아있을 것입니다
— 토비아스 Kienzler에게

8
@Tobias : 그건에 수정 된 파일이있는 경우에만 작동합니다 stuff현재의 지점에 또는 어느 곳의 공통 조상 사이에 수정되지 않은 HEAD과 stuff와의 팁 stuff. 그렇다면 cherry-pick올바른 결과 (본질적으로 병합 결과)를 생성하지만 메서드는 현재 분기의 변경 사항을 버리고 공통 조상의 모든 변경 사항을 유지 stuff합니다. 단일 커밋.
— Cascabel

2
Kienzler @Tobias : 당신의 시작 지점의 부모로부터 충분히 다른 것을 가정 한 stuff벚꽃의 선택의 결과가 떠날 것이라는 A와 B커밋에서의 내용과 다른 내용 stuff. 그러나 그것이 똑같다면 당신이 옳습니다. 당신이 말한대로 할 수 있습니다.
— Mark Longair

@Jeromi, @Mark : 의견을 보내 주셔서 감사합니다. 제 경우에는 완전히 분리 된 파일로 분기를 처리하고 있습니다. 그러나 실제로 나는 조만간 문제가
— 생겼으므로

생각 이 다른 스레드에서 내 대답은 당신이 계신 될 수있다.
— 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 파일 이름


5
나도 그렇게 생각 했지만, 현재 브랜치의 변경 사항을 버리고 두 브랜치 에서 파일이 변경된 경우 끔찍하게 실패합니다
— Tobias Kienzler

당신이 옳습니다.이 방법으로 git이 병합되지 않고 단지 재정의한다는 것을 분명히해야합니다. 그런 다음 "git diff HEAD filename"을 수행하여 변경된 사항을 확인하고 수동으로 병합을 수행 할 수 있습니다.
— cminatti

18

그 상황:

당신은 당신의 지점에 있고, master다른 지점에 대한 헌신을 가지고 있다고 가정 해 봅시다 . 해당 커밋에서 하나의 파일 만 선택해야합니다.

접근:

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이것을 사용 하면 다른 지점에서 모든 변경 사항을 가져오고 커밋을 준비합니다. 이제 불필요한 변경 사항을 모두 제거하고 원하는 변경 사항을 그대로 두십시오. 그리고 자식은 병합이 있음을 알지 못합니다.


고마워, 나는 그 병합 옵션에 대해 몰랐다. 전체 지점의 대부분을 체리 픽 선택하려는 경우 실행 가능한 대안입니다. 그러나 체리 픽 선택과 달리 공통 조상이 없으면 작동하지 않습니다.
— Tobias Kienzler

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

이제 임시 분기를 삭제할 수 있습니다.

$ 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> .

이것은 아직 아무것도 git cherry-pick하지 않습니다. 따라서 원하는 경우 다음을 수행해야합니다.

git add <path>
git commit
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.