사전 체리픽
개념

체리픽

gabury1고친 사람 github-actions[bot]

체리픽은 다른 브랜치에 쌓인 커밋 하나를 골라 지금 브랜치에 다시 얹는 일입니다. 브랜치 전체를 합치지 않고 그 커밋이 고친 것만 가져옵니다. 가져온 변경은 원래 커밋과는 별개인 새 커밋으로 쌓입니다.

쉽고 빠른 이해

체리픽은 옆 브랜치에서 커밋 한 개만 뽑아 내 브랜치에 다시 쌓는 일입니다. 다음 판에 나갈 기능이 잔뜩 쌓인 개발 브랜치에서 급한 버그 수정 커밋 하나만 뽑아 지금 서비스 중인 브랜치로 옮기는 것이 그 일입니다.

한 개만 옮길 방법이 없으면 그 커밋을 가지려고 브랜치를 전부 합쳐야 합니다. 그러면 아직 내보낼 준비가 안 된 기능까지 같이 딸려 나갑니다.

어떻게 도나:

  1. 가져올 커밋이 자기 앞 커밋과 견줘 무엇을 바꿨는지 계산합니다
  2. 그 변경을 지금 브랜치의 끝 위에 얹습니다
  3. 얹은 결과를 새 커밋 하나로 쌓습니다

같은 변경이 두 브랜치에 각각 다른 커밋으로 남습니다. 나중에 두 브랜치를 합칠 때 그 두 벌이 다시 만나 충돌이 날 수 있습니다. 브랜치 전체가 어차피 합쳐질 것이면 체리픽을 쓰지 않습니다. 앞 커밋에 기대는 커밋도 혼자 뽑지 않습니다.

상세

여럿 중 하나만 골라 옮기기

친구와 같은 요리법을 한 장씩 베껴 가서 각자 손으로 고쳐 왔다고 해 봅시다. 친구가 고친 여러 곳 가운데 「물 두 컵을 한 컵 반으로」 한 줄만 내 종이에도 넣으려면 내 종이에서 물이 적힌 줄을 찾아 그 위에 고쳐 써야 합니다. 내가 이미 그 줄을 다르게 바꿔 놓았으면 어느 쪽으로 할지부터 정해야 합니다.

커밋에서 변경만 떼어 내기

커밋은 그때까지 고친 것을 하나로 묶어 확정한 기록입니다. 커밋은 고친 결과와 함께 자기 바로 앞 커밋이 무엇이었는지를 담습니다. 앞에 있던 그 커밋을 부모라고 부릅니다.

커밋마다 다른 커밋과 가려 부르는 값이 하나씩 붙습니다. 이 값을 커밋 아이디라고 부릅니다.

브랜치는 그렇게 이어진 커밋 사슬의 끝이 어느 커밋인지를 가리키는 이름입니다. 브랜치 이름 하나가 그 커밋까지 걸어온 기록 전체를 가리키는 셈입니다.

한 커밋이 무엇을 바꿨는지는 그 커밋과 부모를 견줘 나옵니다. 둘 사이의 차이가 그 커밋이 들여온 변경입니다.

체리픽은 한 브랜치에서 커밋 하나를 골라 그 변경을 다른 브랜치에 다시 적용하는 일입니다. 서로 이어지지 않은 커밋 몇 개를 골라 차례로 옮기는 것도 체리픽이라 부릅니다. 옮기는 것은 커밋 자체가 아니라 그 변경입니다. 원래 커밋이 매달려 있던 사슬은 따라가지 않습니다.

도는 순서

체리픽 한 번은 세 단계로 돕니다.

  1. 고른 커밋을 그 부모와 견줘 변경을 계산합니다
  2. 그 변경을 지금 브랜치의 끝 위에 얹습니다
  3. 얹은 결과를 새 커밋 하나로 기록합니다. 지금 브랜치의 끝이 그 새 커밋이 됩니다

아래 그림은 옆 브랜치의 커밋 둘 중 하나만 가져올 때 사슬이 어떻게 달라지는지를 보입니다. 화살표는 커밋이 자기 부모를 가리키는 방향입니다.

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 cherry-pick

