왜 많은 프로젝트가 “git merge”보다 “git rebase”를 선호합니까? 관계없이 각각의 고유 한 변경 사항을 리포지토리에

DVCS를 사용하면 얻을 수있는 장점 중 하나는 편집 커밋 병합 워크 플로 ( CVCS에서 종종 시행하는 편집 병합 커밋 보다)입니다. 병합과 관계없이 각각의 고유 한 변경 사항을 리포지토리에 기록 할 수 있으므로 DAG 가 프로젝트의 실제 가계도를 정확하게 반영합니다.

많은 웹 사이트 가 “병합 커밋을 피하고 싶다”고 말하는가? 프리 커밋 또는 리베이스 포스트 병합을 병합하지 않으면 회귀를 분리하고 과거 변경 사항을 되 돌리는 등이 더 어려워 집니까?

설명의 포인트 : DVCS의 기본 동작 입니다 병합 커밋 만들 수 있습니다. 왜 이렇게 많은 곳에서 이러한 병합 커밋을 숨기는 선형 개발 히스토리를보고 싶어 하는가?



답변

사람들은 병합 커밋을 피하려고합니다. 진심으로. 그들이 자란 중앙 집중식 로그처럼 보이고 로컬로 단일 지점에서 모든 개발을 수행 할 수 있습니다. 이러한 미학을 제외하고는 “중앙”서버를 거치지 않고 동료로부터 직접 가져 오는 것이 갈등을 유발하는 것과 같은 장점과 언급 한 것 외에도 몇 가지 단점이 있습니다.


답변

두 단어로 : git bisect

선형 이력을 사용하면 버그의 실제 원인을 정확히 파악할 수 있습니다.


예입니다. 이것은 우리의 초기 코드입니다.

def bar(arg=None):
    pass

def foo():
    bar()

지점 1 arg은 더 이상 유효하지 않은 일부 리팩토링을 수행 합니다.

def bar():
    pass

def foo():
    bar()

Branch 2에는 다음을 사용해야하는 새로운 기능이 있습니다 arg.

def bar(arg=None):
    pass

def foo():
    bar(arg=1)

병합 충돌은 없지만 버그가 도입되었습니다. 운 좋게도이 특정 단계는 컴파일 단계에 걸리지 만 항상 운이 좋은 것은 아닙니다. 버그가 컴파일 오류가 아닌 예기치 않은 동작으로 나타나는 경우 일주일 또는 2 주 동안 버그를 찾지 못할 수 있습니다. 그 시점 git bisect에서 구조!

젠장. 이것이 보이는 것입니다 :

(master: green)
|             \_________________________
|                \                      \
(master: green)  (branch 1: green)     (branch 2: green)
|                 |                     |
|                 |                     |
(master/merge commit: green)            |
|                         ______________/
|                        /
(master/merge commit: red)
|
...days pass...
|
(master: red)

따라서 git bisect빌드를 중단 한 커밋을 찾기 위해 전송 하면 병합 커밋을 정확히 찾아냅니다. 글쎄, 그것은 약간 도움이되지만 본질적으로 하나의 커밋 패키지가 아닌 커밋 패키지를 가리키고 있습니다. 모든 조상은 녹색입니다. 반면에 rebasing을 사용하면 선형 이력을 얻을 수 있습니다.

(master: green)
|
(master: green)
|
(all branch 1 commits: green)
|
(some branch 2 commits: green)
|
(branch 2 commit: red)
|
(remaining branch 2 commits: red)
|
...days pass...
|
(master: still red)

이제 git bisect빌드를 깨뜨린 정확한 기능 커밋을 가리킬 것입니다. 커밋 메시지는 다른 리팩토링을 수행하고 버그를 즉시 수정하기에 충분한 의도를 설명하는 것이 이상적입니다.

관리자가 모든 코드를 작성하지 않았을 때 큰 프로젝트에만 영향을 미치므로 주어진 커밋이 수행 된 이유 / 각 분기의 목적을 반드시 기억할 필요는 없습니다. 따라서 정확한 커밋을 정확하게 찾아 내면 커밋을 검사 할 수 있습니다.


