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


586

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

힘내을 가정하는 것은이라고 커밋 stuff파일에 대한 변경 사항을 가지고 A, B, C, 그리고 D하지만 난 단지 병합 할 stuff파일의 변경 AB. 그것은을위한 작업 같은 소리 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현재의 지점에 또는 어느 곳의 공통 조상 사이에 수정되지 않은 HEADstuff와의 팁 stuff. 그렇다면 cherry-pick올바른 결과 (본질적으로 병합 결과)를 생성하지만 메서드는 현재 분기의 변경 사항을 버리고 공통 조상의 모든 변경 사항을 유지 stuff합니다. 단일 커밋.
Cascabel

2
Kienzler @Tobias : 당신의 시작 지점의 부모로부터 충분히 다른 것을 가정 한 stuff벚꽃의 선택의 결과가 떠날 것이라는 AB커밋에서의 내용과 다른 내용 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.