Git 에서는 같은 이름의 명령이 이 일을 합니다. 기존 커밋을 하나 이상 받아 각 커밋이 들여온 변경을 적용합니다. 커밋마다 새 커밋을 하나씩 기록합니다.

작업 디렉토리는 지금 손으로 고치는 파일들이 놓인 폴더입니다. 이 명령은 작업 디렉토리가 깨끗해야 돕니다. 지금 브랜치 끝 커밋과 견줘 고친 것이 없어야 한다는 뜻입니다.

터미널
git cherry-pick master
git cherry-pick master~4 master~2

첫 줄은 master 브랜치 끝 커밋이 들여온 변경을 적용해 새 커밋 하나를 만듭니다. 둘째 줄은 master 끝에서 다섯째와 셋째 커밋의 변경을 적용해 새 커밋 둘을 만듭니다.

터미널
$ git cherry-pick e43a6
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
 3 files changed, 17 insertions(+), 3 deletions(-)

커밋 e43a6 이 들여온 것과 같은 변경이 master 에 들어왔습니다. 새 커밋은 a0a41a9 라는 다른 커밋 아이디를 받습니다. Git 의 커밋 아이디는 SHA-1(Secure Hash Algorithm 1) 해시 값입니다. 변경이 적용된 날짜가 달라서 그 값이 달라졌습니다.

병합 커밋은 대개 체리픽할 수 없습니다. 병합 커밋은 부모가 여럿이라 어느 쪽을 기준으로 변경을 계산할지 모르기 때문입니다. -m 에 기준으로 삼을 부모의 번호를 1부터 세어 주면 그 부모에 견준 변경을 다시 적용합니다.

Git 에서 충돌로 멈춘 백포트

변경을 어떻게 얹을지 분명하지 않으면 Git 은 거기서 멈춥니다. 지금 브랜치의 끝은 마지막으로 성공한 커밋에 머뭅니다. CHERRY_PICK_HEAD 라는 이름이 얹기 어려운 변경을 들여온 커밋을 가리키게 됩니다. --no-commit 옵션을 주었을 때는 이 이름이 생기지 않습니다.

인덱스는 다음 커밋에 담을 내용을 모아 두는 곳입니다. 깔끔하게 얹힌 파일은 인덱스와 작업 디렉토리 양쪽에 반영됩니다. 충돌한 파일에는 <<<<<<< 와 >>>>>>> 로 감싼 충돌 설명이 들어갑니다.

패치는 한 커밋이 들여온 변경을 떼어 낸 것입니다. 아래는 패치 하나를 백포트하려다 멈추고 다시 시도하는 순서입니다.

터미널
git cherry-pick topic^
git diff
git cherry-pick --abort
git cherry-pick -Xpatience topic^

topic^ 은 topic 브랜치 끝의 바로 앞 커밋입니다. 첫 줄은 그 커밋의 변경을 얹어 봅니다. 고칠 대상인 코드가 그사이 너무 많이 바뀌어 깔끔하게 안 얹힙니다. 충돌 정보가 인덱스와 작업 디렉토리에 적힙니다. 새 커밋은 생기지 않습니다.

둘째 줄은 맞춰야 할 변경을 훑어봅니다. 셋째 줄은 체리픽을 취소해 체리픽 전 상태로 돌아갑니다. 넷째 줄은 변경 앞뒤의 문맥 줄을 잘못 맞춰 생기는 실수를 피하려고 시간을 더 들여 다시 얹습니다.

충돌을 직접 풀었다면 git cherry-pick --continue 로 이어 갑니다. 여러 커밋을 옮기던 중이면 --skip 이 지금 커밋만 건너뛰고 나머지를 이어 갑니다.

Subversion 의 svn merge -c

Subversion 은 저장소 전체에 커밋마다 번호를 매깁니다. 이 번호를 리비전이라고 부릅니다. 체리픽은 병합 명령에 리비전 하나를 짚어서 합니다.

