git merge conflict을 어떻게 방지할까

https://blog.jetbrains.com/idea/2016/10/intellij-idea-2016-3-public-preview/

팀에서 git을 쓰다보면 반드시 merge conflict이 발생한다. conflict이 파일 몇 개면 그래도 할 만 하지만, 수십 개가 넘어가면 막막해진다. 그 많은 걸 하나하나 해결하다보면 한 시간 정도는 쉽게 간다. 그러다가 잘못 해결하면, 없던 문제가 생긴다. 빨리 배포해야 하는데 이러면 정말 짜증난다. 이거 미리 막을 수 없을까?

결론: branch protection rule 쓰세요.

merge conflict은 왜 생기나?

git은 여러 branch에서 동시에 작업할 수 있다. 예전에 쓰던 Visual Sourcesafe(VSS)에서는 그게 안 됐다. 한 파일은 딱 한 사람만 수정할 수 있었다. 누군가 그 파일을 수정하고 있으면, 나는 그 사람이 끝날때까지 기다려야 했다. 혹시라도 그 사람이 파일을 연 채로 퇴근하면, 아무도 그 파일을 수정할 수 없었다. 정말 행복하고 느리던 시절이었다.

https://www.dynamsoft.com/codepool/using-sourcesafe-in-visual-studio-2005-2008.html

요즘은 그렇게 행복하지 않다. git은 패러렐 월드를 허용한다. 그 사람이 붙잡고 있는 파일을 나도 내 맘대로 고칠 수 있다. 내 코드와 그 사람의 코드가 각각 동시에 존재한다. 하지만 양자역학과 달리, git은 갈라진 세계를 언젠가는 합쳐야 한다. 우리는 한 팀이니까, 팀원들간의 코드가 영원히 다를 순 없다. 일을 나눠서 하다가 언젠가 합쳐야 한다. 그래야 테스트도 하고 배포도 할 수 있다.

VSS는 행복하고 느렸지만, git는 빠르고 불행하다. 근데 VSS는 pessimistic locking(비관적 잠금)이고 git은 optimistic locking(낙관적 잠금)이다. 비관적이기에 행복했고, 낙관적이기에 불행했다.

https://www.c-sharpcorner.com/UploadFile/shivprasadk/6-ways-of-doing-locking-in-net-pessimistic-and-optimistic/

VSS는 같은 파일을 둘이서 동시에 고치다가 충돌이 날 거라고 비관했다. 그래서 한 파일을 한 명만 고치도록 막았다. 그래서 충돌할 가능성을 원천 차단했다.

반면 git은 같은 파일을 둘이서 동시에 안 고칠쳐서 충돌이 안 날거라고 낙관했다. 그래서 한 파일을 한 명만 고치도록 막지 않았다. 그래서 나중에 충돌할 수 있다.

자 그럼 여러분은 비관적으로 느리고 행복할 것인가, 낙관적으로 빠르고 불행할 것인가? 안타깝게도 우리에게는 선택의 여지가 없다. 당연히 빨라야 한다. 빠른 만큼 불행해진다. 충돌이 없을 거라 낙관하고 수정하다가, 막판에 합치면서 그 동안 쌓였던 수많은 충돌들을 본다.

merge conflict은 어떻게 해결하나?

내가 수정한 코드와 다른 사람이 수정한 코드를 눈으로 비교하며 합친다. 수작업이다. 한 줄 한 줄 코드를 읽고 이해해서, 어떻게 합질지를 나의 의지로 결정한다. 코드가 길면 정말 고통스럽다. git에서 편의상 두 파일의 다른 부분을 보여주지만, 그건 어디까지나 편의를 제공하는 것 뿐이다. 내가 합친게 최신이 된다. 아니 요즘 같은 인공지능 시대에 이걸 사람 손으로 하고 있어야 하나?

그래서 보통은 충돌한 부분을 고친 사람을 부른다. 그래서 그 사람이 어떻게 고쳤는지 자초지종을 듣고, 내 생각을 말하고, 그래서 서로 합의를 해서 고친다. 요즘 같으면 잠깐 화면을 공유하며 코드를 같이 본다. 사람이 합의를 해서 코드를 합치는게 제일 좋다.

https://medium.com/@weblab_tech/pair-programming-guide-a76ca43ff389

근데 충돌이 여러 군데가 날 수 있다. 한 사람이 아니라 여러 사람의 코드와 충돌할 수 있다. 그러면 나는 여러 명과 회의해서 합의해야 한다. 게다가 그 여러 명 끼리도 충돌할 수 있다. A를 고치면 B가 문제고, B를 고치면 C가 문제고, 그래서 C를 고치면 A가 문제라면? A, B, C를 고친 사람들을 모아 회의를 해서 합의를 해야 한다. 근데 마침 오늘 B가 휴가고, C는 시간대가 달라서 잠을 자고 있다면 회의를 나중으로 미뤄야 한다. 그러다보면 며칠이 걸리기도 한다.

그래서 바쁘니까 내가 임의로 고쳤다가, B가 3개월 전에 고쳤던 버그를 내가 다시 만들기도 한다. 다 이유가 있어서 그렇게 고쳤던 건데, 내가 그 분의 이야기를 안 듣고 내 맘대로 고치다보면 문제가 되기도 한다. 하나의 코드는 모두의 합의다. 합의를 함부로 깨면 안된다.

하지만 정말 급할 땐 어떡할까? 심각한 문제라면 일단 고치고 나중에 통보한다. 메일을 보내놓거나 메신저로 말을 걸어둔다. 그래서 사후에 내가 합친게 맞는지 확인한다.

그럼 우버같이 iOS 앱 하나에 매일 수천 건의 commit이 쏟아지는 경우에는 어떡할까? 개발자가 너무 많아서 서로 면대면으로 합의할 시간이 없으면 어떡할까? 우버에서는 시스템으로 자동 해결한다. 두 코드의 교집합만 골라내거나, 성공 확률이 높은 쪽을 선택한다. 먼저 커밋 별로 가중치를 매긴다. 보안 패치는 높고, 화면 수정은 낮다. 그래서 성공 확률을 예측한다. 물론 이러면 위에 말했던 것처럼, 일부 코드가 뒤로 돌아가서 3개월 전에 고쳤던 버그가 다시 생길수도 있다.

merge conflict를 어떻게 방지할까?

Github의 branch protection 기능을 쓴다. 이 규칙을 강력하게 설정할수록 충돌이 적어지거나 아예 없어진다. 다만 규칙이 강력할수록 개발이 느려지니까 적당히 잡아야 한다. 왜냐하면 충돌이 없어지는게 아니라 각 브랜치로 충돌이 옮겨가는 것이기 때문이다. 다만 자신의 브랜치에서 테스트를 한 번 더 해보니까, 빌드 실패나 테스트 실패를 미리 맛볼 수 있다.

Published
Categorized as xacdo

By xacdo

Kyungwoo Hyun

Leave a comment

Your email address will not be published.