작가님의 허락을 받고 번역한 글입니다.(원문)
초보 개발자 시절, 제게 가장 중요하고도 어려웠던 git 기능은 rebasing 이었습니다. 지금와서 돌이켜보면, 깔끔한 초보자용 입문서를 찾는 것은 어려운 문제가 아니었습니다. 이 글은 과거의 저 그리고 저와 같은 미래를 꿈꾸는 개발자를 위한 것입니다.
과거의 저에게 해주고 싶은 또 다른 작은 조언은 Jekyll을 활용해서 개인 사이트를 만들라는 것입니다. 워드프레스 사이트 호스팅하느라 안그래도 부족한 용돈을 낭비하지 마세요.
어쨌든 즐겨주시기 바랍니다. 이 글 읽으려면 git과 버전 컨트롤에 대한 기본적인 지식을 가지고 있어야 합니다. 왜 그런지는 읽어보면 알겁니다.
첫번째, 왜 rebase를 해야할까?
온라인에서 Cupid의 컵케익이라는 컵케익 가게를 시작하게된 주니어 개발자가 있다고 가정해봅시다. 이 가게는 온라인 판매로 엄청난 실적을 올리고 있고, 베테랑 개발자를 많이 보유하고 있습니다. 여러분은 프론트엔드를 주로 맡아 일하게 되었습니다.
여러분의 첫 업무는 카드 컴포넌트를 업데이트하는 것이었습니다. 사람들이 구매할 컵 케이크를 찾을 때, 각 컵케이크는 이 카드 모음 안에 있습니다. 저장소에서 가장 최근 master 브랜치 버전을 가져오고, 새로운 브랜치를 만들어 일을 시작합니다.
몇몇 커밋 이후에 모든 것을 완료할 수 있었습니다. 카드는 더 예뻐졌고, 모든 테스트도 통과했습니다. 심지어 모바일 레이아웃도 개선시켰습니다. 남은 것은 실제 라이브 서비스에 업데이트 되도록, 당신의 기능 브랜치를 마스터 브랜치로 병합시키는 것입니다.
하지만 멈춰야합니다!
왜냐하면 당신이 카드 컴포넌트를 만드는 동안 다른 사람들이 해당 사이트에서 작업을 할 수 있기 때문입니다.
- 한 개발자는 네비게이션바를 변경했습니다.
- 다른 한명은 불필요한 정보를 삭제하기 위해 데이터베이스 필드를 변경했습니다.
- 또 다른 사람은 각 컵케이크에 대한 추가 정보를 덧붙였습니다.
- 누군가는 아무도 모르게 가게의 은행 기록을 변경하여 돈을 횡령했습니다.
이 모든 변화는 우려와 고민을 불러일으킵니다. 당신이 만든 것에 영향력이 있거나 겹치는 변경사항이 누군가에 의해 병합된다면, 이것은 컵케이크 웹사이트에 버그를 발생시킬 수 있습니다. 변경사항의 차이를 보면, 버그 한개 정도는 보일 겁니다. 어떻게 하면 충돌이나 다른 사람들의 작업을 놓치지 않으면서, 여러분의 작업을 병합할 수 있을까요?
이런 상황이 여러분이 rebase를 해야하는 전형적인 경우입니다.
rebase는 뭘까?
여러분이 마스터 브랜치에서 새로운 브랜치를 만들었다고 가정해봅시다. 그 마스터 브랜치에는 커밋 1번이 있습니다. 여러분의 브랜치에서 발생하는 모든 커밋은 1번 커밋의 위에 있습니다. 여러분의 브랜치를 병합할 준비가 되었을 때 보니, 가장 최근 커밋이 5번 커밋인 것을 발견하게 됩니다.
Rebase는 여러분의 브랜치에 있는 모든 커밋을 받아들이고, 그 커밋들을 1 번 커밋이 아니라 5번 커밋 위에 추가하는 작업입니다. 만약 여러분이 1번 커밋이 여러분 브랜치의 베이스라고 생각하고 있ㄷ면, 그 베이스가 가장 최근 것인 5번 커밋으로 변경된다고 보면 됩니다. 그래서 rebase라고 불리는 겁니다.
rebase를 하는 방법
여러분은 Cupid 컵케잌을 위한 훌륭한 카드 컴포넌트를 가지고 있습니다. 이제 여러분은 rebase가 무엇인지 알게 되었습니다. 이제 어떻게 해야할지 더 자세히 알아봅시다.
먼저, 여러분이 rebase할 브랜치가 최신 버전인지 확인해야 합니다. 이 예제에서는 마스터 브랜치라고 가정하겠습니다. git checkout master
그리고 git pull
을 차례로 실행시켜 최신 버전을 가져옵니다. 그리고 나서 git checkout updated-card
을 실행시켜 여러분의 브랜치로 다시 체크아웃합니다.
직관적인 rebase는 꽤 간단한 명령어 구조를 가지고 있습니다: git rebase <branch>
. branch
는 여러분이 rebase할 _목적지 브랜치_입니다. 여기서는 git rebase master
를 실행시키면 됩니다. 만약 충돌이 없다면, 이것으로 rebase 작업이 끝납니다.
Rebase 자체는 기술적으로 저장소의 커밋 기록을 재작성해서, 여러분의 이전 커밋을 삭제하고 동일한 커밋을 새로 만드는 겁니다. 이건 원격 저장소에 rebase할 때 추가적으로 무언가 필요하다는 것을 의미합니다. git push --force
를 사용하면 잘 수행됩니다. 하지만 더 안전한 옵션은 git push --force-with-lease
입니다. 후자는 여러분에게 알지 못했던 upstream 변화를 경고하고 push를 방지합니다. 이렇게 하면 다른 사람의 작업을 덮어쓰는 것을 피할 수 있어 좀 더 안전하게 작업할 수 있습니다.
이것으로 여러분의 rebase는 다 끝났습니다. 하지만, rebase 작업들은 항상 원활하게 진행되지는 않습니다.
rebase 충돌은 어떻게 처리합니까?
다른 사람의 작업과 여러분의 새로운 카드 작업이 충돌할 것을 걱정했던게 기억나십니까? 바로 이 상황이 rebase 충돌 상황입니다. 한 개발자가 새 컵 케이크 카드에 칼로리와 같은 새로운 정보를 추가했습니다. 양쪽에서 업데이트된 마크업들은 같은 라인에 변경사항이 있습니다. 이것은 rebase가 자동으로 일어날 수 없다는 것을 의미합니다. Git은 어느 부분의 변경사항을 유지하고 제거할지 알 수 없습니다. 그래서 개발자가 꼭 해결해야합니다.
다행히, git은 이 작업을 쉽게 할 수 있도록 도와줍니다. Rebase를 하는 동안, git은 각 커밋을 새로운 베이스에 따라 하나씩 추가합니다. 만약 특정 라인에서 서로 다른 두 커밋이 충돌을 하게 되면, rebase를 중단하고, 해당 부분 충돌이 해결되었을 때 다시 시작합니다.
이전에 병합 충돌을 해결해본 경험이 있다면, rebase 충돌은 그와 동일한 방식으로 다루어질 것입니다. git status
를 실행하는 것은 어디에 충돌이 있는지 알려줄 것입니다. 그리고 충돌이 발생하는 부분의 코드는 여러분이 어떻게 고칠지 정할 수 있게 한 눈에 볼 수 있습니다.
모든 충돌이 해결되면, 일반적인 병합 충돌처럼 변경사항을 add
와 commit
하면 됩니다. 그러면 git rebase --continue
를 실행하여 git이 나머지 커밋을 rebase할 수 있습니다. 추후에도 충돌이 발생한다면 잠시 일시 정지를 하게 됩니다. 그리고 마찬가지로 충돌이 해결이 되었을 때, push --force-with-lease
를 하면 됩니다.
rebase에는 많이 사용하지 않지만 두 가지 옵션이 있습니다. 하나는 git rebase --abort
입니다. 이는 rebase를 시작하기 전으로 되돌아가고자 할 때 사용하는 옵션입니다. 예상치 못한 충돌이 일어나 결정을 제때 내릴 수 없을 때 유용합니다. 또 다른 하나는 git rebase --skip
입니다. 충돌을 일으키는 커밋들을 완전히 건너뛰는 기능입니다. 불필요한 커밋이 아니고, 만사가 귀찮지 안하다면, 많이 사용하지는 않을 것입니다.
rebase 프로세스를 추가적으로 제어할 수 있나요?
보셨듯이, git rebase는 강력합니다. 유명한 사람들이 말하듯, 더 강력한 힘은 더 큰 책임을 요구합니다. 감사하게도, 여러분은 rebase의 프로세스를 넘어 더 많은 것을 제어할 수 있습니다.
그렇게 하기 위해서는 --interactive
, 혹은 -i
플래그를 명령어 안에 포함시키면 됩니다. 앞선 컵 케잌 rebase의 경우에 적용해보면, git rebase -i master
이 됩니다.
이것은 rebase하려는 모든 커밋들의 목록을 보여줍니다. 어떤 것을 멈추고 수정할지 그리고 어떤 것을 건너뛰고 혹은 병합할지 결정할 수 있습ㄴ니다. 이것에 대해서 여기서는 더 이상 자세히 다루지는 않겠습니다. 관심이 있으시면 이 글이 더 나아가는데 좋은 시작점이 될 겁니다.
다만, 모든 것은 vim 인터페이스를 통해 이루어진다는 점을 유의하시면 되겠습니다. Rebase를 실행시키기 위해서는, esc + : + w + q + enter
를 입력하시면 됩니다. VIM의 나머지 부분 설명은 다른 분께 부탁드리도록 하겠습니다.
마지막, 유용한 rebase 트릭: autosquashing!
대화형 rebase에서 하나 유용한 트릭이 있다면, autosquashing commit을 소개할 수 있을 것 같습니다. 이것은 두 커밋을 함께 병합하고, 새로우 ㄴ이름으로 바꿉니다. 만약에 크기는 작지만 개수가 많은 유사 커밋들이 결합되어 있거나, 오래된 커밋을 수정할 필요가 있을 경우, 굉장히 유용합니다. Rebasing은 이런 상황을 "autosquash"로 아주 쉽게 만들어 줍니다.
Cupid Cupcake의 새 카드를 위한 브랜치에 5개의 커밋이 있다고 가정해봅시다. 그런 다음 우리가 2번째 커밋에서 작은 세부사항을 놓친 것을 깨달았다고 칩시다. 만약 커밋 기록을 깨끗하게 유지하고 싶다면, 이 변경사항들을 두번째 커밋에 병합할 필요가 있습니다.
이 작업은 fixup
커밋을 활용하여 할 수 있습니다. 이렇게요:
- 변경사항을 만들고, stage 단계로 만듭니다.
- 합쳐버리고 싶은 커밋들의 아이디를 구해옵니다. 여기서는 123456ABCD라고 하겠습니다. 커밋 아이디는
git log
명령어를 실행시켜서 찾으실 수 있습니다. - Fixup 커밋을 위한 공식은 다음과 같습니다.
git commit --fixup <commitID>
. 우리의 경우에는git commit --fixup 123456ABCD
를 사용하면 됩니다. 이것은 변경사항을 일반적으로 커밋합니다. 하지만 이름은 두번째 커밋과 동일하게 됩니다. - 다음으로 rebase를 실행시킬 겁니다. 중요한 것은 제대로 된 매개 변수르 ㄹ포함시켜야 한다는 것입니다.
git rebase -i autosquash master
를 실행시킵니다. - VIM 인터페이스에 모든 커밋이 나열되어 있는, 대화형 rebase를 위한 일반적인 페이지를 보게 되실 겁니다. 하지만 우리의 fixup 커밋은 자동으로 병합되어 우리의 두번째 커밋 바로 밑에 있습니다.
- Rebase를 실행하시면, 커밋들은 자동으로 결합됩니다. 만약 충돌이 있다면, 일반적인 병합 충돌처럼 해결을 요청할 겁니다. 그리곤 계속됩니다.
이 기능은 지루하고 헷갈리게 만드는 수많은 작은 수정들이 기록을 지저분하게 만드는 것을 막는데 굉장히 유용합니다. 최고의 커밋 기록은 이해할 수 있는 이야기를 말해줍니다. Autosquashing은 아주 불쾌한 주석에 의해 압도당하는 것을 방지해줍니다. 활용해보십시오.
컵케익을 즐기십시오!
이 글이 git rebasing의 기초와 용도, 사용법, 접근법을 설명하는데 도움이 되었길 바랍니다. 마침내 rebase를 스스로 이해했을 때, rebase는 자주 사용하고 필수적인 도구가 되었습니다. 다른 주니어 개발자들이 좌절감이나 패닉을 겪지 않고 똑같이 잘 할 수 있기를 바랍니다.
결국, 더 많은 사람들이 컴케잌을 사먹는걸 돕는게 우리가 원하는 거 아니겠습니까? 물론입니다. Git rebasing은 모든 좋은 것들과 같이 그 목표를 현실로 만들어 주는 것입니다.
Comments