Git Rebase 사용을 중단해야하는 이유

몇 년 동안 Git을 사용한 후, 나는 일상적인 워크 플로의 일부로 점점 더 고급 Git 명령을 점차적으로 사용하고 있음을 알게되었습니다. Git rebase를 발견 한 직후, 나는 그것을 매일의 워크 플로우에 빠르게 통합시켰다. rebasing에 익숙한 사람들은 도구가 얼마나 강력하고 도구를 항상 사용하고 싶은지 알고 있습니다. 그러나, 나는 재베이스가 당신이 그것을 처음 시작할 때 명확하지 않은 몇 가지 도전을 제시한다는 것을 곧 알게되었습니다. 그것들을 발표하기 전에, 병합과 rebasing의 차이점을 신속하게 살펴 보겠습니다.

먼저 기능 분기를 마스터와 통합하려는 기본 예를 살펴 보겠습니다. 병합하여 두 분기 간의 병합을 나타내는 새 커밋 g를 만듭니다. 커밋 그래프는 어떤 일이 일어 났는지 명확하게 보여 주며 더 큰 Git-repos에 익숙한 "트레인 트랙"그래프의 윤곽을 볼 수 있습니다.

병합의 예

또는 병합하기 전에 리베이스 할 수도 있습니다. 커밋이 제거되고 기능 분기가 마스터로 재설정 된 후 커밋이 기능 위에 다시 적용됩니다. 이러한 재 적용 커밋의 차이점은 일반적으로 원래의 커밋과 동일하지만 부모 커밋이 다르므로 SHA-1 키가 다릅니다.

리베이스의 예

이제 기능의 기본 커밋을 문자 그대로 b에서 c로 변경했습니다. 피처에 대한 모든 커밋은 마스터의 직계 자손이므로 피처를 마스터에 병합하는 것이 이제 빨리 병합됩니다.

빨리 감기 병합의 예

병합 방식과 비교할 때 결과 분기는 분기 분기가없는 선형입니다. 향상된 가독성은 병합하기 전에 rebasing 브랜치를 선호하는 이유였으며, 다른 개발자들도 마찬가지입니다.

그러나이 방법에는 분명하지 않은 몇 가지 과제가 있습니다.

기능에서 여전히 사용중인 종속성이 마스터에서 제거 된 경우를 고려하십시오. 기능이 마스터에 리베이스 될 때 첫 번째 재 적용 커밋은 빌드를 중단하지만 병합 충돌이없는 한 리베이스 프로세스는 계속 중단되지 않습니다. 첫 번째 커밋의 오류는 모든 후속 커밋에 계속 남아있어 커밋 체인이 끊어집니다.

이 오류는 리베이스 프로세스가 완료된 후에 만 ​​발견되며 일반적으로 새로운 버그 수정 커밋 g를 적용하여 수정됩니다.

실패한 리베이스의 예

그러나 리베이스 중에 충돌이 발생하면 Git은 충돌 커밋에서 일시 중지되어 진행하기 전에 충돌을 해결할 수 있습니다. 긴 커밋 체인을 재조정하는 과정에서 충돌을 해결하는 것은 종종 혼란스럽고 올바르게 이해하기 어렵고 잠재적 인 오류의 또 다른 원인입니다.

리베이스 작업 중에 오류가 발생하면 문제가 발생합니다. 이렇게하면 기록을 다시 작성할 때 새로운 오류가 발생하며 기록이 처음 기록 될 때 발생한 실제 버그를 위장 할 수 있습니다. 특히 이것은 Git 도구 상자에서 가장 강력한 디버깅 도구 인 Git bisect를 사용하기 어렵게 만듭니다. 예를 들어, 다음 기능 분기를 고려하십시오. 지점 끝에 버그를 도입했다고 가정 해 보겠습니다.

끝에 버그가 도입 된 지점

지점이 마스터에 병합 된 후 몇 주가 지나야이 버그를 발견 할 수 있습니다. 버그가 발생한 커밋을 찾으려면 수십 또는 수백 개의 커밋을 검색해야 할 수도 있습니다. 이 프로세스는 버그가 있는지 테스트하는 스크립트를 작성하고 git bisect run 명령을 사용하여 Git bisect를 통해 자동으로 실행하여 자동화 할 수 있습니다.

Bisect는 이력을 통해 이분법 검색을 수행하여 버그를 유발 한 커밋을 식별합니다. 아래에 표시된 예에서는 깨진 커밋에 모두 실제 버그가 포함되어 있기 때문에 첫 번째 잘못된 커밋을 찾는 데 성공합니다.

성공적인 Git bisect의 예

반면에 rebasing (여기서 d와 e) 중에 깨진 커밋을 추가로 도입하면 bisect에 문제가 생길 수 있습니다. 이 경우 Git은 commit f를 나쁜 것으로 식별하기를 원하지만 테스트를 중단시키는 다른 오류가 포함되어 있기 때문에 dinstead를 잘못 식별합니다.