터미널
$ svn merge -c 355 ^/calc/trunk
--- Merging r355 into '.':
U    integer.c
--- Recording mergeinfo for merge of r355 into '.':
 U   .

$ svn status
M       integer.c

기능 브랜치에서 일하던 사람이 메인 브랜치인 trunk 의 리비전 355 하나만 가져오는 장면입니다. 동료가 그 리비전에서 지금 만드는 기능에 바로 걸리는 심각한 버그를 고쳤습니다. trunk 의 변경 전부를 받을 준비는 아직 안 됐을 수 있습니다. 그래도 그 수정은 꼭 있어야 일을 이어 갑니다.

가져온 변경은 바로 커밋되지 않습니다. integer.c 가 수정된 파일로 남아 평소대로 테스트를 거친 뒤 브랜치에 커밋합니다. 커밋하고 나면 Subversion 은 r355 를 이 브랜치에 병합한 것으로 표시합니다.

Mercurial 의 hg graft

Mercurial 에서는 이 일을 graft(접붙이기)라고 부릅니다. hg graft 는 다른 브랜치의 변경을 지금 브랜치로 복사합니다. 이력 그래프에서 브랜치를 병합하지 않고 변경 하나하나를 옮깁니다. 기본으로 원래 변경의 작성자·날짜·설명을 함께 복사합니다.

지금 리비전의 조상인 것, 이미 graft 한 것, 병합인 것은 건너뜁니다. --log 를 주면 커밋 메시지에 (grafted from CHANGESETHASH) 꼴의 주석이 붙습니다. 충돌이 나면 graft 가 멈춥니다. 충돌을 손으로 다 풀면 hg graft --continue 로 이어 갑니다.

리눅스 커널의 stable 트리

리눅스 커널은 이미 내보낸 판에 수정을 모아 내는 stable 트리를 따로 굴립니다. 메인라인은 새 판이 쌓이는 커널 본류입니다. 메인라인에 들어간 수정이 stable 트리로 옮겨 가는 길이 셋 있습니다.

길 하는 일
stable 태그 메인라인에 보낼 패치 설명에 stable 태그를 단다. 패치가 메인라인에 들어가면 작성자나 관리자가 더 할 일 없이 stable 트리에 적용된다
가져가 달라는 요청 이미 메인라인에 들어간 패치를 가져가 달라고 stable 팀에 부탁한다
같은 패치 제출 메인라인 변경과 같은 패치를 stable 팀에 직접 보낸다

stable 태그에는 체리픽에 앞서 필요한 선행 패치를 함께 적습니다.

Cc: <stable@vger.kernel.org> # 3.3.x: a1f84a3: sched: Check for idle
Cc: <stable@vger.kernel.org> # 3.3.x: 1b9508f: sched: Rate-limit newidle
Cc: <stable@vger.kernel.org> # 3.3.x: fd21073: sched: Fix affinity logic
Cc: <stable@vger.kernel.org> # 3.3.x
Signed-off-by: Ingo Molnar <mingo@elte.hu>

이 태그 줄들은 아래 순서로 체리픽하라는 뜻입니다. 선행 패치 셋을 먼저 옮깁니다. 이 커밋은 마지막에 옮깁니다.

터미널
git cherry-pick a1f84a3
git cherry-pick 1b9508f
git cherry-pick fd21073
git cherry-pick <this commit>

패치를 stable 팀에 직접 보낼 때는 패치 설명의 본문 위에 메인라인 커밋 아이디를 따로 한 줄 적어야 합니다. commit <sha1> upstream. 이나 [ Upstream commit <sha1> ] 처럼 적습니다. <sha1> 에 그 커밋 아이디가 들어갑니다.

보낸 패치가 원래 메인라인 패치와 달라졌다면 그 차이와 까닭을 패치 설명에 아주 분명하게 적어야 합니다. 옛 API(Application Programming Interface)에 맞추느라 고친 경우가 그 예입니다.

쿠버네티스의 릴리스 브랜치

