← 홈Setup Tip

Setup Tip

Setup Tip — diff가 비었다고 히스토리까지 합쳐진 것은 아니다

릴리스 후 main을 dev로 되돌려 합치는 PR이 머지됐고, 두 브랜치의 diff는 완전히 비어 있었다. 그런데 그 PR이 가져온 것은 main의 실제 릴리스 커밋이 아니라 내용만 같은 체리픽 복사본이었다. 트리는 같았지만 main은 dev의 조상이 아니었고, '마지막 태그 이후 dev에 쌓인 커밋' 목록에는 이미 배포된 커밋이 다시 섞여 나왔다. 백머지를 확인할 때는 diff가 아니라 조상 관계를 확인한다.

무슨 일이 있었나

릴리스 커밋이 main에 들어간 뒤, 늘 하던 대로 main을 dev로 되돌려 합치는 PR을 열었고 그 PR은 정상적으로 머지됐다. 머지 직후 git diff main dev를 돌려 보니 출력이 한 줄도 없었다. 겉으로는 완벽한 백머지였다.

무엇이 어긋났나

그 PR의 head는 main의 릴리스 커밋 자체가 아니었다. 같은 변경을 다른 부모 위에 체리픽한 복사본이었다. 내용이 같으니 트리도 같았지만, 커밋으로서는 전혀 다른 객체였다. 그래서 dev의 히스토리 어디에도 main의 실제 릴리스 커밋은 들어 있지 않았다.

왜 diff로는 안 보이나

git diff는 두 시점의 파일 상태만 비교한다. 그 상태에 어떤 커밋들을 거쳐 도착했는지는 묻지 않는다. 트리가 같다는 것은 '지금 내용이 같다'는 뜻이지 '같은 히스토리를 공유한다'는 뜻이 아니다.

실제로 무엇이 깨지나

히스토리를 기준으로 도는 도구들이 틀린 답을 낸다. 이번에는 git log 마지막태그..dev 범위에 이미 배포된 커밋들이 다시 나타났다. 이 범위로 다음 릴리스 후보를 판단하거나 릴리스 노트를 만들면, 이미 나간 변경을 새 변경으로 착각하게 된다. 다음 백머지에서 같은 변경이 다시 충돌할 가능성도 생긴다.

확인하는 명령

git merge-base --is-ancestor main dev && echo 조상 || echo 조상아님 한 줄이면 된다. 종료 코드 0이면 main의 커밋이 dev 히스토리에 정말 들어 있는 것이고, 1이면 diff가 비어 있어도 백머지는 끝나지 않은 것이다. git log --oneline dev..main이 비어 있는지도 같이 본다.

어떻게 고쳤나

dev 위에서 main을 --no-ff로 머지하는 새 브랜치를 만들고, 머지 전후 트리가 바뀌지 않았는지 먼저 확인했다. 그 PR은 스쿼시가 아니라 머지 커밋으로 합쳤다. 스쿼시로 합치면 같은 문제가 그대로 다시 생기기 때문이다. 머지 후 조상 관계를 다시 읽어서 main이 dev의 조상이 된 것을 확인했다.

정리

백머지의 목적은 내용을 맞추는 것이 아니라 히스토리를 잇는 것이다. diff가 비었다는 것은 필요조건일 뿐이고, 완료의 증거는 --is-ancestor의 종료 코드다.