Git

Rebase

95_뽀로로 2021. 10. 6. 01:47

한 브랜치에 다른 브랜치의 내용을 적용시키려면 흔히들 쓰는, 그리고 내가 지금까지 쓰던 명령어는 Merge 이다.

 

Master 브랜치에 내 작업내용 PR 을 적용시키려면, Merge 명령어를 썼다.

 

그리고 Master 의 변경내용을 내 작업브랜치에 적용시킬때는 pull ( 원격 저장소의 내용을 가져오는 Fetch + Merge) 를 썼다.

 

 

오늘은 코드를 깔끔하게 병합할 때 쓰는 Rebase 라는 명령을 알아보도록 한다.

 

 

 

 


 

 

내가 깃을 이용한 협업과 gitflow라는 개념을 처음 알게 되었던 글은 우아한 형제들의 블로그였는데, 

https://techblog.woowahan.com/2553/

 

우린 Git-flow를 사용하고 있어요 | 우아한형제들 기술블로그

{{item.name}} 안녕하세요. 우아한형제들 배민프론트개발팀에서 안드로이드 앱 개발을 하고 있는 나동호입니다. 오늘은 저희 안드로이드 파트에서 사용하고 있는 Git 브랜치 전략을 소개하려고 합

techblog.woowahan.com

 

당시에는 그 글에서의 Rebase 가 어려워서 넘어갔던 기억이 든다.

 

Rebase, Squash 등을 이용해서 깃의 로그를 깔끔하게 하는 방법을 알기 위해 Rebase 라는 것에 먼저 공부를 해본다.

 

 

 


 

Rebase

git 에는 HEAD 라는 포인터가 있다.

이 포인터는 현재 작업중인 커밋을 가리킨다.

다르게 말하면, 내가 작업할 커밋에 HEAD 를 놓게 되는 것이다.

우리는 어떠한 작업을 할때, 항상 HEAD 가 가르키는 커밋에서 작업을 이어간다.

 

rebase 명령은 이 HEAD 를 re - base 재 배치하는 명령이다.

 

이를 기억하면서 Rebase 로 병합하는 과정을 거쳐보자.

그리고 Rebase 를 하지 않고, 그냥 Merge 도 해보면서 차이를 보자.

 

 

 

1. 일반적인 Merge

c 는 commit 의 약자. 숫자는 커밋한 시간적 순서에 따라 지정.

Master 브랜치에서 C1이 커밋된 시점에 내가 Branch1 이라는 이름으로 브랜치를 따서 C3 이라는 작업을 커밋했다.

 

그리고 내가 작업하는 사이 C2 의 커밋이 마스터에 적용되었다. (Conflict 는 없다 치자.)

 

이후 내 브랜치도 Master 에 적용시키고 싶을때 아래의 명령어를 사용한다.

 

// 현재 내 작업 브랜치에서 작업하고 있을테니 checkout
$ git checkout master
$ git merge Branch1

 

git GUI 의 로그처럼 그렸다. 한칸에 하나씩 시간적인 순서로 로그를 찍으니까.

 

브랜치에서 작업한 C3 의 내용을 마스터가 이제 보고 있게 되었고, 변경 사항이 잘 적용 되었다.

 

 

하지만

  • 지금은 브랜치가 한개지만, 브랜치가 여러개라면?
  • 지금은 브랜치에서 작업한 내용이 한개지만, 수십 커밋한 브랜치를 병합하면?

길~고 복잡한 깃 로그를 가지게 될 것이다.

 

이에 도움을 줄 수 있는 방법이 Rebase 이다.

 

 

 

 

2. Rebase

다시 아까의 처음 상태로 돌아가보자.

c 는 commit 의 약자. 숫자는 커밋한 시간적 순서에 따라 지정.

 

이번에는 작업 브랜치가 master 로 옮기지 말고 branch1 인 상태에서 아래 명령어를 실행한다.

 

$ git rebase master

결과를 그림으로 먼저 보자면 아래와 같다.

 

