[Git] Merge

앞서, master 브랜치에서 새로운 브랜치를 생성해 보았다. 개별적인 문제를 해결하고자 생성한 브랜치는 결국 기존의 브랜치와 합쳐야 하는데, 이 때 사용하는 것이 merge이다.

예제 진행 과정

Untitled

아래의 과정을 수행하며 자세한 내용을 알아보자.

  1. 웹사이트가 있고 뭔가 작업을 진행하고 있다.
  2. 새로운 이슈를 처리할 새 Branch를 하나 생성한다.
  3. 새로 만든 Branch에서 작업을 진행한다.

이때 중요한 문제가 생겨서 그것을 해결하는 Hotfix를 먼저 만들어야 한다. 그러면 아래와 같이 할 수 있다.

  1. 새로운 이슈를 처리하기 이전의 운영(Production) 브랜치로 이동한다.
  2. Hotfix 브랜치를 새로 하나 생성한다.
  3. 수정한 Hotfix 테스트를 마치고 운영 브랜치로 Merge 한다.
  4. 다시 작업하던 브랜치로 옮겨가서 하던 일 진행한다.

새로운 브랜치에서 작업

  1. 현재 상황

    Untitled

    아직까지는 새로운 브랜치 없이 master 브랜치만을 사용한다.

  2. 이슈 발생 - 브랜치 생성

    Untitled

    이 때, 해결이 필요한 이슈가 등장한다. 새로운 브랜치를 생성하여 문제를 해결해보자.

     $ git checkout -b iss53
     #위의 명령은 아래의 명령 두 개를 합친 것과 같다.
     $ git branch iss53
     $ git checkout iss53
    

    git checkout-b 옵션은 branch, checkout을 동시에 진행한다. 브랜치를 생성하고 HEAD를 해당 브랜치로 이동하는 것이다.

  3. 작업 진행

    Untitled

    새로운 브랜치에서 문제를 해결하며 새로 커밋했다. master 브랜치와 iss53브랜치가 가리키는 커밋이 다른 모습이다.


master에서 갈라진 두 개의 브랜치

  1. 기존의 브랜치로 이동

    이 상황에서 급히 해결해야할 문제가 발생한다. iss53과 코드가 섞이는 것을 방지하기 위해, master에서 새로운 브랜치를 생성해야 한다.

    브랜치를 이동하려면 아직 커밋하지 않은 파일이 체크아웃할 브랜치와 충돌하는지 확인해야한다. 만약 충돌한다면 체크아웃을 진행할 수 없다.(stashing, cleaning에서 설명)

    충돌하지 않게끔 커밋을 진행하고 $ git checkout master를 통해 마스터 브랜치로 이동한다.

  2. 새로운 브랜치 생성

    Untitled

    $ git checkout -b hotfix명령어를 통해 브랜치의 생성, HEAD의 이동을 한다. 위의 이미지는 hotfix브랜치에서 한 차례 커밋을 진행해, master 브랜치와 분리된 모습이다.

  3. Merge 진행

    Untitled

    hotfix가 끝났으면 master브랜치와 hotfix 브랜치를 합쳐야한다. 합체의 중심이 되는 브랜치인 master로 이동 후, merge를 진행한다.

     $ git checkout master
     $ git merge hotfix
     Updating ***
     Fast-forward
      <파일명> | <수정>
      * file changed, * insertions(+)
    

    Fast-forward는 merge하려는 브랜치의 커밋(C4)이 현재 커밋(C2)에 기반했기 때문에 나타난다. 예를 들어 A 브랜치에서 다른 B 브랜치를 Merge 할 때 B 브랜치가 A 브랜치 이후의 커밋을 가리키고 있으면 그저 A 브랜치가 B 브랜치와 동일한 커밋을 가리키도록 이동시킬 뿐이다. hotfix 브랜치가 가리키는 곳을 master 브랜치 또한 가리키게 된다.

    필요 없어진 hotfix 브랜치는 $ git branch -d hotfix명령으로 삭제하자. -d옵션은 삭제를 의미한다.

  4. 기존 작업의 진행 및 merge

    Untitled

    이제 남은 브랜치는 master와 iss53이다. iss53 브랜치로 이동 후, 문제를 해결하고 커밋(C5)한 상황에 도달했다. 이 브랜치는 어떻게 병합할까?

     $ git checkout master
     Switched to branch 'master'
     $ git merge iss53
     Merge made by the 'recursive' strategy.
     <파일이름> |  *+
     * file changed, * insertion(+)
    

    hotfix를 merge 했을 때 처럼, master 브랜치로 체크아웃 후 merge를 시도했다. hotfix와는 다르게 Fast-forward 표시는 뜨지 않는데, 현재 iss53브랜치가 가리키는 커밋이 merge할 브랜치의 조상이 아니기 때문이다. 이런 경우에는 새로운 커밋을 만들어 두 브랜치를 병합한다(3-way Merge).

    Untitled

    C6는 merge 커밋이다. 각 브랜치가 가리키는 커밋 두 개와(C4, C5) 공통 조상 하나(C2)를 사용하여 병합한 것이다. master 브랜치는 merge 커밋을 가리키며, iss53 브랜치는 이전 위치에 머무른다. 필요가 없으니 $ git branch -d iss53명령어로 삭제하자.


Merge시의 충돌

가끔씩 3-way Merge가 실패할 수 있다. 서로 다른 브랜치에서 같은 파일의 한 부분을 동시에 수정한 경우가 그렇다. 아래와 같은 메세지로 확인할 수 있다.

CONFLICT (content): Merge conflict in <파일이름>
Automatic merge failed; fix conflicts and then commit the result.

혹은 git status 명령어로 문제가 되는 파일을 알 수 있다. unmerged 상태로 표시되는데, 충돌이 난 부분 또한 표시된다.

<<<<<<< HEAD:<파일이름>
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53:<파일이름>

위쪽이 HEAD 버전이고, 아래쪽이 병합되는 브랜치이다. 둘 중 하나를 고르거나 아예 새로 작성하여 충돌을 해결할 수 있다. 수정 이후에는 <<<<<<<=======>>>>>>>가 포함된 행을 삭제하고 git add로 다시 git에 저장하면 된다. 이후 git status로 충돌이 해결되었는지 확인하고 git commit 으로 merge한 내용을 커밋하면 병합이 완료된다.

참고자료