사전 리베이스
개념

리베이스

gabury1고친 사람 github-actions[bot]

리베이스는 내가 쌓은 커밋들을 다른 기준점 위에 다시 쌓는 일입니다. 갈라졌던 두 줄기를 합치면서도 갈라진 자국을 남기지 않고 이력을 한 줄로 만듭니다. 대신 옮겨 붙은 커밋은 원래 커밋과 다른 새 커밋이 됩니다.

쉽고 빠른 이해

리베이스는 내 작업 커밋을 떼어 최신 기준 위에 다시 얹는 일입니다. 동료가 그사이 올려 둔 변경 뒤에 내 커밋 세 개를 차례로 다시 쌓는 것이 그 일입니다.

다시 쌓지 않으면 갈라진 두 줄기를 합칠 때마다 합쳤다는 기록이 하나씩 더 생깁니다. 이력이 그물처럼 얽혀서 나중에 어느 변경이 어디서 왔는지 따라가기 어려워집니다.

어떻게 도나:

  1. 두 줄기가 갈라진 지점을 찾아 내 쪽에만 있는 커밋을 고릅니다
  2. 내 브랜치의 출발점을 기준 브랜치의 끝으로 옮깁니다
  3. 고른 커밋의 변경을 하나씩 그 위에 다시 얹습니다

옮겨 붙은 커밋은 이름이 바뀐 새 커밋입니다. 이미 남에게 건네준 커밋을 이렇게 옮기면 동료는 옛 커밋을 그대로 들고 있어서 두 이력이 어긋납니다.

상세

이 절은 회의록 비유로 리베이스를 먼저 떠올린 다음, 커밋이 서로를 어떻게 매달고 있는지 봅니다. 그다음 리베이스가 도는 순서와 커밋 이름이 바뀌는 까닭을 보고, 병합과 갈리는 대목을 표로 가릅니다. 마지막으로 남과 이미 나눈 커밋에 쓰면 무엇이 어긋나는지 짚습니다.

옮겨 적는 회의록

동아리 회의록 한 권을 여럿이 돌려 본다고 해 봅시다. 나는 마지막 장 뒤에 메모 세 장을 이어 붙였습니다. 그사이 다른 사람이 같은 마지막 장 뒤에 자기 장을 먼저 붙여 두었습니다.

내 메모를 그대로 두면 회의록이 두 갈래로 갈립니다. 갈라진 것을 합치려면 「여기서 두 갈래가 만났다」는 장을 한 장 더 끼워야 합니다. 리베이스는 그 장을 끼우는 대신, 내 메모 세 장을 남의 장 뒤로 옮겨 적습니다.

커밋이 매달린 사슬

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

그래서 이력은 나란한 목록이 아니라 사슬입니다. 부모를 따라 거슬러 올라가면 그 줄기가 걸어온 기록이 한 줄로 나옵니다. 브랜치는 그 줄기의 끝이 어느 커밋인지를 가리키는 이름입니다.

두 줄기가 갈라졌다는 말은 어느 커밋까지는 같고 그 뒤부터 다르다는 뜻입니다. 양쪽을 거슬러 올라갔을 때 처음 만나는 같은 커밋을 공통 조상이라고 합니다.

리베이스는 내 커밋들이 매달린 밑동을 이 조상에서 기준 브랜치의 끝 커밋으로 바꿉니다. 영어 이름 rebase 도 밑동을 다시 잡는다는 뜻입니다.

도는 순서

리베이스는 한 번에 통째로 옮겨 붙지 않습니다. 먼저 내 쪽에만 있는 커밋이 무엇인지 골라내고, 그 변경을 새 밑동 위에 하나씩 다시 얹습니다. 아래 그림이 옮기기 전과 뒤의 사슬 모양을 나란히 보입니다.

flowchart TD
    subgraph 전["옮기기 전"]
        bA["내 커밋 A"] --> b0["공통 조상"]
        bB["내 커밋 B"] --> bA
        bM["기준 브랜치의 새 커밋"] --> b0
    end
    subgraph 후["옮긴 뒤"]
        aA["내 커밋 A · 새 이름"] --> aM["기준 브랜치의 새 커밋"]
        aB["내 커밋 B · 새 이름"] --> aA
        aM --> a0["공통 조상"]
        oldA["떨어져 나온 옛 커밋 A · B"] --> a0
    end
    전 ~~~ 후

화살표는 커밋이 자기 부모를 가리키는 방향입니다. 옮기기 전에는 내 커밋 A 가 공통 조상에 매달려 있고, 옮긴 뒤에는 기준 브랜치의 새 커밋에 매달립니다. 사슬이 하나로 이어져서 갈라졌다 만난 모양이 남지 않습니다.

그림 아래쪽의 옛 커밋 A · B 는 지워진 것이 아닙니다. 어느 브랜치도 그것을 안 가리키게 되어 눈에 안 띌 뿐입니다.

이름이 바뀌는 까닭

커밋의 이름은 사람이 붙이지 않습니다. 고친 내용과 부모, 만든 사람 같은 정보를 한데 모아 해시를 돌려 나온 값이 그 커밋의 이름입니다. 해시는 입력이 조금만 달라도 전혀 다른 값을 내놓는 계산입니다.

부모도 그 입력에 들어 있습니다. 그래서 매달린 부모가 바뀌면 고친 내용이 한 글자도 안 달라져도 이름이 달라집니다. 리베이스가 커밋을 옮기지 않고 새로 만드는 까닭입니다.

브랜치가 거쳐 온 위치를 따로 적어 두는 기록이 있습니다. 레퍼로그라고 부르고, 리베이스 전에 브랜치가 가리키던 커밋도 거기 남습니다. 옛 커밋을 되찾고 싶으면 그 기록을 뒤집니다.

