본문 바로가기
컴퓨터 & IT (Computer & IT)/Github

[GitHub] 병합과 충돌

by Physics 2021. 4. 24.
728x90

아래는 "git 교과서" 내용을 정리한 글입니다.


1. 병합이란?  

  분리된 두 브랜치를 하나로 합치는 작업
 ▪ Git에서 브랜치를 합치는 방법은 크게 두 가지로 나뉜다: 병합(Merge)와 리베이스(Rebase) 

 1) 깃의 자동 병합 

    (1) 원본 파일을 기준으로 두 파일의 변경 이력을 비교함 
    (2) 변경된 파일 내용이 발견되면 자동으로 수정된 코드 내용을 병합 
    (3) 깃의 병합은 브랜치를 기준으로 함
        - 분리된 각각의 브랜치에서 수정된 사항을 하나의 브랜치로 병합 
    (4) 깃이 자동으로 병합을 한다고 하더라도 모든 코드 병합을 완벽하게 처리할 수 없음
        - 깃이 병합을 할 수 없는 상황을 "충돌"이라 함. 

※ 깃의 자동 병합 방식: Fast-Forward 방식, 3-way 방식 

 2) 브랜치 병합 여부 확인

   (1) git의 브랜치 병합 여부 확인: $ git branch --merged 
      ▪ 병합한 브랜치는 *기호로 표시됨
      ▪ 병합을 완료한 브랜치는 -d 옵션을 사용해서 삭제 
   (2) 병합하지 않은 브랜치 확인: $ git branch --no-merged 
      ▪ 병합하지 않은 브랜치를 삭제하는 경우에는 -D 옵션을 사용해서 브랜치 삭제 


2. Fast-Forward 방식 

▪ 일반적으로 Fast-Forward 방식은 혼자 개발할 때 많이 사용됨
▪ Fast-Forward 방식은 순차적 커밋에 맞추어 병합을 함  
    - 혼자 개발하는 과정에서 브랜치는 생성된 커밋에 순차적으로 분기되는 경항이 있음
    - 혼자 개발할 때는 코드의 수정 이력도 순차적으로 됨
    - 혼자 개발할 때는 브랜치 생성 등 대부분의 과정이 순차적으로 진행됨 
▪ $git merge <브랜치 이름> 
  (1) <브랜치 이름>을 master 브랜치와 병합함
  (2) merge 명령어는 현재 브랜치를 기준으로 다른 브랜치의 모든 커밋을 병합함  

※ Fast-Forward 방식은 작업한 브랜치를 원본 브랜치에 병합할 때, 작업한 브랜치의 시작 커밋을 원본 브랜치 이후의 커밋으로 가리킴
※ 커밋의 위치를 최신으로 옮기는 것과 비슷 


3. 3-way 병합

(1) 위와 같은 형태일 경우, Fast-Forward 방식을 적용할 수 없고, 3-way 방식을 사용함. 
(2) 두 브랜치를 병합하기 위해선, 
   ▪ 분할 기준인 공통 커밋(공통 조상 커밋)을 찾은 후, 이를 포함하는 브랜치(노랑)와 새로운 두 브랜치(초록 및 빨강)를 하나로 병합
   ▪ git에서는 두 브랜치의 공통 조상 커밋을 자동으로 찾아주며, 공통 조상 커밋을 기준으로 브랜치를 병합
   ▪ 병합이 성공적으로 완료하게 되면, 새로운 커밋(병합커밋)을 추가함
      - 병합 커밋은 부모 커밋이 2개라는 특징이 존재 
(3) 3-way 방식은 Fast-Forward 방식과 다르게 병합 커밋 메시지가 필요함 
   ▪ 다음과 같은 커밋 메시지가 병합 후 커밋에 자동 삽입됨: "Merge branch <branch_name>"
   ▪ 만일 병합 시 해당 커밋 메시지를 직접 작성할 경우: $ git merge <branch_name> --edit 


4. 브랜치 삭제 

▪ 병합을 완료한 브랜치의 경우, 삭제를 하거나 지속적인 통합 및 개발을 해야하는 브랜치의 경우 남겨둔다. 
▪ 병합 후 불필요한 브랜치 삭제: $ git branch -d <브랜치 이름>
  - "-d" 옵션은 병합을 완료한 브랜치만 삭제할 수 있음 


5. 충돌 

1) 충돌이 발생하는 상황

3-way 병합을 하는 경우, 다른 브랜치에서 서로 같은 위치의 코드를 동시에 수정을 하게 될 경우, 충돌이 발생하게 됨. 
  - Git에서 코드들의 서로 다른 위치를 수정할 경우 충돌 메시지가 발생되지 않음 
  - 일반적으로 충돌이 발생되는 원인은 코드의 동일한 부분을 서로 다르게 수정을 했기 때문에 발생됨 
  - 충돌이 발생될 경우, 깃은 "Merge Conflict"란 메시지를 출력하며 병합을 중단함 
