← 블로그로 돌아가기

Git cherry-pick 완전 정복: 써야 할 때와 쓰지 말아야 할 때

tutorial git

Git Cherry-Pick이란?

중요한 버그 수정이 방금 개발 branch에 들어왔습니다 -- 그런데 같은 버그가 프로덕션에도 살아 있습니다. 개발 branch 전체를 릴리스 branch에 merge할 수는 없습니다. 절반만 완성된 기능들로 가득하니까요. 정말 필요한 것은 그 commit 하나만 집어서 다른 곳에 적용하는 것입니다. 바로 그것이 git cherry-pick이 하는 일입니다.

cherry-pick은 기존 commit이 도입한 변경 사항을 가져와 현재 branch 위에 완전히 새로운 commit으로 재현합니다. 새 commit은 동일한 diff와 (기본적으로) 동일한 메시지를 담고 있지만, 부모가 다르므로 SHA도 다릅니다. 이 세부 사항은 보기보다 중요합니다. Git은 commit을 SHA로 식별하기 때문에, cherry-pick 이후 저장소에는 같은 변경을 도입하는 서로 다른 두 개의 commit이 존재하게 됩니다. 이 점을 기억해 두세요 -- cherry-pick이 왜 그렇게 유용한지, 그리고 왜 남용하면 나중에 고생하게 되는지를 모두 설명해 주는 지점입니다.

cherry-pick이 올바른 도구인 경우

hotfix를 릴리스 branch로 백포트하기

가장 전형적인 경우입니다. main에서 버그를 고쳤지만, 사용자들이 쓰는 것은 버전 2.3이고 수정이 지금 당장 필요합니다. 수정 사항을 릴리스 branch에 cherry-pick하세요:

git switch release/2.3
git cherry-pick a1b2c3d

릴리스 branch는 정확히 그 수정만 받고, main의 다른 어떤 것도 따라오지 않습니다.

commit이 잘못된 branch에 올라간 경우

피처 branch에 commit하려 했는데 main에 있었던 거죠. cherry-pick은 깔끔한 복구 방법을 제공합니다. commit이 있어야 할 branch로 전환해서 그곳에 cherry-pick한 다음, 돌아가서 commit이 있어서는 안 되는 branch에서 제거하면 됩니다. 제거 단계가 불안하다면, Git commit을 되돌리는 방법 가이드에서 각 선택지를 자세히 다룹니다.

버려진 branch에서 수정 하나만 구출하기

실험이 결실을 맺지 못했지만, branch 중간의 commit 하나가 실제 버그를 고쳤을 때가 있습니다. 막다른 branch를 merge하는 대신, 가치 있는 그 commit 하나만 활성 branch에 cherry-pick하고 나머지 실험은 마음 편히 삭제되도록 두세요.

기본 사용법

가장 단순한 형태는 commit 하나를 받습니다:

git cherry-pick a1b2c3d

Git은 그 commit의 변경 사항을 적용하고, 원래 메시지를 재사용해 현재 branch에 즉시 새 commit을 만듭니다. 여러 commit을 한 번에 넘길 수도 있으며, Git은 나열한 순서대로 하나씩 적용합니다:

git cherry-pick a1b2c3d f4e5d6c

알아 두어야 할 실용적인 옵션

-x: commit의 출처 기록하기

백포트할 때는 나중에 commit이 어디에서 왔는지 알고 싶어집니다. -x 옵션은 새 commit 메시지에 (cherry picked from commit a1b2c3d...) 같은 줄을 덧붙입니다:

git cherry-pick -x a1b2c3d

오래 유지되는 공개 branch 사이의 cherry-pick에서는 이것을 습관으로 만드세요. 6개월 뒤, 그 한 줄이 수정이 어떤 릴리스에 들어갔는지, 어디에서 왔는지를 즉시 알려 줍니다.

commit 범위 가져오기

점 두 개 문법으로 범위 전체를 cherry-pick할 수 있습니다:

git cherry-pick A..B

주의: 이것은 A 이후의 commit들을 B까지 (B 포함) 적용합니다. A 자체는 제외됩니다. A를 포함하고 싶다면 이렇게 쓰세요:

git cherry-pick A^..B

^는 "A의 부모"를 의미하며, 이렇게 하면 A가 범위 안에 다시 들어옵니다. 이 두 문법을 혼동하는 것이 가장 흔한 cherry-pick 실수 중 하나입니다.

--no-commit: commit 없이 적용하기

변경 사항은 원하지만 자동 commit은 원하지 않을 때가 있습니다 -- 예를 들어 가져온 여러 commit을 하나로 합치거나, commit 전에 결과를 손보고 싶을 때입니다:

git cherry-pick --no-commit a1b2c3d

변경 사항은 작업 디렉터리와 staging 영역에 들어가고, 준비가 되면 직접 commit을 만들면 됩니다.

cherry-pick 중 충돌 처리하기

cherry-pick은 merge와 같은 이유로 충돌할 수 있습니다. 가져오려는 commit이 현재 branch와 다른 모습의 코드를 기준으로 작성되었기 때문입니다. 충돌이 발생하면 Git은 작업 도중에 멈추고 충돌한 파일을 표시합니다:

error: could not apply a1b2c3d... fix login timeout
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue"

