본문 바로가기
버전관리/GIT_HUB

GIT 병합 시 충돌은 왜 발생하는걸까? (브랜치 병합 충돌과 해결)

by joa-yo 2023. 12. 1.
반응형

지금까지 Git을 사용할 때 브랜치를 별도로 나누지 않고 메인브랜치만 사용해왔습니다. 그런데 대규모 프로젝트를 가거나 소스 변경이 빠르게 일어나는 스타트업에서는 브랜치를 잘 활용하고 있다는 것을 알게되었는데요. 그래서 병합 충돌이 발생하는 원인과 그 해결방안에 대해서 알아봤습니다. 


충돌이 일어나는 이유

1. 동일한 파일을 동시에 수정한 경우

예를들어 각각의 브랜치에서 "a.txt"파일의 동일한 부분을 수정했다면 충돌이 발생합니다. 꼭 동일한 부분이 아니더라도 인접한 영역을 수정했더라도 충돌로 인식될 수 있습니다.

 

2. 삭제된 파일이나 새로운 파일이 동시에 추가된 경우

예를 들어 한 브랜치에서 파일을 삭제하고 다른브랜치에서 같은 파일을 수정하는 경우 충돌이 발생할 수 있습니다.

또는 한 브랜치에서 새로운 파일을 추가하고, 다른 브랜치에서도 동일한 파일의 내용을 수정하는 경우에도 충돌이 발생할 수 있습니다.

 


브랜치 충돌 해결 방법은?

먼저 merge를 실행했다는 가정하에 말씀 드리겠습니다. merge를 수행 한 결과 충돌이 발생했다면, merge되지 않고 해당 파일에 충돌 내용이 표시됩니다. 이 내용을 커밋할 내용으로 수정한 뒤 다시 커밋해주면, 신규 커밋이 생성되며 병합이 완료됩니다. 그럼 충돌이 일어나는 상태를 만들어보고 충돌을 해결해보겠습니다.


병합 충돌이 일어나는 상태 만들기

## 브랜치 생성 및 초기 커밋
$ git init branch2
$ pwd
/d/git/repositories
$ cd branch2
$ touch README.txt
$ git add README.txt
$ git commit -m 'init commit'
[master (root-commit) a0e591d] init commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.txt

## 새로운 브랜치, newBranch 생성
$ git branch newBranch

## master 브랜치에서 README.txt 수정
$ echo 'Edit on master branch' >> README.txt
$ cat README.txt
Edit on master branch
$ git add README.txt
warning: in the working copy of 'README.txt', LF will be replaced by CRLF the next time Git touches it
$ git commit -m "edit readme"
[master 1f8a325] eidt on master
 1 file changed, 1 insertion(+)

## newBranch에서 README.txt 수정
$ git switch newBranch
Switched to branch 'newBranch'
$ echo "Edit on newBranch" >> README.txt
$ cat README.txt
Edit on newBranch
$ git add README.txt
warning: in the working copy of 'README.txt', LF will be replaced by CRLF the next time Git touches it
$ git commit -m "edit on newBranch"
[newBranch a0a4549] eidt on newBranch
 1 file changed, 1 insertion(+)

## 결과
$ git log --oneline --graph --all
* a0a4549 (HEAD -> newBranch) edit on newBranch
| * 1f8a325 (master) edit readme
|/
* a0e591d init commit

 

 

병합 및 충돌 발생

## 병합 수행
$ git merge master
Auto-merging README.txt
CONFLICT (content): Merge conflict in README.txt
Automatic merge failed; fix conflicts and then commit the result.

 

자동 merge가 실패했으니, 충돌내용을 변경 한 뒤 그 결과를 커밋해달라는 문구가 발생합니다.

 

충돌 내용 확인

## 충돌 상태 확인
$ git status
On branch newBranch
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   README.txt

no changes added to commit (use "git add" and/or "git commit -a")

## 충돌된 README.txt파일을 열어 충돌 내용확인
$ cat README.txt
<<<<<<< HEAD
Edit on newBranch
=======
Edit on master branch
>>>>>>> master

 

'both modified:   README.txt'라고 표시된 부분이 보이시나요? README.txt파일을 병합 대상 브랜치 모두에서 수정이 일어나서 충돌이 일어났다는 의미입니다.

 

'no changes added to commit (use "git add" and/or "git commit -a")' 커밋이 추가되지 않았으니 git add와 git commit을 수행해야한다고 합니다.

 


 

README.txt파일 내용을 보니, 알 수 없는 기호들이 가득 있는데요, 한번 세세히 뜯어보겠습니다.

<<<<<<< HEAD
Edit on newBranch 
=======
Edit on master branch
>>>>>>> master

 

초록색 부분은 git이 소스 파일 중 충돌 영역을 구분 짓기 위해서 추가해준 기호들입니다. " ======="를 기점으로 위쪽으로는 HEAD가 있는 브랜치, 아래쪽으로는 master 브랜치입니다. HEAD는 현재 위치한 브랜치의 위치를 가리키므로 현재브랜치인 newBranch를 의미하겠죠? 각각의 브랜치에서 수정한 내용을 표시하고 있습니다.

 

충돌 해결

수동으로 소스 내용 수정

## 충돌 해결
$ vi README.txt
[vi 에디터에서 파일 수정 후 저장]
$ cat README.txt
Edit on newBranch
Edit on master branch

git에서 추가한 특수기호는 모두 제거하고, REAEME.txt의 본문내용을 커밋하고 싶은 형태로 수정해주세요. 저는 두 개의 내용을 모두 추가해보겠습니다.

 

충돌 해결 내용 반영

## 커밋 수행
$ git add README.txt
$ git commit -m "reslove conflict"
[newBranch e8983dd] reslove conflict

## 결과 확인
$ git log --oneline --graph --all
*   e8983dd (HEAD -> newBranch) reslove conflict
|\
| * 1f8a325 (master) edit readme
* | a0a4549 eidt on newBranch
|/
* a0e591d init commit

충돌 해결한 내용이 성공적으로 커밋된 것을 확인할 수 있습니다.

 

충돌 이전으로 복구하고 싶다면?

$ git reset --merge

현재 브랜치의 HEAD를 이전 커밋으로 되돌림으로써 병합이 시작되기 전 상태로 돌아갑니다. 이력을 그대로 유지하면서 돌아가기 때문에 안전합니다.

$ git reset --hard ORIG_HEAD

병합 이후 변경사항을 모두 삭제하고 이전 커밋으로 돌아가는 명령어입니다. 작업디렉터리의 변경도 모두 복구됩니다. 때문에 사용시 가장 주의를 요하는 명령어입니다.

$ git merge --abort

병합 이후 변경사항을 모두 삭제하고 이전 커밋으로 돌아갑니다.

 

 

  커밋이력 유지 및 헤드이동 커밋 이력 삭제 디렉토리 삭제
$ git reset --merge O X X
$ git reset --hard ORIG_HEAD X O O
$ git merge --abort X O X

앞에서 말한 내용을 간략하게 정리한 표입니다.

 


마무리하며

지금까지 Git브랜치 병합시 충돌하는 이유와 그 해결방법에 대해서 알아봤습니다. git의 기본 명령어만 알면 충돌을 해결 할 수 있었네요.그리고 충돌 해결 후 다시 한 번 꼼꼼히 테스트 해보는 것이 중요할 것 같습니다. 그럼 다음 포스팅은 병합 할 때 유용하게 사용할 수 있는 git 명령어인 stash에 대해서 이야기해보도록 하겠습니다. 감사합니다.

반응형

댓글