git cherry-pick

Apply the changes introduced by some existing commits

Git 공식 매뉴얼(git-cherry-pick(1) Manual Page)

"아, 그 기능만 쏙 빼서 운영 서버에 반영하고 싶다."라는 안일한 생각이 불러온 대재앙. 그냥 새로 짜는 게 빨랐을지도 모른다는 후회가 밀려올 때는 이미 늦었다.(...)

1. 개요

Git의 강력하면서도 극도로 위험한 명령어 중 하나. 특정 브랜치에 있는 특정 커밋(Commit)만을 콕 집어 현재 브랜치에 강제로 쑤셔 박는(?) 행위이다. 이름 그대로 맛있는 체리만 쏙쏙 골라 먹는 체리 피킹(Cherry picking) 행위에서 유래했다. 실무에서는 보통 급하게 특정 버그 패치만 운영 환경에 반영해야 하거나, 개발 브랜치에서 실수로 섞여 들어간 작업물 중 일부만 쏙 빼오고 싶을 때 눈물을 머금고 시전하게 된다. 물론 그 체리가 독이 든 체리일 가능성은 언제나 열려 있다.

2. 개념과 사용법

가장 기본적인 사용법은 매우 간단하다. 옮겨오고 싶은 커밋의 해시(Hash) 값을 알아낸 뒤, git cherry-pick <commit-hash>를 입력하기만 하면 끝이다. 원한다면 여러 개의 커밋을 한 번에 가져올 수도 있고(git cherry-pick A..B), 아예 커밋을 생성하지 않고 작업 디렉터리에 변경 사항만 스테이징(Staging) 상태로 올리는 --no-commit 옵션도 제공한다. 머지(Merge)처럼 브랜치 전체를 합치는 거대한 작업이 부담스러울 때, 딱 필요한 알맹이만 골라올 수 있어 겉보기에는 무척이나 우아하고 스마트해 보이는 명령어다.(...) 하지만 이 명령어의 진짜 무서움은 실행한 '직후'에 찾아온다.

3. 실무에서의 애환과 지옥도

체리픽을 밥 먹듯이 하는 팀의 깃 히스토리(Git History)를 열어보면 높은 확률로 지옥도가 펼쳐져 있다. 체리픽은 기존 커밋을 복사해서 '새로운 해시 ID를 가진 전혀 다른 커밋'을 생성하는 방식이기 때문에, 동일한 변경 사항이 다른 해시값으로 여러 브랜치에 분산된다. 이 상태에서 나중에 두 브랜치를 진짜로 머지하려고 하면, 깃은 동일한 내용인데 해시가 다른 두 커밋을 보고 정신을 못 차리며 엄청난 양의 병합 충돌(Merge Conflict)을 뿜어내기 시작한다. 결국 개발자는 한참 전에 자기가 작성한 코드를 보며 "이게 왜 충돌이 나지?" 하고 머리를 쥐어뜯게 된다.1 특히 비즈니스 로직이 얽히고설킨 거대한 프로젝트에서 체리픽을 남용하는 것은, 미래의 자신에게 시한폭탄을 선물하는 것과 다름없다.

4. 실제 사용 예시

다른 브랜치에 있는 특정 커밋 하나만 현재 브랜치로 가져오는 기본 흐름이다. 먼저 가져올 커밋 해시를 확인한 뒤 대상 브랜치에서 cherry-pick을 실행한다.

git switch release/1.2
git log --oneline main

git cherry-pick a1b2c3d

충돌이 나면 파일을 고치고 스테이징한 뒤 계속 진행한다. 너무 꼬였으면 --abort로 체리픽 시작 전 상태로 되돌린다.

# 충돌 해결 후 계속 진행
git add src/app.ts
git cherry-pick --continue

# 체리픽 중단
git cherry-pick --abort

# 커밋은 만들지 않고 변경 사항만 가져오기
git cherry-pick -n a1b2c3d

5. 관련 밈 및 드립

5.1. 체리 픽이 아니라 체리 폭탄(Cherry Bomb)

실무에서 누군가 "그 커밋만 체리픽해서 올릴게요!"라고 호기롭게 말한 뒤, 10분 후에 메신저로 충돌 스크린샷과 함께 살려달라고 울부짖는 현상을 비꼬는 밈이다. 분명히 상큼한 체리를 골랐는데 터진 건 수류탄이었다는 자조적인 드립으로, 특히 배포 당일 오후 5시 이후에 터지는 체리 폭탄은 해당 개발자의 퇴근을 깔끔하게 증발시킨다.(...)

5.2. git cherry-pick --abort (인생 되돌리기)

체리픽 도중 걷잡을 수 없는 충돌의 홍수가 밀려올 때, 모든 상황을 없던 일로 되돌려주는 구원의 명령어 git cherry-pick --abort에서 유래한 밈. 실무자들은 인생에서 돌이킬 수 없는 실수를 저질렀을 때나, 회의 도중 상사의 무리한 요구로 갑자기 분위기가 싸해졌을 때 머릿속으로 이 명령어를 백만 번쯤 외친다고 한다. 현실에는 --abort 옵션이 없다.2

6. 여담

  • 체리피킹의 이중적 의미: 경제학이나 일상 용어에서 '체리 피킹'은 자신에게 유리한 조건만 골라 취하는 얌체 같은 행위를 뜻하는데, Git에서도 아주 묘하게 그 뉘앙스가 맞아떨어진다. 히스토리를 정갈하게 관리하지 않고 얌체처럼 단물만 빨아먹으려다 히스토리 전체가 꼬이기 때문.
  • 소프트체리픽 vs 하드체리픽: -n 또는 --no-commit 옵션을 주면 커밋을 바로 생성하지 않고 작업 공간(Working Tree)에 변경 사항만 올려두는데, 실무에서는 이 상태에서 코드를 한 번 더 검증하고 수동으로 커밋하는 것이 정신 건강에 매우 이롭다.3
  • 충돌 해결의 귀찮음: 체리픽 중 충돌이 나면 수정 후 git add를 하고 git cherry-pick --continue를 해야 하는데, 초보자들은 여기서 겁을 먹고 그냥 git commit을 해버려 상황을 더 파국으로 몰고 가기도 한다.(...)

7. 관련 문서

각주

  1. 실제로 이 충돌을 해결하다가 실수로 다른 사람의 소중한 커밋을 날려먹는 대참사도 심심치 않게 일어난다.

  2. 물론 퇴사라는 극단적인 --abort가 존재하기는 한다.

  3. 하지만 대부분의 개발자들은 귀찮아서 그냥 옵션 없이 질러버리고 기도를 시작한다.