kubernetes/kubernetes 저장소는 릴리스 브랜치로의 체리픽을 정해진 절차로 다룹니다. 풀 리퀘스트는 내 브랜치의 변경을 합쳐 달라고 올리는 요청입니다. 흔한 쓰임은 master 에 들어간 풀 리퀘스트를 릴리스 브랜치로 백포트하는 것입니다.

패치 릴리스는 이미 낸 판에 수정만 담아 다시 내는 릴리스입니다. 쿠버네티스는 패치 릴리스가 쉽고 안전하게 받아 쓸 수 있는 것이어야 한다고 정합니다.

회귀는 잘 되던 동작이 새 변경 뒤에 다시 깨지는 일입니다. 보안 수정과 심각한 버그 수정을 회귀 위험을 최소로 줄여 내보내려는 것입니다. 백포트 대상으로 기대하는 변경은 다음 다섯 종류뿐입니다.

  • 보안 수정
  • 회귀 수정
  • 심각한 버그 수정. 데이터 유실 · 메모리 손상 · 패닉 · 크래시 · 멈춤
  • 핵심 의존성을 갱신하는 데 앞서 필요한 변경
  • 릴리스 브랜치에서 실패하거나 들쭉날쭉한 테스트를 안정시키는 테스트 전용 변경

체리픽은 지원 중인 릴리스 브랜치 가운데 최신 판부터 옛 판 순서로 시작합니다. 최신 판이 1.27 이면 1.25 로 체리픽하기 전에 1.26 과 1.27 의 체리픽 풀 리퀘스트가 이미 있어야 합니다. 다음 판으로 올렸을 때 생기는 회귀를 막는 데 도움이 됩니다.

GitLab 의 체리픽 화면

GitLab 에서는 명령 대신 화면에서 체리픽합니다. GitLab 은 풀 리퀘스트를 병합 요청이라고 부릅니다. 고르는 대상은 커밋 하나일 수도 있습니다. 병합이 끝난 병합 요청 하나가 들여온 변경 전부일 수도 있습니다.

포크는 남의 저장소를 복사해 따로 굴리는 사본입니다. 쓰는 때로는 둘을 듭니다. 기본 브랜치의 버그 수정을 이전 릴리스 브랜치로 백포트할 때, 그리고 포크의 변경을 원래 저장소로 복사할 때입니다.

develop 과 main 두 브랜치가 있다고 합시다. develop 의 커밋 B 를 main 으로 체리픽하면 B 가 main 으로 복사됩니다. develop 브랜치는 손대지 않은 채 남습니다.

대가

이 절은 체리픽이 남기는 중복을 다룹니다. 대상은 같은 변경을 담은 두 커밋입니다. 그 두 벌이 언제 다시 만나는지, 그리고 도구가 두 벌을 알아보려고 무엇을 따로 두는지를 봅니다.

같은 변경이 두 벌로 남는다

체리픽을 마치면 같은 변경이 두 브랜치에 커밋 아이디가 다른 두 커밋으로 남습니다. 커밋 아이디가 다르니 이력만 봐서는 둘이 같은 변경인지 바로 안 보입니다.

두 브랜치를 나중에 합칠 때 이 두 벌이 다시 만납니다. Subversion 에서는 같은 변경을 같은 브랜치에 다시 병합하면 거의 언제나 충돌이 납니다. Subversion 은 옮긴 리비전을 병합한 것으로 기록합니다. 다음 병합은 그 리비전을 건너뜁니다.

이처럼 두 벌을 알아보는 장치를 도구마다 따로 둡니다.

도구 장치 하는 일
Subversion 병합 기록 옮긴 리비전을 병합한 것으로 표시해 다음 병합에서 건너뛴다
Mercurial 건너뛰기 · --log 이미 graft 한 변경은 건너뛴다. 메시지에 어디서 왔는지를 붙인다
Git git cherry 공백과 줄 번호를 뺀 차이로 상대 브랜치에 같은 변경이 있는지 견준다
Git 패치 아이디 줄 번호를 무시한 차이로 값을 구해 중복일 것 같은 커밋을 찾는다
Git 리베이스의 건너뛰기 기준 브랜치에 같은 변경이 이미 있는 커밋을 미리 떨어뜨린다
리눅스 커널 upstream 줄 stable 팀에 패치를 직접 보낼 때 메인라인 커밋 아이디를 적게 한다