병합 충돌이 발생하게 되면?
  - 직접 수동으로 소스코드를 보고 충돌된 부분을 확인 후 수정해야 하며, 충돌을 해결 후, 병합 커밋을 직접 만들어야 함 

Conflict

 


6. 리베이스

주의: 리베이스는 히스토리를 강제로 조작하는 것이기 때문에 다른 사람이 만약 해당 히스토리를 본다면 완전히 꼬이게 된다. 따라서 리베이스는 혼자서만 쓰는 브랜치에서 수행을 해야 한다. 이처럼 이력을 조작하고 푸시를 하는 것은 다른 개발자들에게 위험한 행위이므로 무조건 혼자 사용하는 브랜치에서만 해야한다. 

1) 리베이스 vs 병합

종류 설명
병합  병합: 파생된 두 브랜치를 하나로 합치는 과정 
 병합하기 위해선? 
  (1) 두 브랜치의 공통 조상 커밋을 먼저 찾아야 함 
  (2) 공통 조상 커밋을 찾은 후, 두 브랜치를 3-way 방식으로 병합 
  (3) 최종적으로 병합 커밋을 생성함 
리베이스 두 브랜치를 서로 비교하지 않고 순차적으로 커밋 병합을 시도함 
  - 따라서 병합 커밋이 존재하지 않음  

 

2) 리베이스 명령어

$ git rebase <명령어> 
  - merge 명령어는 현재의 기준 브랜치 (master)에서 다른 브랜치를 읽어와서 결합함 
  - 따라서, merge 명령어를 사용할 때에는 기준 브랜치로 체크아웃을 해야 함 
  - 반면에, rebase의 경우, 병합되는 브랜치의 방향이 반대 
     -- 즉, 기준 브랜치로 체크아웃을 하는 것이 아닌 파생 브랜치로 체크아웃을 한 후, 리베이스를 해야 함. 

※ 리베이스를 하면서 병합과 마찬가지로 충돌 문제가 발생할 수 있다. 이때에는 사용자가 직접 수동으로 충돌 문제를 해결해야 한다. 
- 충돌을 수정한 후에는 $git rebase --continue을 사용하여, 충돌 이후의 병합을 계속 진행한다. 

7. 풀 리퀘스트 (Pull Request)와 포크 

1) 풀 리퀘스트란?
여러 사람들과 같이 작업을 하는 과정에서 특정 브랜치를 병합을 할 때, 다른 사람들의 동의가 필요하거나 확인이 필요한 경우가 있다. 풀 리퀘스트란 협력자에게 병합을 요청하는 메시지를 보내는 것이다. 

2) 포크와 풀 리퀘스트
   ▪ Github의 원본 저장소 관리의 어려움
      (1) GitHub의 원본 저장소의 소유자 입장에서는 협력자가 늘어날수록 원본 저장소를 관리하기 어려워짐 

         - 협력자가 원본 저장소에 Push를 할 수 있기 때문
      (2) 반면에 동시에 많은 개발자에게 의견을 받고 오픈 소스를 개선하고 싶어함.
      (3) 개발자들 역시 오픈 소스에 참여하고 싶지만 원본 저장소에 직접 푸쉬하는 것에 대한 부담이 존재
   ▪ 포크란? 
       - 개발자들은 원본 저장소를 자신의 계정에 복사 (Fork)하는 것 

   ▪ 포크를 이용한 개발 프로세스
      (1) 개발자들은 포크에서 작업들을 하며 커밋을 한다.
      (2) 그 후 원본 소유자에게 병합을 요청한다. 
      (3) 원본 소유자는 개발자의 병합 요청을 검토해서 원본 저장소에 반영한다.  
   ▪ 포크를 이용한 병합과 브랜치에서 병합을 요청할 때의 차이점 및 공통점  
      (1) 공통점: 브랜치에서 병합을 요청할 때 풀 리퀘스트를 하는 것과 동일
      (2) 차이점: 원본 저장소와 다른 원격 저장소에서의 브랜치에서 원본 저장소의 블랜치로 풀 리퀘스트를 하는 것

3) 브랜치와 포크의 차이점 

종류 설명
브랜치 - 하나의 원본 저장소에서 분기함 
- 하나의 원본 저장소에서 코드 커밋 이력을 편하게 볼 수 있음 
- 다수의 사용자가 다수의 브랜치를 만들면 관리하기 어려움
포크 - 여러 원격 저장소를 만들어 분기를 나눔 
- 원본 저장소에 영향을 미치지 않으므로 원격 저장소에서 마음껏 코드를 수정할 수 있음 
- 원본 저장소의 커밋 이력을 따로 보기 위해선 주소를 추가해야 함. 



 

 

  

728x90

댓글