즉, (현재) 여전히 병합을 선호합니다. 릴리스 브랜치에 기반을두면 git bisect일상적인 작업에 대한 실제 기록을 유지하면서 와 함께 사용할 선형 기록을 제공합니다 .


답변

간단히 말해서, 병합은 종종 무언가 잘못 될 수있는 또 다른 장소이기 때문에 사람들이 다시 다루기를 매우 두려워하기 위해 한 번만 잘못하면됩니다 (한 번 두 번 부끄러워하는 경우).

새 계정 관리 화면에서 작업 중이고 새 계정 워크 플로에서 버그가 발견되었다고 가정하겠습니다. OK, 우리는 두 가지 경로를 취합니다-계정 관리를 마치고 새 계정으로 버그를 수정합니다. 우리는 모두 계정을 다루기 때문에 매우 유사한 코드로 작업하고 있습니다. 아마도 동일한 코드 조각을 조정해야 할 수도 있습니다.

이제이 시점에서 서로 다른 두 가지 버전의 소프트웨어가 있습니다. 우리는 모두 변경 사항에 대한 커밋을 실행했으며, 코드를 충실히 테스트했으며, 독립적으로 우리는 훌륭한 일을 해냈다 고 확신합니다. 이제 뭐?

글쎄, 이제 합병 할 때가되었지만 ….. 이제 어떻게됩니까? 계정 관리 기능이 작동하지 않고 새 계정이 손상되어 이전 버그가 여전히 있는지 알 수없는 두 개의 작동하는 소프트웨어 세트에서 하나의 통합 된 끔찍하게 부서진 새로 버그가있는 소프트웨어로 바뀔 수 있습니다. .

어쩌면 소프트웨어가 영리했고 충돌이 있다고 말하고 우리가 그것을 인도한다고 주장했습니다. 글쎄, 쓰레기-나는 그것을하기 위해 앉아서 내가 즉시 이해하지 못하는 복잡한 코드를 추가 한 것을 보았습니다. 나는 그것이 변경 한 내용과 충돌한다고 생각합니다 … 나는 당신에게 묻습니다. 분이 지나면 확인하고 이해하지 못하는 코드를 보게됩니다. 우리 중 하나 또는 둘 다 시간을내어 앉아, 적절한 병합을 해시하고, 우리가 그것을 깨뜨리지 않았는지 확인하기 위해 전체 댕글 링을 다시 테스트해야합니다.

한편 8 명의 다른 사람들은 모두 사디스트처럼 코드를 작성하고 있습니다. 몇 가지 작은 버그를 수정하여 병합 충돌이 있다는 것을 알기 전에 제출했습니다. 오후에 쉬거나 회의에 갇혀 있거나 어쩌면 그냥 휴가를 가야 할 것 같습니다. 또는 경력을 바꾸십시오.

그래서,이 악몽을 피하기 위해, 어떤 사람들은 헌신을 매우 두려워하게되었습니다 (새로운 것이 무엇입니까?). 우리는 이런 시나리오에서 당연히 위험에 빠질 수 있습니다. 우리가 어리석게 생각하고 그것을 망쳐 놓지 않는다면, 사람들은 무모한 포기로 행동하기 시작합니다. 한숨

그래서 당신은 간다. 그렇습니다. 현대의 시스템은 이러한 고통을 완화 시키도록 설계되었으며, 쉽게베이스를 풀고베이스를 해제하고베이스를 해제하고 프리베이스와 Hanglide 등을 쉽게 수행 할 수 있어야합니다.

그러나 그것은 더 많은 일이며, 우리는 전자 레인지의 버튼을 누르고 포크를 찾을 시간이 있기 전에 4 코스 식사를하고 싶습니다. 매우 만족스럽지 않습니다. 코드는 작동합니다. 의미는 있지만 합병을 정상적으로 처리하는 것은 중요하지 않습니다.

일반적으로 프로그래머는 훌륭한 작업 메모리를 개발 한 다음 문제를 완료하자마자 모든 정크 및 변수 이름과 범위를 즉시 잊어 버리고 병합 충돌을 처리하는 경향이 있습니다. 잘못 처리 된 병합)은 사망률을 상기시키기위한 초대입니다.


