Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

git의 merge와 rebase 비교하기

개인 프로젝트 형상관리용도로 DVCS인 Git를 주로 쓰는데 보통 혼자서 쓰기 때문에 충돌같은 이슈를 겪을일은 별로 없습니다. 이젠 좀 익숙해 졌기 때문에 브랜치나 태그기능도 쓰고 있는데 기존의 Subversion같은 중앙저장소 방식의 VCS와 다르게 DVCS에서는 브랜치를 자주 사용하는데 연습삼아 사용하다보니 브랜치를 나눠서 작업한 후 다시 합치려니까 merge와 rebase의 동작이 좀 헷갈렸습니다.

  • git-merge : Join two or more development histories together.
  • git-rebase : Forward-port local commits to the updated upstream head.

Git 매뉴얼을 보면 위와같이 정의가 되어 있고 Git, 분산 버전 관리 시스템을 보면 git merge는 바로 합치기로 git rebase는 브랜치 이력 재정렬하기로 되어 있는데 결과적으로는 둘다 합쳐지는 건데 명확한 차이점이 잘 구분되지 않았습니다. 이런 저런 문서나 설명을 봐도 명확히 잘 이해가 되지 않아서 간단히 테스트를 해보았습니다.




일단 파일을 2개 준비했습니다. one.txt와 two.txt 2개의 파일을 만들고 각 라인마다 한글자씩 one.txt는 1~10까지 쓰고 two.txt에는 a~j까지 작성했습니다. master 브랜치에서 여기까지 커밋을 하고 slave 브랜치를 만들어서 두 파일에서 각각 2번에 걸쳐서 한라인씩 삭제를 하고 다시 master브랜치에서 파일마다 한라인씩 삭제했습니다.

테스트를 위해 준비한 master 브랜치

테스트를 위해 준비한 slave 브랜치

그 결과는 위와 같습니다.(시간대가 정확히 안나오는데 master의 위의 2개의 커밋은 시간순으로는 slave 커밋 이후에 처리된 커밋입니다.) Author에 Insider로 된 커밋들은 slave에서 커밋된 내용들이고 일부러 커미터를 다르게 하였습니다.




터미널에서 git merge slave 실행

이제 master 브랜치에서 slave브랜치를 머지합니다. 변경내역이 합쳐진 것을 볼 수 있습니다.

slave를 merge한 master 브랜치의 히스토리

커밋히스토리를 master에서 보면 위와 같습니다.  시간대상으로는 master상의 최근 커밋 2개가 slave 쪽 커밋들보다 늦은 커밋이지만 트리로그는 새로 머지한 것들이 최근 커밋들로 합쳐지고 마지막에 merge brance 커밋이 하나더 추가되었고 트리그래프가 브랜치되고 머지된 내역을 표시해 주고 있습니다.




git rebase slave를 실행한 화면

이번에는 rebase입니다. 마찬가지로 master 브랜치에서 slave 브랜치를 rebase했습니다. merge때와는 로그가 다르게 나타납니다.

사용자 삽입 이미지

위와같이 시간순서대로 정렬된 커밋내역이 나타나고 트리그래프도 브랜치에 상관없이 일렬로 나오는 것을 볼 수 있습니다.

사실 이 결과는 제가 기대했던 rebase의 결과와는 약간 다릅니다. rebase라는 명령어는 사실상 브랜치의 기준이 되는 base를 다시 잡아주는 것인데 저는 master를 최신커밋 위에 slave 커밋들이 재정렬되기를 기대했는데 그렇게 되지 않았습니다. 이는 사실 rebase의 대상이 반대로 되었기 때문입니다. master에서 빠져나온 slave 브랜치를 다시 master의 HEAD기준으로 rebase하려면 slave 브랜치에서 master를 rebase해주어야 하는데 저는 반대로 했기 때문에 위와 같은 결과가 나온 것입니다.






추가테스트를 위한 기본 master 브랜치 히스토리

그래서 다시 테스트를 했습니다.  앞의 테스트와 동일한 환경에서 master에 3개의 커밋을 더 추가했습니다.(just now로 표시된) slave의 커밋한 내용과 동일한 수정내용을 가지는 2개의 커밋을 더 하고 새로운 수정내용을 하나더 커밋했습니다.




slave에서 git merge master를 실행한 화면

이번에는 slave에서 master를 머지했습니다.

merge된 slave 히스토리

slave기준으로 보면 slave의 커밋내역이후에 master의 커밋내역이 머지가 되고 머지커밋도 추가로 생기는 것을 볼 수 있습니다.




slave에서 git rebase master를 실행한 화면

이번엔 slave에서 master를 rebase했습니다. 로그를 보면 2개의 커밋에 대해서는 No changes -- Patch already applied라고 나오는 것을 볼 수 있습니다.

rebase된 slave의 히스토리 화면

이번엔 원하는대로 rebase가 되었습니다. master의 HEAD를 기준으로 slave쪽에 있던 4개의 커밋이 재적용되었고 동일한 변경내역인 2개의 커밋은 이미 master에 존재하기 때문에 사라져버려서 결과적으로 히스토리에는 2개의 slave커밋만 적용되었습니다. 





위의 명령에 대한 실행결과를 보면 rebase의 동작방식이 꽤 명확한 하게 이해할 수 있는데 master에서 중간에 브랜치되어서 4건의 커밋을 갖게된 slave를 현재의 master를 기준으로 rebase하니 master의 HEAD를 기준으로 4건의 커밋을 재적용하였고 master의 커밋과 겹치는 커밋들은 적용안되는 것을 볼 수 있습니다.

Git 매뉴얼의 rebase설명부분에 설명이 잘 되어 있습니다.(내부 동작에 대한 설명이 많은데 트리 그림있는 부분만 보셔도 될듯 합니다.) 그럼 merge랑 rebase를 어떻게 구별해서 사용해야 하는가라는 의문이 남습니다. A Rebase Workflow for Git를 보면 히스토리 관리를 별로 신경쓰지 않고 혼자서만 커밋하거나 아니면 믿을수 있는 소수만 커밋하지 않는다면 merge대신 rebase를 사용하라고 권장하고 있습니다.

예를 들어 A라는 공개 프로젝트를 clone받아서 제가 수정을 하게 된다면 master에서 직접 수정하지 않고 브랜치를 분리해서 작업을 하고나서 커밋하기 전에 rebase를 하고 푸시를 하는 방식을 취할 수 있습니다. 이렇게 할 경우 같은 내역을 다른 커미터가 먼저 커밋했을때 해당 커밋을 유지시키고 자신이 수정한 커밋은 생략되어서 히스토리를 깔끔하게 유지할 수 있고 필요가 없는 merge 커밋같은 것이 남지 않습니다.
2011/07/12 03:29 2011/07/12 03:29