본문 바로가기

프로그래밍 독학/Git

Pro git 독학일지 8장 - Git 브랜치(4)

반응형

Pro git 독학일지 8장 - Git 브랜치(4)

 

Pull 하기

 

git fetch 명령을 실행하면 서버에는 존재하지만, 로컬에는 아직 없는 데이터를 받아와서 저장한다.

이 때 워킹 디렉토리의 파일 내용은 변경되지 않고 그대로 남는다.

서버로부터 데이터를 가져와서 저장해두고 사용자가 Merge하도록 준비만 해둔다.

간단히 말하면 git pull 명령은 대부분 git fetch 명령을 실행하고 나서 자동으로 git merge 명령을 수행하는 것 뿐이다.

바로 앞 절에서 살펴본 대로 clone이나 checkout 명령을 실행하여 추적 브랜치가 설정되면 git pull 명령은 서버로부터 데이터를 가져와서 현재 로컬 브랜치와 서버의 추적 브랜치를 Merge한다.

일반적으로 fetch와 merge 명령을 명시적으로 사용하는 것이 pull 명령으로 한 번에 두 작업을 하는 것보다 낫다.

 

 

리모트 브랜치 삭제

 

동료와 협업하기 위해 리모트 브랜치를 만들었다가 작업을 마치고 master 브랜치로 Merge했다.

협업하는 데 사용했던 그 리모트 브랜치는 이제 더 이상 필요하지 않기에 삭제할 수 있다.

 

git push 명령에 --delete 옵션을 사용하여 리모트 브랜치를 삭제할 수 있다.

serverfix라는 리모트 브랜치를 삭제하려면 아래와 같이 실행한다.

 

 

위 명령을 실행하면 서버에서 브랜치(즉, 커밋을 가리키는 포인터) 하나가 사라진다.

서버에서 가비지 컬렉터가 동작하지 않는 한 데이터는 사라지지 않기 때문에

종종 의도치 않게 삭제한 경우에도 커밋한 데이터를 살릴 수 있다.

 

 

Rebase 하기

 

Git에서 한 브랜치에서 다른 브랜치로 합치는 방법으로는 두 가지가 있다.

하나는 Merge이고 다른 하나는 Rebase이다.

 

이 절에서는

1. Rebase가 무엇인지,

2. 어떻게 사용하는지,

3. 좋은 점은 뭐고,

4. 어떤 상황에서 사용하지 말아야 하는지

알아본다.

 

Rebase의 기초

 

앞의 Merge의 기초 절에서 살펴본 예제로 다시 돌아가보자.

두 개의 나누어진 브랜치의 모습을 볼 수 있다.

 

 

이 두 브랜치를 합치는 가장 쉬운 방법은 앞에서 살펴본 대로 merge 명령을 사용하는 것이다.

두 브랜치의 마지막 커밋 두 개(C3, C4)와

공통 조상(C2)을 사용하는 3-way Merge로 새로운 커밋을 만들어낸다.

 

 

비슷한 결과를 만드는 다른 방식으로, C3에서 변경된 사항은 Patch로 만들고 이를 다시 C4에 적용시키는 방법이 있다.

Git에서는 이런 방식을 Rebase라고 한다.

rebase 명령으로 한 브랜치에서 변경된 사항을 다른 브랜치에 적용할 수 있다.

 

위의 예제는 아래와 같은 명령으로 Rebase한다.

 

 

실제로 일어나는 일을 설명하자면

일단 두 브랜치가 나뉘기 전인 공통 커밋으로 이동하고 나서

그 커밋부터 지금 Checkout한 브랜치가 가리키는 커밋까지 diff를 차례로 만들어

어딘가에 임시로 저장해놓는다.

Rebase할 브랜치(experiment)가 합칠 브랜치(master)가 가리키는 커밋을 가리키게 하고

아까 저장해놓았던 변경사항을 차례대로 적용한다.

 

 

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

 

 

 

C4'로 표시된 커밋에서의 내용은 Merge 예제에서 살펴본 C5 커밋에서의 내용과 같을 것이다.

Merge이든 Rebase이든 둘 다 합치는 관점에서는 서로 다를 게 없다.

하지만, Rebase가 좀 더 깨끗한 히스토리를 만든다.

Rebase한 브랜치의 Log를 살펴보면 히스토리가 선형이다.

일을 병렬로 동시에 진행해도 Rebase하고 나면 모든 작업이 차례대로 수행된 것처럼 보인다.

 

Rebase는 보통 리모트 브랜치에 커밋을 깔끔하게 적용하고 싶을 때 사용한다.

아마 이렇게 Rebase 하는 리모트 브랜치는 직접 관리하는 것이 아니라 그냥 참여하는 브랜치일 것이다.

메인 프로젝트에 Patch를 보낼 준비가 되면 하는 것이 Rebase니까

브랜치에서 하던 일을 완전히 마치고 origin/master 로 Rebase 한다.

이렇게 Rebase 하고 나면 프로젝트 관리자는 어떠한 통합 작업도 필요 없다.

그냥 master 브랜치를 fast-forward시키면 된다.

 

Rebase를 하든지, Merge를 하든지, 최종 결과물은 같고 커밋 히스토리만 다르다는 것이 중요하다.

