티스토리 뷰

git을 소수 인원으로 사용할 때에는 push, pull, merge만 사용해도 큰 문제가 없습니다. 개인 프로젝트할 때에는 그렇게 했구요. git history도 깔끔했습니다. 하지만 소수인원이 아닌 회사에서 협업하게 된다면 git history가 엉망이될 겁니다. 이럴 때 우리가 사용할 수 있는 기능이 rebase입니다. 

개인적으로 sourcetree라는 git GUI툴을 사용하지만 사용하면 사용할수록 시각적인 매력 외에 불편함을 느끼고 있었습니다. CLI가 익숙해지면 계속 그것만 사용하게 될거라는 분도 계셨구요. CLI로 진행하게 된다면 git history가 깔끔하게 정렬되어 있는 게 좋을 거 같았습니다. 

 

 

Merge와 Rebase의 차이?

Git에서 한 브랜치에서 다른 브랜치로 합치는 방법으로는 두 가지가 있습니다. 하나는 merge , 나머지 하나는 rebase입니다.

두 개의 브랜치로 나누어진 커밋 히스토리

두 브랜치를 합치는 가장 쉬운 방법은 merge명령어를 사용하는 것입니다. 두 브랜치의 마지막 커밋 두 개(C3, C4)와 공통 조상 (C2)을 사용하는 3-way Merge로 새로운 커밋을 만들어 냅니다. 

 

나뉜 브랜치를 merge하기

비슷한 결과를 만드는 다른 방식으로, C3에서 변경도니 사항을 Patch로 만들고 이를 다시 C4에 적용시키는 방법이 있습니다. Git에서는 이런 방식을 Rebase라고 합니다. rebase 명령으로 한 브랜치에서 변경된 사항을 다른 브랜치에 적용할 수 있습니다. 

rebase 실행 명령어 

  • master -> main 
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

-> 두 브랜치가 나뉘기 전인 공통 커밋으로 이동하고 나서 그 커밋부터 지금까지 checkout한 브랜치가 가리키는 커밋까지 diff를 차례로 만들어 어딘가에 임시로 저장해놓고 Rebase할 브랜치(experiment)가 합칠 브랜치(master)가 가리키는 커밋을 가리키게 하고 아까 저장해놓았던 변경사항을 차례대로 적용합니다. 

C4의 변경사항을 C3에 적용하는 Rebase과정

그리고 나서 master 브랜치를 Fast-forward 시킵니다. 

$ git checkout master
$ git merge experimen

master 브랜치를 fast-forward 시키기

->C4로 표기된 커밋에서의 내용은 merge 예시에서 살펴본 C5커밋에서의 내용과 같을 것입니다. Merge이든 rebase든 둘 다 합치는 관점에서는 서로 다를 게 없습니다. 하지만, Rebase가 좀 더 깨끗한 히스토리를 만듭니다.

Rebase한 브랜치의 log를 살펴보면 히스토리가 선형(linear)입니다. 일을 병렬로 동시에 진행해도 Rebase하고 나면 모든 작업이 차례대로 수행된 것처럼 보입니다. (깔끔한 git history를 가지게 됐습니다.)

 

Rebase는 보통 리모트 브랜치에 커밋을 깔끔하게 적용하고 싶을 때 사용합니다. 아마 이렇게 Rebase하는 리모트 브랜치는 직접 관리하는 것이 아니라 그냥 참여하는 브랜치일 것입니다 메인 프로젝트에서 Patch를 보낼 준비가 되면 하는 것이 Rebase니까 브랜치에서 하던 일을 완전히 마치고 orgin/master 로 Rebase합니다. 이렇게 rebase를 하고나면 프로젝트 관리자는 어떠한 통합작업도 필요 없게 됩니다. master 브랜치를 fast-forward 시키면 됩니다. 

 

Rebase 활용 관련 사례는 링크를 걸어두겠습니다. 참고해주세요. 

활용 링크

Rebase의 위험성

rebase는 장점이 많은 기능이지만 단점이 없는 것은 아닙니다. 

 

