Git

Git conflict 해결하기 / Can’t automatically merge.

최진영 2021. 12. 11. 22:25

Can’t automatically merge.

 우테코 미션 내에서 Test 코드의 변경으로 인하여 메일이 2주차 그리고 이번 3주차에도 왔었습니다. 테스트코드가 변경되었기 때문에 Pr을 보낼 때 Can’t automatically merge. 문구를 노출시키지 않고 Git 파일 충돌을 해결하는 방법에 대해서 해결해보라는 내용이었어요.

 과연 이게 무엇인지, 어떻게 해결하는지 알아볼까 합니다.

 

변경점이 없을 때의 상태

 먼저 특정 아이디로 repository를 하나 fork해보겠습니다.

repository를 fork한 모습

 기존 repository에서는 변경점이 이루어지지 않았기 때문에 origin repository와 지금 fork한 repository에는 큰 문제점이 발생하지 않을 것입니다.

 그 예로 fork repository에 새로운 기능을 위한 브랜치를 만들어 변경점을 줘 볼게요.

fork repository에 commit한 후 push

 branch를 하나 생성하여 Car 클래스를 하나 생성했습니다. 그럼 기존에 pr을 보내던 것처럼 pr을 보낸다면?

정상적으로 동작하는 pull request

 당연히 아직까지는 git 충돌이 나지 않기 때문에 어떠한 경고문구 없이 pr을 작성하게끔 도와줍니다.

 그럼 지금까지의 버전을 간단하게 확인해 보겠습니다.

현재 commit 상태

 Origin Repository의 현재 Application이 변경점이 없기 때문에 Fork Repository에서 어떠한 작업을 한 후 pr을 전송하게 된다면 정상적으로 요청됨을 그림 버전만 보더라도 확실히 알 수 있습니다.

 

변경점을 직접 수정해서 커밋한다면

 현재 미션에서의 문제 상황입니다. fork를 받았아서 작업을 진행하고 있는데 갑자기 Origin Repository에서 변경점이 발생하였고 이를 반영을 하고 작업을 해야하는 상황입니다. 사실 이렇게하면 안되지만 직접 에러를 보기 위해서 다음과 같이 진행하려고 해요.

Application을 직접 수정하고 Pull Request를 생성하면 Can’t automatically merge.

 변경점을 아는 상태에서 제가 직접 Application의 변경점을 수정하고 커밋해서 pr을 요청해보려고 합니다. 아래와 같은 방법으로 직접 말이죠.

원본 repository를 일시적으로 수정

 

 자, 그럼 변경된 사항을 확인하고 저희 저장소에서 위의 변경점을 "직접  수정해서 " commit하고 다시 열심히 작업했다라고 가정해볼게요. 그럼 pr을 요청하면 잘 될까요? 변경점을 실제로 적용은 했으니말이죠.


Able to merge 뭔가 잘 되는거처럼 보이기는 합니다. 최소한 conflict는 안나는거처럼 보이거든요.

 근데 사실 로컬에서 default를 setting으로 직접 수정을 한 업데이트 내용이고, pr에서 나타나야할 변경점이라기보다는 Origin에서 이미 변경해둔 변경점이기 때문에 아래 file change에서 나타나기에는 부적절한 file change내용입니다.

 

 그럼 수정을 했다고 감안을 하고 Application을 더 수정해보도록 할게요.

직접 변경점을 수정해둔 상태에서 다른 기능 추가

 이전에도 conflict가 나지 않았기 때문에 정상적으로 버전관리가 잘 되는 상태라고 생각하고 pr을 요청해보겠습니다.

추가하니 conflict가 난 상황

 이제 갑자기 automatically merge에러가 발생하는 것을 볼 수 있습니다. 파일 상의 변경점은 물론 잘 지키면서 만들어 낸 것 처럼보이지만 git상의 파일은 automatically하게 잘 관리되지 못하는 것이었죠.

Origin
- init project
- feat: Origin 변경점 업데이트

Fork
- init project
- feat: Car 객체 생성
- feat: move 메소드 추가
- feat: Origin 변경점 반영
- feat: Origin print 추가

 이렇게 git history가 있다라고 정리하면, Fork Repository에는 - feat: Origin 변경점 업데이트가 없이 Origin에 pr을 요청하게 되는 것이고 결국 pr요청은 된다지만, git branch의 commit은 conflict난 상황이 되겠죠.

 git flow로 한번 remote add upstream를 로컬에 더 추가해서 확인해보도록 할게요.

$ git remote add upstream (origin 저장소 주소)
현재 git flow 상황

 upstream은 실제 origin의 최신 버전을 위한 저장소 네이밍으로 생각하면 좋을 것 같아요.

 branch 그림만 보더라도 git branch가 완전히 꼬여있음을 확인할 수 있습니다. 빨간색 branch가 최신 메인 branch인데 우리가 사용하고 있는 파란색 branch는 무시하고 자기 방향대로 가고있거든요. 아까전에 이야기한대로 분명 conflict는 나지 않겠지만 pr을 보내본다면 다음과 같은 현상이 발생합니다.