이 명령어를 친걸 직접적으로 해석해보면 다음과 같을 것이다.

"master 브랜치를 branch1 브랜치에 rebase 해라!"

 

내부적으로는 아래와 같은 일이 일어난다.

  1. 두 브랜치가 나뉘기 전 공통 브랜치 C1 로 이동.
  2. 공통 브랜치 C1 에서 현재 브랜치의 C3 까지의 Diff 를 임시 저장.
  3. Branch1를 master 가 있는 곳을 가리키게 이동.
  4. 아까 임시 저장한 C1-C3 의 Diff 커밋들을 적용.

곰곰히 생각해보면 위의 그림이 결과로 나온다.

 

 

이후 master 브랜치로 이동해서 master 를 merge 한다.

$ git checkout master
$ git merge branch1

Branch1 을 Master 브랜치로 병합할때, Branch1 의 이력은 rebase 로 인해 Master 의 이력을 그대로 가지고 있다.
따라서, Master 브랜치로 병합할때, Master 가 가리키는 방향만 그대로 옮기면 된다.

이런 병합을 Fast-Forward 병합 이라고 한다.

 

작업내용을 병합한 결과가 위의 Merge 만 사용한 것과는 다르게 한줄로 간단하다.

또 merge 커밋 하나도 줄었다.

 

이런 식으로 간단한 깃 로그를 만들 수 있다.

 

 

 

 

 

 

3. Rebase 시 충돌 - continue

c 는 commit 의 약자. 숫자는 커밋한 시간적 순서에 따라 지정.

이 상황에서 C2, C3 가 같은 부분을 수정하여 Conflict 가 발생할 수 있다.

 

 

그때는, merge 시 충돌처럼 충돌부분을 적당히 수정하고, 수정부분을 스테이징하는 것 까진 똑같지만

 

commit 으로 적용하는 것이 아니다.

 

$ git add .
$ git rebase --continue

 

 

 

 

 

4. Rebase Checkout 하지 않고 간단히 쓰기.

c 는 commit 의 약자. 숫자는 커밋한 시간적 순서에 따라 지정.

우리는 이 상황에서 아래와 같이 rebase 했다.

  1. branch1 로 Checkout 
  2. master rebase
  3. master 로 Checkout
  4. branch1 merge
$ git checkout branch1
$ git rebase master
$ git checkout master
$ git merge branch1

 

 

굳이 checkout 을 하지 않고 한줄로도 가능하다.

$ git rebase <basebranch> <topicbranch>

토픽(server) 브랜치를 Checkout 하고 베이스(master) 브랜치에 Rebase 한다.

쉽게 말해서, 로그상으로 생각해봤을때, 적혀진 순서대로
<basebranch>-<topicbranch> 가 된다.

 

 

따라서, master 브랜치에서

$ git rebase branch1 master

를 하게 되면,

가 된다. 대신, 시간 순서대로 커밋이 정렬되지 못한다.

 

 

반대로, branch1 브랜치에서

$ git branch master branch1

을 하게 되면,

가 된다. master 브랜치로 checkout 후 fast-forward merge 를 해야 할 것이다.

 

 

 

 

5. Rebase 시 주의점.

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

Rebase 명령은 사실, 변경사항은 같지만 다른 커밋을 만든다.

 

변경사항을 복사해서 새로운 커밋을 만든다라고도 할 수 있겠다.

 

따라서, 공개 저장소에 이미 올라간 커밋을 가져와서 Rebase 하면, 새로운 커밋들로 이루어진 로그를 만들게 된다.

 

이를 다시 공개저장소에 push 하려면, force 명령어를 써서 하게 될 텐데, 이렇게 올려버리면 문제가 생기는 것이다.

 

왜냐면, 이미 공개 저장소에 올라간 커밋들을 팀원들은 쓰고 있을텐데, 새로운 커밋으로 구성된 브랜치를 내가 강제로 올려버리면 다른 팀원들은 merge 할때 문제가 생기기 때문이다.