이제 세 가지 출구가 있습니다:

  • git cherry-pick --continue -- 충돌한 파일을 수정하고 git add로 staging에 올린 뒤 실행하면, cherry-pick을 마무리하고 commit을 만듭니다.
  • git cherry-pick --abort -- 전체 작업을 취소하고 branch를 시작 전과 정확히 같은 상태로 되돌립니다. 잃는 것도 없고, 적용되는 것도 없습니다.
  • git cherry-pick --skip -- 여러 commit을 가져오는 중에 현재 commit을 건너뛰고 다음으로 넘어갑니다. 범위 안의 어떤 commit이 이미 적용되었거나 불필요한 것으로 드러났을 때 유용합니다.

충돌 해결 자체는 merge 충돌 해결과 똑같이 진행됩니다. 충돌한 각 파일을 열고, 경쟁하는 hunk 중에서 선택하거나 (또는 결합하고), 충돌 마커를 제거한 뒤, 파일을 staging에 올리면 됩니다. 충돌 마커가 아직 부담스럽다면, Git merge 충돌을 시각적으로 해결하기 안내가 cherry-pick에도 그대로 적용됩니다.

cherry-pick으로 하면 안 되는 것

branch 전체를 cherry-pick하지 마세요

한 branch에서 다른 branch로 commit을 다섯 개, 열 개, 스무 개씩 cherry-pick하고 있다면, 멈추세요. 그건 merge와 rebase가 할 일입니다. 둘 다 Git이 branch 사이의 관계를 이해한 채로 작업을 옮기므로, 이후의 작업이 단순하고 충돌 없이 유지됩니다. 둘 중 무엇이 자신의 워크플로에 맞는지 확신이 없다면, rebase vs merge 비교 글에서 장단점을 살펴보세요.

중복된 히스토리는 나중에 발목을 잡습니다

기억하세요. cherry-pick은 새 SHA를 가진 새 commit을 만들며, Git은 두 commit이 "같은 변경"이라는 공식 기록을 전혀 남기지 않습니다. 피처 branch의 commit들을 main으로 cherry-pick한 뒤 나중에 그 피처 branch를 어쨌든 merge하게 되면, Git은 같은 변경의 두 사본을 조정해야 합니다. 대개는 조용히 성공합니다. 하지만 어느 한쪽 사본이 이후에 수정된 순간, 양쪽이 거의 똑같아 보이는 혼란스러운 충돌이 생깁니다 -- 게다가 히스토리에는 같은 변경이 두 개의 날짜와 두 개의 SHA로 두 번 나타나, git loggit bisect 같은 도구로 추적하기가 더 어려워집니다.

좋은 경험칙: cherry-pick은 예외를 옮기기 위한 것입니다 -- hotfix, 잘못 놓인 commit, 구출한 수정. cherry-pick이 branch 사이에서 작업을 옮기는 일상적인 방법이 되는 순간, 히스토리가 branch 전략을 다시 생각해야 한다고 말해 주고 있는 것입니다.

GitSquid에서의 cherry-pick

GitSquid에서 cherry-pick은 기대하는 바로 그 자리에 있습니다. commit 그래프에서 아무 commit이나 우클릭하고 컨텍스트 메뉴에서 cherry-pick 옵션을 선택하면 됩니다. 로그와 터미널 사이에서 SHA를 복사해 나를 필요가 없습니다.

더 큰 이점은 무엇이 기다리고 있는지 미리 아는 것입니다. GitSquid의 Conflict Predictor는 -- 모두에게 무료로 -- merge, rebase, cherry-pick을 실행하기 전에 어떤 파일이 충돌할지 정확히 보여 줍니다. hunk 미리보기와 문법 강조까지 함께요. 작업 도중에 충돌을 발견하는 대신, 미리 확인하고 계속 진행할지, 다른 commit을 고를지, 차분히 해결을 준비할지 결정할 수 있습니다.

그리고 실제로 충돌이 나면, "Resolve in scratch worktree"가 임시 worktree를 새 탭으로 열어 주므로, 활성 checkout은 손대지 않은 채 그곳에서 모든 것을 해결할 수 있습니다.

빠른 참조

명령어 하는 일
git cherry-pick <sha> commit 하나를 현재 branch에 적용
git cherry-pick <sha1> <sha2> 여러 commit을 순서대로 적용
git cherry-pick -x <sha> 원본 SHA를 commit 메시지에 덧붙임
git cherry-pick A..B A 이후부터 B까지의 commit 적용 (A 제외)
git cherry-pick A^..B A를 포함한 범위 적용
git cherry-pick --no-commit <sha> commit 없이 변경 사항만 적용
git cherry-pick --continue 충돌 해결 후 pick 마무리
git cherry-pick --abort 취소하고 branch를 원래대로 복원
git cherry-pick --skip 현재 commit을 건너뛰고 나머지 계속

cherry-pick은 메스이지 삽이 아닙니다. 가끔의 정밀한 추출 -- hotfix 백포트, 잘못 놓인 commit, 구출한 수정 -- 에 쓰면 Git에서 가장 만족스러운 명령어 중 하나입니다. merge의 대용품으로 쓰면, 히스토리는 조용히 중복으로 가득 차서 나중에 당신을 괴롭히러 돌아옵니다. 그 차이를 알고, 백포트할 때는 -x를 붙이고, 비상구로 --abort를 기억해 두세요.

시작하기도 전에 어떤 파일이 충돌할지 보고 싶나요? GitSquid 무료 다운로드 후 Conflict Predictor에게 다음 cherry-pick의 어림짐작을 맡겨 보세요.