체리픽
고친 사람 github-actions[bot]
체리픽은 다른 브랜치에 쌓인 커밋 하나를 골라 지금 브랜치에 다시 얹는 일입니다. 브랜치 전체를 합치지 않고 그 커밋이 고친 것만 가져옵니다. 가져온 변경은 원래 커밋과는 별개인 새 커밋으로 쌓입니다.
쉽고 빠른 이해
체리픽은 옆 브랜치에서 커밋 한 개만 뽑아 내 브랜치에 다시 쌓는 일입니다. 다음 판에 나갈 기능이 잔뜩 쌓인 개발 브랜치에서 급한 버그 수정 커밋 하나만 뽑아 지금 서비스 중인 브랜치로 옮기는 것이 그 일입니다.
한 개만 옮길 방법이 없으면 그 커밋을 가지려고 브랜치를 통째로 합쳐야 합니다. 그러면 아직 내보낼 준비가 안 된 기능까지 같이 딸려 나갑니다.
어떻게 도나:
- 가져올 커밋이 자기 앞 커밋과 견줘 무엇을 바꿨는지 계산합니다
- 그 변경을 지금 브랜치의 끝 위에 얹습니다
- 얹은 결과를 새 커밋 하나로 쌓습니다
같은 변경이 두 브랜치에 각각 다른 커밋으로 남습니다. 나중에 두 브랜치를 합칠 때 그 두 벌이 만나 충돌로 나타나기도 합니다.
브랜치 전체가 어차피 합쳐질 것이면 병합이나 리베이스를 씁니다. 앞뒤 커밋에 엮인 커밋은 뽑지 않습니다.
상세
여럿 중 하나만 골라 옮기기
친구와 같은 요리법을 한 장씩 베껴 가서 각자 손으로 고쳐 왔다고 해 봅시다. 친구가 고친 여러 곳 가운데 「물 두 컵을 한 컵 반으로」 한 줄만 내 종이에도 넣으려면 내 종이에서 물이 적힌 줄을 찾아 그 위에 고쳐 써야 합니다. 내가 이미 그 줄을 다르게 바꿔 놓았으면 어느 쪽으로 할지부터 정해야 합니다.
커밋에서 변경만 떼어 내기
커밋은 그때까지 고친 것을 하나로 묶어 확정한 기록입니다. 커밋은 고친 결과와 자기 바로 앞 커밋이 무엇이었는지를 같이 담습니다. 앞에 있던 그 커밋을 부모라고 부릅니다.
브랜치는 그렇게 이어진 커밋 사슬의 끝이 어느 커밋인지를 가리키는 이름입니다. 브랜치 이름 하나가 그 커밋까지 걸어온 기록 전체를 가리키는 셈입니다.
한 커밋이 무엇을 바꿨는지는 그 커밋과 부모를 견줘 나옵니다. 둘 사이의 차이가 그 커밋이 한 일입니다.
체리픽이 옮기는 것은 커밋 자체가 아니라 이 변경입니다. 원래 커밋이 매달려 있던 사슬은 따라가지 않습니다. 그래서 갈라져 나간 브랜치에서 커밋 하나만 뽑아 얹는 일이 됩니다.
도는 순서
아래 그림은 옆 브랜치의 커밋 둘 중 하나만 가져올 때 사슬이 어떻게 달라지는지를 나란히 보입니다. 화살표는 커밋이 자기 부모를 가리키는 방향입니다.
flowchart TD
subgraph 전["가져오기 전"]
m1["내 브랜치 끝"] --> b0["갈라진 커밋"]
s1["옆 브랜치 커밋 1"] --> b0
s2["옆 브랜치 커밋 2 · 가져올 것"] --> s1
end
subgraph 후["가져온 뒤"]
n2["내 브랜치 끝 · 커밋 2 의 변경 · 새 이름"] --> m2["직전까지의 내 브랜치 끝"]
m2 --> c0["갈라진 커밋"]
t2["옆 브랜치 커밋 2 · 남아 있다"] --> t1["옆 브랜치 커밋 1"]
t1 --> c0
end
전 ~~~ 후
가져오기 전에는 두 브랜치가 갈라진 커밋에서 각자 뻗어 있습니다. 가져온 뒤에는 내 브랜치 끝에 커밋이 하나 더 붙습니다. 그 커밋이 담은 변경은 옆 브랜치 커밋 2 의 것과 같고 이름만 다릅니다. 이름이 왜 달라지는지는 바로 다음 소절에서 봅니다.
옆 브랜치는 손대지 않습니다. 커밋 2 는 옆 브랜치에 그 모습 그대로 남습니다. 옮겨 간 것은 그것을 베낀 새 커밋입니다.
새 커밋이 되는 까닭
커밋에 붙는 이름은 사람이 짓지 않습니다. 고친 내용과 부모, 만든 사람과 때 같은 정보를 한데 모아 해시 계산을 돌려 나온 값을 이름으로 씁니다. 해시는 입력이 한 글자만 달라도 전혀 다른 값을 내놓는 계산입니다.
부모도 그 입력에 들어갑니다. 체리픽으로 옮겨 온 변경은 매달리는 부모가 달라지므로, 고친 내용이 한 글자도 안 달라져도 이름이 다른 커밋이 됩니다.
그래서 체리픽은 커밋을 옮기는 일이 아니라 같은 변경을 담은 커밋을 새로 만드는 일입니다. 옮겨 왔다는 흔적은 커밋 메시지에 적어 두지 않으면 이력에 남지 않습니다.
충돌로 멈추는 때
가져올 변경은 원래 브랜치의 내용 위에서 만들어진 것입니다. 내 브랜치는 그사이 다른 방향으로 고쳐졌을 수 있습니다. 얹으려는 변경이 건드리는 줄을 내 브랜치가 이미 다르게 고쳐 놓았으면 기계는 어느 쪽을 남길지 고를 수 없습니다.
그러면 체리픽은 거기서 멈추고 사람을 부릅니다. 사람이 남길 쪽을 골라 충돌 해소를 하면 그 결과를 커밋으로 쌓고 끝냅니다.
가져올 커밋이 앞 커밋에 기대고 있으면 충돌이 잦아집니다. 앞 커밋이 만들어 놓은 함수를 이 커밋이 고치는 꼴을 떠올려 보면 됩니다. 내 브랜치에는 그 함수가 아직 없어서 고칠 대상이 없습니다.
같은 변경이 두 벌로 남는 대가
체리픽을 마치면 같은 변경이 두 브랜치에 각각 다른 커밋으로 존재합니다. 이름이 다르니 이력만 봐서는 둘이 같은 변경인지 한눈에 안 보입니다.
나중에 두 브랜치를 병합할 때 이 두 벌이 만납니다. 양쪽이 같은 줄을 같은 값으로 고쳐 놓았으면 대개 조용히 지나갑니다. 한쪽을 그 뒤에 더 손봤으면 충돌로 나타납니다.
그래서 체리픽을 자주 한 브랜치는 합칠 때 손이 더 갑니다. 옮겨 온 커밋의 원래 이름을 커밋 메시지에 남겨 두는 것이 흔한 대비책입니다.
병합·리베이스와 갈리는 대목
체리픽 말고도 다른 브랜치의 변경을 내 쪽으로 가져오는 방법이 둘 더 있습니다. 병합은 갈라진 두 브랜치를 커밋 하나에서 한꺼번에 합칩니다. 그 커밋은 양쪽 끝을 부모로 함께 가리킵니다. 이렇게 두 갈래를 잇는 커밋을 병합 커밋이라고 부릅니다.
리베이스는 내 커밋들을 떼어 다른 기준점 위에 다시 쌓습니다. 다시 쌓은 커밋은 부모가 달라지므로 이름도 달라집니다. 이미 남에게 내보낸 커밋을 이렇게 다시 쌓으면 남이 가진 이력과 내 이력이 서로 달라집니다.
셋은 가져오는 범위와 원래 커밋을 어떻게 다루는지가 갈립니다.
| 병합 | 리베이스 | 체리픽 | |
|---|---|---|---|
| 가져오는 범위 | 갈라진 뒤 쌓인 커밋 전부 | 내 커밋 전부를 다른 기준점 위로 | 고른 커밋만 |
| 원래 커밋 | 남는다 | 이름이 다른 새 커밋이 된다 | 남고 복사본이 하나 더 생긴다 |
| 이력 모양 | 갈라졌다 만난 자국이 남는다 | 한 줄로 이어진다 | 두 브랜치가 갈라진 채 같은 변경만 양쪽에 있다 |
| 치르는 대가 | 병합 커밋이 쌓인다 | 이미 내보낸 커밋이면 남의 이력과 어긋난다 | 같은 변경이 두 벌로 갈린다 |
표의 첫 줄이 셋을 가르는 대목입니다. 브랜치 전체가 필요하면 앞의 둘입니다. 그중 한 커밋만 필요하면 체리픽입니다.
언제 쓰고 언제 안 쓰나
쓰는 때는 대개 하나입니다. 한 브랜치의 일부만 다른 브랜치에 필요할 때입니다. 서비스 중인 브랜치에 급한 수정 하나만 넣는 핫픽스, 새 판에서 고친 것을 옛 판 유지보수 브랜치로 내려보내는 백포트가 그런 때입니다.
커밋 하나만 뽑아 오는 수단이 없으면 그 수정을 가지려고 브랜치를 통째로 합쳐야 합니다. 아직 내보낼 준비가 안 된 기능까지 같이 나갑니다. 체리픽은 그 딸림을 끊으려고 씁니다.
브랜치 전체가 어차피 합쳐질 것이면 병합이나 리베이스로 한 번에 가져옵니다. 커밋 열 개를 하나씩 체리픽하면 같은 변경이 열 벌 더 생깁니다. 합칠 때 그 열 벌과 다시 만납니다.
가져올 커밋이 앞뒤 커밋과 엮여 있을 때도 안 맞습니다. 한 기능을 셋으로 나눠 쌓은 커밋 중 가운데 하나만 뽑으면 그 변경은 기댈 것이 없어 충돌하거나, 붙더라도 동작하지 않습니다.
Git 에서 부르는 명령
Git 에서는 이 동작을 같은 이름의 명령으로 부릅니다. 가져올 커밋의 이름을 인자로 댑니다.
git cherry-pick a1b2c3d # 새 커밋 하나
git cherry-pick --abort # 멈춘 뒤 되돌림
git cherry-pick --continue # 이어서 마저
첫 줄이 기본 쓰임입니다. 둘째 줄은 충돌로 멈춘 뒤 그만두고 체리픽 전 모습으로 돌아갑니다. 셋째 줄은 충돌을 풀고 나서 나머지를 이어 갑니다.
관련 항목
체리픽이 골라 옮기는 이력의 단위
커밋 · 브랜치 · 부모 커밋 · 공통 조상 · HEAD · 해시 · 변경분 · 패치
체리픽 대신 쓸 수 있는 다른 합치기 방식
병합 · 리베이스 · 세 갈래 병합 · 병합 커밋 · 스쿼시 · 빨리 감기 · 되돌리기
체리픽이 멈췄을 때 사람이 손대는 대상
충돌 · 충돌 해소 · 작업 디렉토리 · 인덱스 · 스태시
체리픽으로 옮긴 커밋을 되찾거나 물리는 수단
레퍼로그 · 체크아웃 · 리셋 · 분리된 HEAD
체리픽을 정책으로 쓰는 릴리스 운영 방식
릴리스 엔지니어링 · 브랜치 전략 · 릴리스 브랜치 · 핫픽스 · 백포트 · 메인라인 · 트렁크 · 유지보수 브랜치
체리픽이 남기는 중복을 다루는 개념
중복 커밋 · 패치 아이디 · 선형 이력 · 이력 재작성
체리픽을 제공하는 버전관리 도구
다른 이름: cherry-pick · git cherry-pick · 체리 픽 · 체리피킹