실패한 Git bisect의 예

이 문제는 처음에 보이는 것보다 큽니다.

왜 우리는 Git을 전혀 사용하지 않습니까? 코드에서 버그의 원인을 추적하는 데 가장 중요한 도구이기 때문입니다. 힘내는 우리의 안전망입니다. 리베이스를 통해 우리는 선형 이력을 달성하려는 욕구에 찬성하여 우선 순위를 줄입니다.

얼마 전, 우리 시스템의 버그를 추적하기 위해 수백 개의 커밋을 통과해야했습니다. 잘못된 커밋은 동료가 수행 한 잘못된 리베이스로 인해 컴파일되지 않은 긴 커밋 체인의 중간에 위치했습니다. 이 불필요하고 완전히 피할 수있는 오류로 커밋을 추적하는 데 거의 하루를 더 소비했습니다.

그러면 rebasing 중에 이러한 일련의 깨진 커밋을 어떻게 피할 수 있습니까? 한 가지 접근 방식은 rebase 프로세스가 완료되도록하고 코드를 테스트하여 버그를 식별 한 다음 기록으로 돌아가 버그가 발생한 위치를 수정하는 것입니다. 이를 위해 대화식 리베이스를 사용할 수 있습니다.

또 다른 방법은 리베이스 프로세스의 모든 단계에서 Git을 일시 중지하고 버그를 테스트 한 후 진행하기 전에 즉시 수정하는 것입니다.

이는 번거롭고 오류가 발생하기 쉬운 프로세스이며이를 수행하는 유일한 이유는 선형 히스토리를 달성하는 것입니다. 더 간단하고 좋은 방법이 있습니까?

있다; 힘내 병합. 한 번의 커밋으로 모든 충돌이 해결되는 간단한 1 단계 프로세스입니다. 그 결과 병합 커밋은 브랜치 간의 통합 지점을 명확하게 표시하며, 이력은 실제로 발생한 상황과 발생 시점을 보여줍니다.

당신의 역사를 진실로 유지하는 것의 중요성을 과소 평가해서는 안됩니다. rebasing함으로써, 당신은 자신과 팀에 거짓말입니다. 어제 커밋이 다른 커밋에 따라 어제 작성되었을 때 오늘 커밋 된 것으로 가정합니다. 원래 상황에서 커밋을 수행하여 실제로 일어난 일을 위장했습니다. 코드가 빌드되는지 확신 할 수 있습니까? 커밋 메시지가 여전히 타당하다는 것을 확신 할 수 있습니까? 당신은 당신이 당신의 역사를 정리하고 명확히하고 있다고 믿을 수도 있지만, 그 결과는 그 반대 일 수 있습니다.

미래의 코드베이스에 어떤 오류와 문제가 있는지 말하기는 불가능합니다. 그러나 실제 기록이 다시 작성된 (또는 가짜) 기록보다 더 유용하다는 것을 확신 할 수 있습니다.

사람들이 지점을 리베이트하게하는 동기는 무엇입니까?

나는 그것이 허영에 관한 결론에 도달했습니다. Rebasing은 순수한 미적 작업입니다. 분명히 깨끗한 역사는 개발자로서 우리에게 호소하지만 기술적 인 측면이나 기능적인 측면에서 정당화 될 수는 없습니다.

비선형 역사. Paul Hammant의 그림

비선형 히스토리 그래프 인 "트레인 트랙"은 위협적 일 수 있습니다. 그들은 분명히 저에게 그런 식으로 시작했지만 그들을 두려워 할 이유가 없습니다. GUI 및 CLI 기반의 복잡한 Git 히스토리를 분석하고 시각화 할 수있는 훌륭한 도구가 많이 있습니다. 이 그래프에는 발생한 상황과 발생 시간에 대한 유용한 정보가 포함되어 있으며 선형화를 통해 아무것도 얻지 못합니다.

힘내는 비선형 역사를 위해 만들어지고 장려합니다. 그것이 당신을 끄는 경우 선형 히스토리 만 지원하는 더 간단한 VCS를 사용하는 것이 좋습니다.

나는 당신이 당신의 역사를 진실로 유지해야한다고 생각합니다. 분석 도구에 익숙해 지십시오. 유혹을 다시 쓰려고하지 마십시오. 재 작성에 대한 보상은 미미하지만 위험은 큽니다. 다음 번에 이력을 살펴보면 비열한 벌레를 찾아 내 주셔서 감사합니다.

이 게시물의 초안에 대한 귀중한 피드백을 주신 Paul Hammant와 Aslak Hellesøy에게 감사드립니다. 비선형 역사를 보여준 Paul Hammant에게 감사드립니다. 그의 뛰어난 사이트는 적극 권장됩니다. 이 게시물을 처음에 작성하도록 격려 한 Aslak에게 특별한 감사를드립니다.

이 게시물은 내가 JavaZone 2016에서 노르웨이어로 한 대화를 기반으로합니다 : https://vimeo.com/182068915