Cherry-pick: 다른 브랜치의 특정 커밋만 골라 현재 브랜치에 적용하기
cherry-pick은 다른 브랜치에 있는 특정 커밋의 변경 내용을 현재 브랜치에 새로운 커밋으로 복제해 적용하는 Git 명령이다. 브랜치 전체를 병합하지 않고 필요한 커밋만 가져올 수 있어 핫픽스 반영이나 잘못된 브랜치에 한 커밋을 옮길 때 유용하다. 원본과 내용은 같지만 부모와 해시가 다른 별개의 커밋이 만들어진다는 점을 이해하는 것이 핵심이다.
By 송민성
1. 개념
git cherry-pick은 지정한 커밋이 도입한 변경분(diff) 을 현재 브랜치(HEAD) 위에 새 커밋으로 다시 적용하는 명령이다.
- 원본 커밋은 그대로 남는다.
- 새로 생긴 커밋은 내용(변경분)은 같지만 부모 커밋이 다르므로 커밋 해시(commit hash)도 다르다.
- 병합(merge)이 브랜치의 전체 이력을 합치는 것이라면, cherry-pick은 커밋 단위로 선택해 복사하는 것이다.
2. 왜 사용하는가
- 핫픽스 반영:
main에서 수정한 버그 픽스를release/1.x같은 유지보수 브랜치에도 적용해야 할 때. - 잘못된 브랜치에 한 커밋:
main에 커밋해 버린 작업을 기능 브랜치로 옮길 때. - 부분 반영: 기능 브랜치에 커밋이 여러 개 있지만 그중 일부만 먼저 배포해야 할 때.
- 병합하면 원하지 않는 커밋까지 딸려 오는 상황을 피할 수 있다.
3. 동작 원리
Git은 커밋을 "파일 전체의 스냅샷"으로 저장하지만, cherry-pick을 수행할 때는 대상 커밋 C와 그 부모 P의 차이(P → C)를 계산한다. 이 차이를 현재 HEAD에 3-way merge 방식으로 적용한다.
- 기준(base): 대상 커밋의 부모
P - 한쪽(theirs): 대상 커밋
C - 다른 쪽(ours): 현재 HEAD
같은 부분을 서로 다르게 수정했다면 충돌(conflict)이 발생하고, 사용자가 해결해야 한다. 충돌이 없으면 원본의 작성자(author) 정보와 메시지를 유지한 새 커밋이 만들어진다. 이때 커밋 작성자(committer)와 커밋 시각은 cherry-pick을 수행한 시점의 값으로 바뀐다.
적용 전 적용 후 (main에서 C를 cherry-pick)
main: A - B main: A - B - C'
feature: A - X - C feature: A - X - CC'는 C와 변경 내용은 같지만 다른 커밋이다.
4. 코드 예제
실습용 저장소를 만들어 직접 실행해 볼 수 있는 셸 스크립트이다. (기본 브랜치 이름은 main으로 지정한다.)
#!/bin/sh
set -e
# 실습 저장소 준비
mkdir cherry-demo && cd cherry-demo
git init -b main
git config user.name "demo"
git config user.email "demo@example.com"
echo "base" > app.txt
git add app.txt
git commit -m "A: 초기 커밋"
# feature 브랜치에서 커밋 두 개 생성
git switch -c feature
echo "login" > login.txt
git add login.txt
git commit -m "X: 로그인 기능 추가"
echo "bugfix" > fix.txt
git add fix.txt
git commit -m "C: 결제 버그 수정"
# main으로 돌아와 C만 가져오기
git switch main
git cherry-pick feature # feature의 마지막 커밋(C)만 적용
git log --oneline --all --graph
ls # fix.txt만 있고 login.txt는 없다자주 쓰는 형태는 다음과 같다.
# 커밋 여러 개를 지정
git cherry-pick <해시1> <해시2>
# 범위 지정 (A는 제외, B까지 포함)
git cherry-pick A..B
# 범위 지정 (A 포함)
git cherry-pick A^..B
# 커밋하지 않고 변경만 작업 트리에 반영
git cherry-pick -n <해시>
# 원본 커밋 해시를 메시지에 기록
git cherry-pick -x <해시>
# 머지 커밋은 어느 부모를 기준으로 할지 지정
git cherry-pick -m 1 <머지커밋해시>충돌이 났을 때의 흐름은 다음과 같다.
git cherry-pick <해시>
# CONFLICT 발생 → 충돌 파일을 수정한 뒤
git add <수정한 파일>
git cherry-pick --continue
# 중단하고 원래 상태로 되돌리기
git cherry-pick --abort5. 시간 복잡도 또는 성능 특성
cherry-pick의 비용은 저장소 전체 크기가 아니라 적용하는 커밋의 변경 규모와 충돌 여부에 주로 좌우된다. 일반적인 크기의 커밋은 거의 즉시 끝난다. 범위 지정으로 많은 커밋을 가져오면 커밋 수만큼 순서대로 적용하므로 시간이 늘어난다. 정확한 복잡도 수치는 Git 내부 구현과 변경 파일 수에 따라 달라지므로 단일한 Big-O로 단정하기 어렵다.
6. 실무 사용 사례
- 릴리스 브랜치 백포트(backport): 메인 브랜치에서 고친 보안 패치를 이전 버전 브랜치에 적용한다.
- 작업 위치 교정: 엉뚱한 브랜치에 커밋했을 때 올바른 브랜치로
cherry-pick한 뒤, 잘못된 브랜치의 커밋은git reset으로 정리한다. - PR 분리: 큰 기능 브랜치에서 독립적인 리팩터링 커밋만 골라 별도 PR로 올린다.
- 팀에서 추적이 필요할 때는
-x옵션으로 원본 커밋 해시를 메시지에 남긴다. 단, 이 정보는 공개 브랜치에서 가져올 때 의미가 있다.
7. 주의할 점
- 중복 커밋: 같은 변경이 두 브랜치에 서로 다른 커밋으로 존재한다. 나중에 두 브랜치를 병합해도 보통 문제없이 처리되지만, 같은 영역을 이후에 또 수정했다면 충돌이 생길 수 있다.
- 의존성 누락: 선택한 커밋이 앞선 커밋의 변경에 의존하면 충돌이 나거나 빌드가 깨질 수 있다. 가져오기 전에 필요한 선행 커밋이 있는지 확인한다.
- 머지 커밋:
-m옵션으로 기준 부모를 지정하지 않으면 실패한다. - 남용 금지: 잦은 cherry-pick은 이력을 추적하기 어렵게 만든다. 브랜치 전체를 합쳐야 하는 상황이라면 merge나 rebase가 적합하다.
- 빈 커밋: 이미 같은 변경이 반영되어 있으면 적용할 내용이 없어 cherry-pick이 멈춘다.
git cherry-pick --skip으로 건너뛰거나--abort로 중단한다.
8. 핵심 정리
- cherry-pick은 특정 커밋의 변경분을 현재 브랜치에 새 커밋으로 복제한다.
- 내용은 같지만 부모가 달라 해시는 다르다.
- 내부적으로 부모를 기준(base)으로 한 3-way merge를 수행하므로 충돌이 날 수 있다.
- 충돌 시
git add후--continue, 포기하려면--abort를 쓴다. - 핫픽스 백포트나 잘못된 브랜치 교정에 적합하며, 남용하면 이력이 복잡해진다.