Can’t automatically merge.가 나더라도 pr은 되지만 다음과 같이 conflict를 해결하라는 제한사항이 발샏ㅇ

 결국 pr은 정상적으로 났지만 pr 하단에서 conflict가 났으니 알아서 처리하라는 이야기가 나옵니다. 당연히 바로 merge는 되지 않게 잠겨있구요. github에서 자동으로 resolve conflict를 해결해줄 수 있는 버튼이 생기기는 했지만 이전에 로컬 저장소에서 최신 branch 업데이트를 안해줘서 발생한 문제이기 때문에 로컬에서 먼저 해결해줄 수 있겠죠.

 물론 print추가를 하기 전 직접 수정으로 commit만 변경하고 다른 작업을 하지않아도 여러명이 작업하다보면 언젠가는 branch가 꼬여 발생할 문제입니다. git flow를 잘 지켜줘야한다는 이야기죠.

 

변경점 잘 적용하기

그럼 어떻게 로컬에서 잘 해결할 수 있는지 두가지 방법을 이야기하려고 합니다.

 

1. fetch upstream

앞서 명령어로 사용했던 upstream 저장소를 받아와서 git 변경점을 업데이트하는 방법입니다. 로컬 저장소에 upstream remote 저장소를 추가한 다음 변경 내용을 업데이트 하는 방식이죠.

직접 commit으로 변경점을 수정하지 않은 상태

이전과는 다르게 직접 변경점을 수정하는 것이 아닌 먼저 upstream 저장소를 받아오겠습니다.

$ git remote add (최신 업데이트 저장소 네임) (최신 업데이트 저장소 url)
ex) git remote add upstream https://github.com/jinyoungchoi95/Cant_Automatically_Merge
upstream remote 추가

 위에서 잠깐 이야기한 대로 로컬 저장소에 원본 저장소에서 update되어 갈려있는 저장소 하나를 만들 수 있습니다. 이 branch를 파란색 로컬 브랜치에 동기하는것이 목적이죠.

그럼 이제 fetch를 통해 동기화하고 싶은 저장소의 브랜치를 받아오겠습니다.

$ git fetch upstream (동기화할 로컬 브랜치)
ex) git fetch upstream master

지금은 master가 update되어있으므로 master를 먼저 받아올게요.

branch fetch

 그럼 이제 upstream에 우리가 동기화하고싶은 master 브랜치가 생기는거죠. 그럼 이제 마지막으로 fork repository의 master를 rebase로 동기화를 해보면

$ git rebase upstream/master
rebase하니 중간에 git commit이 정상적으로 잘 들어가 있다.

 로컬 master가 rebase로 인해 잘 동기화가 되었고, 로컬 master에서 나온 기능 브랜치도 또한 마찬가지로 같이 동기됨을 알 수 있습니다.

 이렇게 git flow가 적용되어있지만, 실제로는 해당 commit이 있다는 가정하에 기능 커밋이 적용된 상태이기 때문에 conflict없이 잘 pr될 것입니다. 아래처럼요.

정상적으로 변경점을 git flow에 맞게 적용한 모습

 

 file change 또한 이전에 직접 commit해서 수정한 것과는 다르게 원본 저장소에서 변경했던 change는 변경이력에 없고 실제 제가 update한 부분만 file change에 있음을 알 수 있습니다. 버전관리를 함에 있어서 feat: Origin 변경점 업데이트는 master브랜치에서 작업한 git 내역이기때문에 겹치지않음과 더불어 지금 pr에서 이렇게 나오지 않는것이 정상적인 git flow가 되겠죠.

 

2. github Fetch upstream

 github에서 branch를 자동으로 동기화해주는 방식을 사용하는 것입니다. 원격 fork 저장소에 fetch한 다음, pull해서 가져오는 방식이 되겠죠.

Github에서 제공하는 fetch and merge
정상적으로 반영되어 merge됨을 확인할 수 있다.

 기대한대로 잘 fetch merge된 모습입니다. 사실 1번과정에서 했던 내용들이 실제 github에서 제공할 뿐이지 크게 다른건 없습니다. 실제로 github docs에 들어가면 다음과 같이 되어있어요.

Github에서 제공하는 fetch and merge가 실제 동작하는 순서

 명령어를 안쓰고, github에서 충돌이 나지않는 선에서 알아서 처리를 해주는 거죠. 결국 1번과 다른건 fetch 후 merge를 한다이기 때문에, conflict를 당연히 해결해줍니다. 다만 merge되었기 때문에 merge commit이 남아있겠죠.

 

정리

 git flow를 지키면서 원본 브랜치에 conflict가 나지 않게 동기하는 방법 두가지를 알아봤습니다. 정리하면 딱 두가지에요.

 

  1. cli
$ git remote add (최신 업데이트 저장소 네임) (최신 업데이트 저장소 url)

$ git fetch upstream (동기화할 로컬 브랜치의 origin 브랜치)

$ git rebase upstream/(동기화할 로컬 브랜치)
  1. GitHub 내에서 fetch and merge

 

 두 가지 방법 모두 변경이력에 대해서 동기를 해줌은 맞지만 github같은 경우 merge가 강제되고, cli의 경우 rebase를 쓰거나 merge를 쓰거나 두가지 방법을 선택할 수 있습니다. (rebase와 merge의 차이는 다음에 정리해보겠습니다)

 

 중요한건 원본 저장소의 변경 내용이 있을 때 그와 똑같이 commit만 짜서 push해서 될 것이 아니라, git flow에 맞게 버전관리를 잘 해주어야 conflict없이 안전한 git 사용을 할 수 있다인 것 같습니다 :)