충돌로 멈추는 때

다시 얹는 일은 커밋 하나씩 차례로 일어납니다. 얹으려는 변경과 이미 그 밑에 깔린 내용이 같은 줄을 서로 다르게 고쳤으면 기계가 어느 쪽인지 고를 수 없습니다. 그러면 리베이스는 그 커밋에서 멈추고 사람을 부릅니다.

사람이 남길 쪽을 골라 충돌 해소를 하면 나머지 커밋부터 다시 이어 갑니다. 커밋마다 멈출 수 있어서 비슷한 곳을 여러 번 고르게 되기도 합니다.

병합은 갈라진 양쪽을 한 번에 맞대고 합치므로 충돌도 한 번에 고릅니다. 고르는 횟수가 갈리는 것이 두 방식의 눈에 띄는 차이 하나입니다.

병합과 갈리는 대목

병합으로 합치면 두 줄기를 잇는 커밋이 하나 생깁니다. 이 커밋을 병합 커밋이라고 합니다. 합치는 일이 잦아지면 병합 커밋이 그만큼 쌓이고, 이력은 갈라졌다 붙기를 되풀이한 그물처럼 보입니다.

그물이 된 이력에서는 어느 변경이 어느 줄기에서 왔는지 따라가려면 갈래를 하나씩 헤쳐야 합니다. 리베이스는 그 갈래를 안 만들고 한 줄로 남기려는 선택입니다.

둘 다 갈라진 줄기를 하나로 되돌리는 방법입니다. 결과로 남는 이력의 모양과 치르는 대가가 갈립니다.

병합 리베이스
이력 모양 갈라졌다 만난 자국이 남는다 한 줄로 이어진다
새로 생기는 커밋 두 줄기를 잇는 병합 커밋 하나 옮겨 붙는 커밋마다 하나씩
원래 커밋 그대로 남는다 이름이 다른 새 커밋으로 바뀐다
충돌을 고르는 때 합치는 한 번 다시 얹는 커밋마다
남과 나눈 이력 그대로 둬도 된다 어긋난다

표의 마지막 줄이 무엇을 고를지 정하는 대목입니다. 내 컴퓨터에만 있는 커밋이면 두 방법 다 쓸 수 있고, 남에게 이미 건네준 커밋이면 리베이스는 아래 어긋남을 안고 갑니다.

남과 나눈 커밋에 쓸 때의 어긋남

이미 푸시해서 동료가 받아 간 커밋을 리베이스하면 두 이력이 갈립니다. 내 쪽에는 새 이름의 커밋이 있고 동료 쪽에는 옛 이름의 커밋이 그대로 있습니다. 같은 작업을 서로 다른 커밋으로 하나씩 들고 있는 셈입니다.

이 상태로 올려 보내려면 받는 쪽의 확인을 건너뛰는 강제 푸시를 써야 합니다. 그러면 원격에 있던 옛 커밋이 브랜치에서 떨어져 나갑니다. 동료가 그 커밋 위에 이어 붙인 작업까지 같이 떨어집니다.

그래서 흔한 규칙은 하나입니다. 리베이스는 아직 내 컴퓨터 밖으로 안 나간 커밋에만 씁니다. 여럿이 함께 쓰는 브랜치에는 강제 푸시 자체를 막아 두는 것이 흔합니다.

커밋을 손보는 대화식 리베이스

기준점을 바꾸지 않고 내 커밋들만 손보는 쓰임이 하나 더 있습니다. 다시 얹는 도중에 커밋을 어떻게 처리할지 사람에게 묻는 방식이고, 대화식 리베이스라고 부릅니다.

커밋 여럿을 하나로 합치는 스쿼시, 커밋 메시지 고치기, 커밋 순서 바꾸기, 커밋 빼기가 이 쓰임에 들어갑니다. 검토를 받기 전에 어지러운 작업 기록을 읽을 만한 덩이로 추리는 데 씁니다.

어느 쪽이든 커밋을 새로 만드는 것은 같습니다. 남과 나눈 커밋에 안 쓴다는 규칙도 그대로 걸립니다.

Git 에서 부르는 명령

널리 쓰는 Git 에서는 이 동작을 명령 하나로 부릅니다. 기준으로 삼을 브랜치 이름을 함께 댑니다.

터미널
git rebase main       # main 끝에 다시 쌓기
git rebase --abort    # 멈춘 뒤 원래대로
git rebase -i main    # 커밋을 골라 손보기

첫 줄이 기본 쓰임입니다. 둘째 줄은 충돌로 멈춘 뒤 그만두고 리베이스 전 모습으로 돌아가는 명령이고, 셋째 줄이 앞에서 본 대화식 리베이스입니다.

관련 항목

리베이스가 다시 쌓는 이력의 단위

커밋 · 브랜치 · 부모 커밋 · 공통 조상 · HEAD · 해시

리베이스와 같은 일을 하는 다른 합치기 방식

병합 · 세 갈래 병합 · 병합 커밋 · 빨리 감기 · 스쿼시 · 체리픽

리베이스가 멈췄을 때 사람이 손대는 대상

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

리베이스 뒤에 이력을 맞추려고 하는 동작

푸시 · 강제 푸시 · 페치 · 풀 · 원격 저장소 · 원격 추적 브랜치

리베이스로 떨어져 나간 커밋을 되찾는 수단

레퍼로그 · 되돌리기 · 체크아웃 · 분리된 HEAD

리베이스를 정책으로 정하는 협업 방식

브랜치 전략 · 트렁크 · 풀 리퀘스트 · 코드 리뷰 · 선형 이력

리베이스를 제공하는 버전관리 도구

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

다른 이름: rebase · git rebase · 리베이싱