Rebase의 경우는 브랜치의 변경사항을 순서대로 다른 브랜치에 적용하면서 합치고

Merge의 경우는 두 브랜치의 최종 결과만을 가지고 합친다.

 

 

Rebase 활용

 

Rebase는 단순히 브랜치를 합치는 것만 아니라 다른 용도로도 사용할 수 있다.

다른 토픽 브랜치에서 갈라져나온 토픽 브랜치 같은 히스토리가 있다고 하자.

server 브랜치를 만들어서 서버 기능을 추가하고 그 브랜치에서 다시 client 브랜치를 만들어 클라이언트 기능을 추가한다.

마지막으로 server 브랜치로 돌아가서 몇 가지 기능을 더 추가한다.

 

 

이 때 테스트가 덜 된 server 브랜치는 그대로 두고 client 브랜치만 master로 합치려는 상황을 생각해보자.

server와는 아무 관련이 없는 client 커밋은 C8, C9이다. 

이 두 커밋을 master 브랜치에 적용하기 위해서 --onto 옵션을 사용하여 아래와 같은 명령을 실행한다:

 

$ git rebase --onto master server client

 

이 명령은 master 브랜치부터 server 브랜치의 공통 조상까지의 커밋을 client 브랜치에서 없애고 싶을 때 사용한다.

client 브랜치에서만 변경된 패치를 만들어 master 브랜치에서 client 브랜치를 기반으로 새로 만들어 적용한다.

 

 

이제 master 브랜치로 돌아가서 Fast-forward시킬 수 있다.

 

 

 

 

아래 명령은 토픽 브랜치를 Checkout 하고 베이스 브랜치에 Rebase 한다.

 

git rebase [basebranch] [topicbranch]

 

server 브랜치의 일이 다 끝나면

아래의 명령으로 Checkout하지 않고 바로 Server 브랜치를 master 브랜치로 Rebase 할 수 있다.

 

$ git rebase master server

 

server 브랜치의 수정사항을 master 브랜치에 적용했다.

 

 

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

 

모든 것이 master 브랜치에 통합됐기 때문에 더 필요하지 않다면 client나 server 브랜치는 삭제해도 된다.

브랜치를 삭제해도 커밋 히스토리는 최종 커밋 히스토리 같이 여전히 남아있다.

 

 

 

Rebase의 위험성

 

Rebase가 장점이 많은 기능이지만 단점이 없는 것은 아니니 조심해야 한다.

 

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

 

이 지침만 지키면 Rebase를 하는데 문제될 게 없다.

하지만 이 주의사항을 지키지 않으면 사람들에게 욕을 먹을 것이다.

 

Rebase는 기존의 커밋을 그대로 사용하는 것이 아니라 내용은 같지만 다른 커밋을 새로 만든다.

새 커밋을 서버에 Push하고 동료가 다시 Push했을 때 동료는 다시 Merge해야 한다.

그리고 동료가 다시 Merge한 내용을 Pull하면

내 코드는 정말 엉망이 된다.

 

이미 공개 저장소에 Push한 커밋을 Rebase하면 어떤 결과가 초래되는지는 다음에 살펴보겠다.

 

 

Rebase vs. Merge

 

둘 중 무엇을 쓰는게 좋지?

 

이 질문에 대한 답을 찾기 전에 히스토리의 의미에 대해서 잠깐 다시 생각해보자.

 

히스토리를 보는 관점 중에 하나는 작업한 내용의 기록으로 보는 것이 있다.

작업 내용을 기록한 문서이고, 각 기록은 각각 의미를 가지며, 변경할 수 없다.

이런 관점에서 커밋 히스토리를 변경한다는 것은 역사를 부정하는 꼴이 된다.

언제 무슨 일이 있었는지 기록에 대해 거짓말을 하게 되는 것이다.

이렇게 했을 때 지저분하게 수많은 Merge 커밋이 히스토리에 남게 되면 문제가 없을까?

역사는 후세를 위해 기록하고 보존해야 한다.

 

히스토리를 프로젝트가 어떻게 진행되었나에 대한 이야기로도 볼 수 있다.

소프트웨어를 주의깊게 편집하는 방법에 매뉴얼이나 세세한 작업내용을 초벌부터 공개하고 싶지 않을 수 있다.

나중에 다른 사람에게 들려주기 좋도록 Rebase나 filter-branch같은 도구로 프로젝트의 진행 이야기를 다듬으면 좋다.

 

Merge나 Rebase 중 무엇이 나으냐는 질문은 다시 생각해봐도 답이 그리 간단치 않다.

Git은 매우 강력한 도구고 기능이 많아서 히스토리를 잘 쌓을 수 있지만,

모든 팀과 모든 이가 처한 상황은 각기 다르다.

예제를 통해 Merge나 Rebase가 무엇이고 어떤 의미인지 배웠다.

이 둘을 어떻게 쓸지는 각자의 상황과 각자의 판단에 달렸다.

 

일반적인 해답을 굳이 내리자면 로컬 브랜치에서 작업할 때는 히스토리를 정리하기 위해서 Rebase할 수도 있지만,

리모트 등 어딘가에 Push로 내보낸 커밋에 대해서는 절대 Rebase하지 말아야 한다.

 

 

반응형