지금까지 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에 대해서 이야기해보도록 하겠습니다. 감사합니다.
'버전관리 > GIT_HUB' 카테고리의 다른 글
레포지토리 다른 계정으로 이전하기 (Github Respository Transfer) (0) | 2023.12.06 |
---|---|
GIT Stash, commit하지 않고 변경이력 임시 저장하기! (0) | 2023.12.04 |
GIT 브랜치 병합 (3) | 2023.11.29 |
GIT 브랜치를 사용하는 이유와 생성/수정/삭제 (1) | 2023.11.25 |
GIT HUB 초기 푸시 프로세스 (0) | 2023.11.22 |
댓글