이미 공개 저장소에 Push한 커밋을 rebase하지마라

  • rebase는 기본의 커밋을 그래도 사용하는 것이 아니라 내용은 같지만 다른 커밋을 새로 만드는 것입니다. 새 커밋을 서버에 push하고 동료 중 누군가가 그 커밋을 pull해서 작업을 한다고 칩니다. 그런데 그 커밋을 gir rebase로 바꿔서 push해버리면 동료가 다시 push했을 때 동료는 다시 merge해야 합니다. 그리고 동료가 다시 merge한 내용을 pull하게 되면 나의 코드는 엉망이 됩니다. 관련된 자세한 예시는 링크를 참고합시다. 

Rebase 한 것을 다시 Rebase하기

위의 상황(한 팀원이 다른 팀원의 의존하는 커밋을 없애고 Rebase한 커밋을 다시 push함)에 빠지게 되면 유용한 Git 기능이 있습니다. 어떤 팀원이 강제로 내가 한 일을 덮어썼다고 하면 내가 했던 일은 무엇이고 덮어쓴 내용이 무엇인지 알아내야 합니다.

커밋 SHA 체크섬 외에도 Git은 커밋에 Patch할 내용으로 SHA-1 체크섬을 한번 더 구합니다. 이 값은 "patch-id"라고 합니다. 덮어쓴 커밋을 받아서 그 커밋을 기준으로 Rebase할 때 Git은 원래 누가 작성한 코드인지 잘 찾아낼 수 있다. 

그래서 Patch가 원래대로 잘 적용된다. 

위의 상황으로 돌아가 다시 merge하는 대신 git rebase gamza/master (gamza는 그냥 제 이름이라 칩시다)명령을 실행하면 Git은 아래와 같은 작업을 합니다. 

  • 현재 브랜치에만 포함된 커밋을 결정한다. (C2, C3, C4, C6, C7)
  • Merge 커밋이 아닌 것을 결정한다. (C2, C3, C4)
  • 이 중 merge할 브랜치에 덮어쓰이지 않은 커밋을 결정한다. (C2, C3. C4는 C4’와 동일한 Patch다)
  • 결정한 커밋을 gamza/master 브랜치에 적용한다.

결과를 확인해보면 같은 merge를 다시 한다 같은 결과 대신 제대로 정리된 강제로 덮어쓴 브랜치에 Rebase하기 같은 결과를 얻을 수 있습니다. 

 