답변

리베이스는 이동 분기점을 제공하여 변경 사항을 기준으로 다시 푸시하는 프로세스를 단순화합니다. 따라서 장기 실행 브랜치를 마치 로컬 변경 인 것처럼 취급 할 수 있습니다. 리베이스하지 않고 분기는 기준선에서 변경 사항을 누적하여 변경 사항을 다시 기준선에 병합합니다.

병합하면 기준점이 원래 분기점에 남습니다. 분기에서 벗어난 라인에서 몇 주 동안의 변경 사항을 병합하면 이제 분기 지점에서 많은 변경 사항이 발생하며이 중 많은 부분이 기준선에있게됩니다. 이로 인해 지점의 변경 사항을 식별하기가 어렵습니다. 변경 사항을 기준으로 되 돌리면 변경 사항과 관련이없는 충돌이 발생할 수 있습니다. 충돌이 발생하는 경우 일관되지 않은 변경 사항을 적용 할 수 있습니다. 진행중인 병합은 관리 노력이 필요하며 변경 사항을 푸는 것이 상대적으로 쉽습니다.

Rebase는 지점을 기준선의 최신 개정으로 이동합니다. 발생하는 모든 충돌은 변경 사항에만 해당됩니다. 변경 사항을 푸시하는 것이 훨씬 간단합니다. 추가 리베이스를 수행하여 로컬 브랜치에서 충돌을 처리합니다. 상충되는 푸시의 경우, 마지막으로 변경 사항을 푸시하면 변경 사항으로 문제를 해결해야합니다.


답변

자동화 된 도구는 병합 코드가 컴파일 및 실행되도록하여 구문 상 충돌 을 피하는 데 도움이 되지만 병합으로 인해 발생할 수있는 논리적 충돌 이 없음을 보장 할 수는 없습니다 . 따라서 ‘성공적인’병합은 실제로 아무 것도 보장하지 않을 때 거짓된 자신감을 갖게하며 모든 테스트를 다시 수행해야합니다.

내가 본 것처럼 분기 및 병합의 실제 문제는 그것이 속담이 길을 따라 내려갈 수 있다는 것입니다. 일주일 동안 “나는 내 작은 세상에서 일할 것”이라고 말하고 나중에 일어날 문제를 다룰 수 있습니다. 그러나 버그 수정은 항상 신선 할 때 더 빠르거나 저렴합니다. 모든 코드 분기가 병합되기 시작할 때 이미 수행 된 작업의 일부 뉘앙스를 잊어 버릴 수 있습니다.

앞에서 언급 한 두 가지 문제를 함께 해결하면 활동이 조금 느려지더라도 모든 사람이 같은 트렁크에서 작업하고 충돌이 발생할 때마다 갈등을 해결하는 것이 더 쉽고 쉬운 상황에 처할 수 있습니다.


답변

추가 관련 포인트는 다음과 같습니다. 리베이스를 사용하면 릴리스 브랜치에서 기능을 쉽게 선택하거나 되돌릴 수 있습니다.


답변

어떤 대답에서도 세 가지가 언급되지 않았습니다.

  • 브랜치 내부에서 확산 :

    • 병합 커밋이있을 경우 브랜치에서 임의의 커밋 쌍 사이의 차이는 매우 어려워집니다.
  • 한 번에 하나의 항목 병합 :

    • 두 브랜치 간의 차이를 해결하는 경우 일반적으로 병합은 한 번에 모두 발생하며 특정 커밋이 충돌을 일으킨 컨텍스트와 상관없이 병합 충돌이 발생합니다. 리베이스하는 경우 충돌이 발생한 시점에서 리베이스가 중지되고 해당 컨텍스트에서 해결할 수 있습니다.
  • 푸시 전 정리 :

    • 나중에 수정해야 할 실수 커밋을 한 경우 대화식 리베이스를 푸시하지 않으면 커밋을 결합 / 분할 / 변경 / 이동할 수 있습니다. 병합 한 경우에도 그렇게 할 수 있지만 병합 경계를 가로 질러 결합 / 분할 / 변경 / 이동하려는 경우 매우 어려워집니다.