같은 변경을 알아보는 계산

git cherry 는 한 범위의 커밋마다 상대 브랜치에 같은 변경이 있는지 견줍니다. 견주는 기준은 공백과 줄 번호를 뺀 차이입니다. 그래서 체리픽이나 리베이스로 복사된 커밋을 알아봅니다.

출력은 커밋마다 커밋 아이디 한 줄입니다. 같은 변경이 상대 쪽에 있으면 커밋 아이디 앞에 - 를, 없으면 + 를 붙입니다.

패치 아이디는 커밋 아이디와 다른 값입니다. 패치 하나가 고친 파일 차이들에서 줄 번호를 무시하고 SHA-1 해시를 구해 더한 것입니다. 주된 쓰임은 중복일 것 같은 커밋을 찾는 일입니다.

줄 번호를 빼고 구하므로 줄 번호만 다른 같은 변경은 같은 값을 받습니다. 그런 뜻에서 패치 아이디는 꽤 안정적입니다.

패치 아이디는 동시에 꽤 유일합니다. 서로 다른 변경이 같은 값을 받는 일이 드물다는 뜻입니다. 패치 아이디가 같은 두 패치는 거의 틀림없이 같은 것입니다.

리베이스가 치르는 읽기 비용

Git 의 리베이스는 커밋을 다시 얹기 전에 미리 거치는 단계가 있습니다. 기준 브랜치에 같은 변경이 이미 체리픽으로 들어가 있는 커밋을 골라 떨어뜨리는 단계입니다. --reapply-cherry-picks 나 --keep-base 를 주면 떨어뜨리지 않습니다.

이 판정을 하려면 기준 브랜치 쪽 커밋을 전부 읽어야 합니다. 기준 브랜치 쪽 커밋이 많은 저장소에서는 비쌀 수 있습니다.

출처 꼬리표가 붙는 조건

Git 의 -x 는 커밋 메시지 끝에 (cherry picked from commit …) 한 줄을 덧붙여 어느 커밋에서 왔는지 남깁니다. 이 줄은 충돌 없이 끝난 체리픽에만 붙습니다.

비공개 브랜치에서 가져올 때는 이 옵션을 쓰지 않습니다. 받는 쪽에는 쓸모없는 정보이기 때문입니다. 공개된 두 브랜치 사이에서 옮길 때는 이 정보가 도움이 될 수 있습니다. 개발 브랜치의 수정을 옛 판의 유지보수 브랜치로 백포트하는 경우가 그렇습니다.

체리픽이 안 맞는 커밋

브랜치 전체가 어차피 합쳐질 것이면 병합이나 리베이스로 한 번에 가져옵니다. 커밋 열 개를 하나씩 체리픽하면 같은 변경이 열 벌 더 생깁니다. 나중에 합칠 때 그 열 벌과 다시 만납니다.

앞 커밋에 기대는 커밋도 혼자 옮기면 안 맞습니다. 앞 커밋이 만들어 놓은 함수를 이 커밋이 고치는 경우를 떠올려 보면 됩니다. 내 브랜치에는 그 함수가 아직 없어서 고칠 대상이 없습니다. 충돌하거나 붙더라도 동작하지 않을 수 있습니다.

이럴 때는 기대는 앞 커밋을 먼저 옮깁니다. 앞의 커널 예시가 선행 패치를 체리픽 순서대로 적는 것이 이 방식입니다.

경계

차례로 한 체리픽과 리베이스

한 브랜치의 커밋 여러 개를 하나씩 차례로 체리픽하면 그것은 리베이스인가. 아닙니다. 변경을 다시 얹는 계산은 비슷합니다. 갈리는 것은 계산이 끝난 뒤 브랜치 이름이 어디를 가리키느냐입니다.