동료가 생성했던 C4와 C4` 커밋 내용이 완전히 같을 때만 위와 같이 동작됩니다. 커밋 내용이 아예 다르거나 비슷하다면 커밋이 두 개 생깁니다. (같은 내용이 두 번 커밋될 수 있기 때문에 깔끔X)

 

git pull 명령을 실행할 때 옵션을 붙여서 git pull --rebase 로 Rebase 할 수 있습니다. 물론 git fetchgit rebase gamza/master 이 명령을 직접 순서대로 실행해도 됩니다. 

 

✅git pull 명령을 실행할 때 기본적으로 --rebase 옵션이 적용되도록 pull .rebase 설정을 추가할 수 있습니다. 
git config --global pull .rebase true 명령으로 추가합니다. 

Push 하기 전에 정리하려고 Rebase하는 것은 괜찮습니다. 또 절대 공개하지 않고 혼자 Rebase 하는 경우도 괜찮습니다. 하지만, 이미 공개하여 사람들이 사용하는 커밋을 Rebase하면 틀림없이 문제가 생깁니다. 

차후에, 후회하지말고 git pull --rebase로 문제를 미리 방지할 수 있다는 점을 작업하는 동료와 모두 함께 공유하길 권고합니다. 

 

그래서 Rebase vs Merge?

히스토리를 보는 관점 중 하나는 작업한 내용의 기록으로 보는 것(혹은 프로젝트가 어떻게 진행되었나에 대한 이야기)입니다. 작업 내용을 기록한 문서이고, 각 기록은 각각 의미를 가지며, 변경할 수 없습니다. 이런 관점에서 커밋 히스토리를 변경한다는 것은 역사를 부정하는 꼴입니다. 이렇게 했을 때 지저분하게 수많은 Merge 커밋이 히스토리에 남게되면 문제가 과연없을 지 생각해봐야 합니다. 

첨언을 하자면  로컬 브랜치에서 작업할 때는 히스토리를 정리하기 위해서 Rebase할 수도 있지만, 리모트 등 어딘가에 Push로 내보낸 커밋에 대해서는 절대 Rebase하지 말아야 합니다.

 


위의 설명에서 봤듯이 merge는 branch를 통합하는 것이며, Rebase는 branch의 base를 옮긴다는 개념의 차이가 있습니다. 그래서 둘 중에 하나만 쓰는 것이 아니라 두 가지가 존재합니다.

  1. merge만 사용한다 (only merge)
  2. Rebase와 merge를 사용한다 (merge + rebase)

Rebase는 무엇일까요?

사전적 의미는 base를 재설정한다는 의미입니다. 

여기서 말하는 base는 branch의 base를 의미합니다. 

branch는 base 지점을 가지고 있어 base에서부터 코드를 수정합니다. 

git history를 살펴보면 branch의 base가 어디 있는 지 확인할 수 있습니다.

 

  • B 지점을 base로 가진 branch가 D, E 커밋을 진행한다.
  • C 지점으로 base를 이동하기 위해 branch에서 C 지점으로 rebase를 한다.
  • C 지점으로 rebase하게 되면 기존 D, E 커밋은 새롭게 정렬되어 C 지점 이후로 변경된다.

-> C 지점으로 base를 옮기고 기존에 있던 Commit을 재정렬하게 된다는 의미입니다. 

리베이스의 효과는 바로 Git History가 상당히 깔끔해진다는 것입니다.

 

 merge만 했을 때와 Rebase를 같이 진행했을 때 git history는 어떤 지 확인해봅시다. 

merge dev from branch1 -> merge dev from branch2

merge만 사용하여 dev 브랜치에 각자의 브랜치를 머지하게 되면 서로 엮이지 않는 branch가 각각 dev에 진행되는 모습으로 서로 엉키게 됩니다. 브랜치가 서로 엉키는 모습은 개발자의 수가 늘어날수록 심해지겠죠 

이런 branch들이 엉키는 git histroy를 깔끔하게 해주는 방법이 rebase입니다. 

 

merge와 rebase를 사용하여 dev에 반영했을 때는 

merge dev from branch 1 -> rebase dev into branch2

branch1이 merge된 dev branch를 branch2에 rebase했습니다. 

rebase를 하게 되면 git history의 commit이 재정렬됩니다. 

마치 branch1이 dev에 merge된 후 branch를 새로 딴 것처럼 commit 내용들이 정렬됩니다. 

그 다음, branch2를 dev에 merge 합니다. (merge dev from branch2)

branch2를 dev에 merge 하면 위와 같이 깔끔한 git history를 가질 수 있습니다. 

개발자가 여러명이라도 순서대로 commit 한 것과 같은 git history를 만들 수 있습니다. 

 

 

참고 자료: 

https://backlog.com/git-tutorial/kr/stepup/stepup2_8.html

 

누구나 쉽게 이해할 수 있는 Git 입문~버전 관리를 완벽하게 이용해보자~ | Backlog

누구나 쉽게 알 수 있는 Git에 입문하신 것을 환영합니다. Git을 사용해 버전 관리를 할 수 있도록 함께 공부해봅시다!

backlog.com

https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-Rebase-%ED%95%98%EA%B8%B0

 

Git - Rebase 하기

Rebase는 기존의 커밋을 그대로 사용하는 것이 아니라 내용은 같지만 다른 커밋을 새로 만든다. 새 커밋을 서버에 Push 하고 동료 중 누군가가 그 커밋을 Pull 해서 작업을 한다고 하자. 그런데 그 커

git-scm.com

https://firework-ham.tistory.com/12

 

Git Merge와 Rebase의 차이, 아름다운고 깔끔한 Git History 만들기.

Git Merge와 Rebase의 차이, 아름다운고 깔끔한 Git History 만들기. 0. 서론 그동안 git을 사용하면서 소수의 인원으로 같이 일할때는 push, pull, merge만 사용해도 큰 문제는 없었습니다. git history도 손보지

firework-ham.tistory.com

 

댓글