ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Git 교과서 03 (병합과 충돌)
    Git Github 2021. 12. 15. 20:48

    병합과 충돌

     

    브랜치를 생성하는 목적은 원본 코드에 영향을 주지 않고 개발하기 위해서이다. 개발이 끝나면 다시 원본 브랜치에 작업한 결과를 반영해야 한다. 분리된 브랜치를 한 브랜치로 합치는 작업을 병합(합치기)이라고 한다.

     

    깃의 자동병합은 원본을 기준으로 두 파일의 변경 이력을 비교한다. 변경된 파일 내용이 발견되면 자동으로 수정된 코드 내용을 병합한다.

     

    깃은 병합을 위해 두 가지 알고리즘을 제공한다. 충돌 없이 병합하기 위해 두 가지의 차이를 알아야 한다.

    • Fast-Forward 병합
    • 3-way 병합

     

     

    Fast-Forward 병합

     

    직역하면 빨리 감기라는 뜻이다. 주로 혼자 개발할 때 사용한다. 모든 변경 사항은 순차적으로 진행된다. 

    브랜치를 생성할 때 분기 기준은 master의 최종 커밋 포인터다. 

     

    깃의  merge 명령어는 브랜치를 병합한다. 현재 브랜치를 기준으로 다른 브랜치의 모든 커밋을 병합한다. 기준은 체크아웃된 현재 브랜치이다. 병합을 하려면 먼저 master 브랜치로 체크아웃해야 한다.

    $ git merge 브랜치이름

     

    Fast-Forward 병합은 작업한 브랜치를 원본 브랜치에 병합할 때 작업한 브랜치의 시작 커밋을 원본 브랜치 이후의 커밋으로 가리킨다.

    Fast-Forward 병합은 병합할 하나의 브랜치 파일을 기준 브랜치로 복사하여 수정된 파일을 원본에 그대로 적용한 것과 같다. 즉, 원본에 추가된 내용이 없다는 가정하에 변경한 파일을 대체하는 것이다.

     

     

    3-way 병합

     

    상대적으로 더 복잡한 알고리즘이다. 주로 여러 개발자와 협업으로 작업하는 경우 사용한다.

    브랜치가 순차적으로 이어지지 않고 모양이 갈라지는 형태로 나뉠 때는 Fast-Forward 방식은 사용할 수 없다. 

     

    두 브랜치를 병합하려면 먼저 분할 기준인 공통 커밋을 찾아야 한다. 이를 공통 조상 커밋이라고 한다. 

     

    공통 조상 커밋을 포함하는 브랜치와 새로운 두 브랜치, 이렇게 3개를 하나를 병합하기 때문에 3-way라고 한다.

     

    각 브랜치에 독립적으로 작업된 소스를 파일 하나로 결합한다. 병합을 완료한 후에는 새로운 커밋을 추가로 하나 생성한다. 새로 생성된 커밋을 병합 커밋이라고 한다. 병합 커밋은 부모 커밋이 2개다.

     

    3-way 병합은 병합 메시지가 필요하다. 자동으로 작성되는 메시지 외에 직접 커밋 메시지를 작성할 수도 있다. 

    $ git merge 브랜치이름 --edit
    $ git reset --hard HEAD^    //병합취소

     

     

    병합 후 삭제

     

    병합된 브랜치의 커밋은 모두 원본 브랜치에 적용된다. 중복되는 커밋을 가지는 별도의 브랜치를 유지할 필요는 없다. 불필요한 브랜치는 삭제한다.

    $ git branch -d 브랜치이름    // 병합을 완료한 브랜치만 삭제
    $ git branch -D    // 병합을 완료하지 않는 브랜치 강제 삭제

     

     

    충돌

     

    대부분의 충돌 원인은 같은 위치의 코드를 동시에 수정했기 때문이다. 두 수정 중 어떤 것이 맞는지 깃에서 알 수 없기 때문에 수동으로 제거한다.

     

    충돌이 발생되면 깃은 'Merge Conflicts' 메시지를 출력한다. 

    병합 충돌이 발생하면 자동으로 커밋이 생성되지 않고 중단한다. 이 때 수동으로 해결해야 한다. 

     

    충돌을 해결한 후 병합 커밋을 직접 만들어야 한다. 

     

     

    병합 여부 확인

     

    깃은 병합한 브랜치와 병합하지 않은 브랜치를 구분하는 옵션을 제공한다. 

    병합한 브랜치는 별표(*) 기호로 표시된다.

    $ git branch --merged
    $ git branch --no-merged    // 병합하지 않은 브랜치 확인

     

    리베이스

     

    브랜치를 합치는 방법은 두 가지다. 앞에서 작성한 병합(merge)와 리베이스(rebase)이다.

    리베이스는 커밋의 트리 구조를 재배열한다. 실무에선 merge보다 rebase를 더 선호한다.

     

    브랜치가 파생되는 커밋2를 베이스라고 한다. 병합에서는 이를 공통 조상 커밋이라고 한다. 브랜치의 베이스를 변경하는 이유는 커밋의 진행 모습을 단순화하기 위해서이다.

     

    리베이스는 코드의 베이스 분기점을 변경하여 하나의 기찻길처럼 만든다. 

     

    리베이스는 병합처럼 두 브랜치를 서로 비교하지 않고 순차적으로 커밋 병합을 시도한다. 먼저 공통 조상 커밋을 찾고 베이스 커밋을 변경하여 두 브랜치의 커밋 위치를 바꾼다. 그리고 파생된 브랜치의 diff를 임시 공간에 잠시 보관한다.  변경하는 기준 브랜치의 마지막 커밋에서 차례로 임시 공간에 저장한 diff를 하나씩 적용한다. 

    $  git rebase 브랜치

     

     

    리베이스는 병합 기준 브랜치가 merge 명령어와 반대다. merge를 사용한 병합은 현재의 기준 브랜츠이에 다른 브랜치를 읽어 와서 병합한다.

    리베이스 명령이 실행되면 파생 브랜치의 커밋들은 기준 브랜치의 마지막 커밋으로 재정렬된다.

    리베이스는 커밋 위치를 재종할 뿐 브랜치의 HEAD 포인터까지 옮기지는 않는다. 즉 리베이스된 브랜치한 후에 HEAD 포인터를 일치하기 위해 Fast-Forward 병합을 실행해야 한다. 

     

    이처럼 리베이스하게 되면 복잡한 트리 모양의 구조가 아니라 선형 구조로 브랜치를 변경할 수 있다.

     

     

     

    리베이스는 커밋을 재조정하는 것 외에도 여러 커밋을 한 커밋으로 묶을 수 있다. 이때는 -i 옵션을 사용한다.

    $ git rebase -i HEAD~3

     

     

     

    저장소를 외부에 공개했다면 공개된 순간부터 커밋은 리베이스를 사용하지 않는 것이 원칙이다. 외부로 코드를 푸시하거나 공개하기 전에 로컬에서만 실행하는 것이 좋다. 외부에 공개된 커밋을 리베이스하면 커밋 위치나 해시 값이 변경되어 혼란을 줄 수 있다. 공개된 커밋을 변경할 때는 revert를 사용한다.

     

     

    'Git Github' 카테고리의 다른 글

    Git 교과서 05 (배포와 태그)  (0) 2021.12.16
    Git 교과서 04 (복귀)  (0) 2021.12.15
    Git 교과서 02 (임시처리)  (0) 2021.12.15
    Git 교과서 01  (0) 2021.12.13
    Git - branch & conflict  (0) 2021.03.08
Designed by Tistory.