Git 의 리베이스는 이렇게 돕니다. 먼저 내 브랜치가 갈라진 뒤의 커밋 가운데 기준 브랜치에 같은 변경이 없는 것을 추립니다. 기준 브랜치의 끝에서 출발해 그 커밋들을 하나씩 차례로 다시 얹습니다. 이 단계가 커밋마다 git cherry-pick 을 돌리는 것과 비슷합니다.

체리픽을 「커밋 하나짜리 리베이스」라고 설명하는 글이 있는 것도 이 비슷함 때문입니다.

차이는 마지막 단계에 있습니다. 리베이스는 끝으로 내 브랜치 이름을 다시 얹은 마지막 커밋으로 옮깁니다. 원래 커밋들은 브랜치에서 떨어져 나갑니다. 체리픽은 지금 브랜치 끝에 새 커밋을 붙일 뿐입니다. 원래 커밋은 원래 브랜치에 남습니다.

아래 그림은 topic 브랜치의 커밋 A·B·C 를 두 방식으로 master 위에 얹은 결과입니다. 화살표는 커밋이 부모를 가리키는 방향입니다. 점선은 사이 커밋을 줄인 것입니다.

flowchart TD
    subgraph 리["topic 에서 리베이스한 뒤"]
        r1["C' · topic 이 가리킨다"] --> r2["B'"]
        r2 --> r3["A'"]
        r3 --> r4["G · master 끝"]
        r4 -.-> r6["E · 갈라진 커밋"]
        r5["원래 A·B·C · 가리키는 브랜치 없음"] --> r6
    end
    subgraph 체["master 에서 A·B·C 를 차례로 체리픽한 뒤"]
        c1["C' · master 가 가리킨다"] --> c2["B'"]
        c2 --> c3["A'"]
        c3 --> c4["G"]
        c4 -.-> c6["E · 갈라진 커밋"]
        c5["원래 A·B·C · topic 이 가리킨다"] --> c6
    end
    리 ~~~ 체

두 그림에서 C'·B'·A' 로 이어진 새 사슬은 같은 모양입니다. 리베이스 쪽에서는 topic 이 새 사슬로 옮겨 가 원래 A·B·C 를 가리키는 브랜치가 없어집니다. 체리픽 쪽에서는 master 가 새 사슬 끝으로 나아갑니다. topic 은 원래 A·B·C 를 계속 가리킵니다. 두 브랜치에 같은 변경이 두 벌 남는 것은 체리픽 쪽뿐입니다.

그래서 판정은 「아니다」입니다. 차례로 한 체리픽은 원래 브랜치를 둔 채 사본을 하나 더 만듭니다. 리베이스는 브랜치 자체를 옮깁니다.

관련 항목

체리픽이 골라 옮기는 이력의 단위

커밋 · 브랜치 · 부모 커밋 · 공통 조상 · HEAD · 해시 · 변경분 · 패치 · 리비전 · 체인지셋 · 병합 요청

체리픽과 같은 일을 부르는 다른 이름

백포트 · 그래프트 · 포워드포트

체리픽 대신 쓸 수 있는 다른 합치기 방식

병합 · 리베이스 · 세 갈래 병합 · 병합 커밋 · 스쿼시 · 빨리 감기 · 되돌리기

체리픽이 멈췄을 때 사람이 손대는 대상

충돌 · 충돌 해소 · 작업 디렉토리 · 인덱스 · 스태시

체리픽으로 옮긴 커밋을 되찾거나 물리는 수단

레퍼로그 · 체크아웃 · 리셋 · 분리된 HEAD

체리픽을 정책으로 쓰는 릴리스 운영 방식

릴리스 엔지니어링 · 브랜치 전략 · 릴리스 브랜치 · 핫픽스 · 메인라인 · 트렁크 · 유지보수 브랜치 · 패치 릴리스 · 회귀 · 포크

체리픽이 남기는 중복을 다루는 개념

중복 커밋 · 패치 아이디 · 병합 추적 · 선형 이력 · 이력 재작성

체리픽을 제공하는 버전관리 도구

Git · Subversion · Mercurial · 버전관리 · 분산 버전관리 · GitHub · GitLab

다른 이름: cherry-pick · git cherry-pick · 체리 픽 · 체리피킹