왜 많은 프로젝트가 "git merge"보다 "git rebase"를 선호합니까?


68

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

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

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


18
도구가 항상 올바른 것은 아닙니다. 그리고 그들이 잘못되면, 오, 그들이 잘못합니까?
Oded

2
@Oded 자동 병합 커밋 (예 :에 의해 생성 된 커밋)을 참조하고 git pull있습니까? 왜 이것이 문제가 될 수 있는지 알 수 있지만 내 질문은 모든 병합 커밋 에 관한 입니다.
Jace Browning

의 중복 가능성 빈번한 복잡한 병합 문제의 로그인을하는 충돌? " 사람이 해결해야하는 내재적 갈등 (즉, 두 명의 개발자가 동일한 코드 라인을 변경)으로 인해 병합과 리베이스가 똑같은 충돌을
일으켜야합니다

답변을 게시 한 후 IMO가 답변으로 정확히 맞지 않는다는 생각 : git pull --rebase == svn up(느슨한 같음, 엄격하지 않음)
Izkata

SourceSafe와 같은 중앙 저장소에서는 지속적인 노력으로 변경 사항을 계속 체크인하지만 안정된 안정에 도달하면 특정 날짜를 기준으로 모든 파일에 레이블을 지정합니다. 그런 다음 적어도 버그 전후를 이러한 버전이나 rebase- line 과 연관시킬 수 있습니다 .
Andyz Smith

답변:


64

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


5
당신은 내 질문을 올바르게 이해 한 유일한 사람입니다. 더 명확하게하려면 어떻게해야합니까?
Jace Browning

12
"선형 개발 역사의 이점은 무엇입니까?"
Karl Bielefeldt

11
"자신의 커밋이 원격 브랜치 (기고하려고하지만 유지 보수하지 않는 프로젝트)에 명확하게 적용되도록하기 위해이 작업을 수행합니다.이 경우 작업을 수행하게됩니다. 메인 프로젝트에 패치를 제출할 준비가되었을 때, 브랜치에서 작업을 오리진 / 마스터로 리베이스하십시오. " git-scm.com/book/en/Git-Branching-Rebasing
Andyz Smith 1

12
마치 장식적인 것처럼 들리지만 실제로 실용적입니다. 선이 교차하고 병합되는 복잡한 DAG보다 선형 이력에서 무슨 일이 일어나고 있는지 이해하는 것이 훨씬 쉽습니다.
Daniel Hershcovich

20
"그 미학을 제외하고는 어떤 이점도 없습니다"– 나는 그것에 대해 모른다. 역사가 회로 기판처럼 보일 때 역사에서 무슨 일이 일어나고 있는지 이해하기는 정말 어렵습니다. (각각 고유 한 지사와 워크 플로가있는 여러 팀 구성원을 가정합니다.)
Archagon

46

두 단어로 : 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일상적인 작업에 대한 실제 기록을 유지하면서 와 함께 사용할 선형 기록을 제공합니다 .


2
이 설명에 감사드립니다. 왜 여전히 병합을 선호하는지 확장 할 수 있습니까? 나는 많은 사람들이 대부분의 상황에서 재혼하기를 선호하지만 그 이유를 이해할 수 없었습니다.
필립

@ 필립 나는 작은 개인 프로젝트에서 몇 달 동안 만 팀을 사용하지 않았기 때문에 조금 주저합니다. 그러나 내가 최근에 한 일을 추적 할 때 여러 지점에서 커밋의 흐름을 볼 수있는 gitk --all것이 매우 유용합니다 (특히 일반적으로 주어진 시간에 3 개의 지점이 있고 매일 내 기분에 따라 전환합니다) 시간).
이즈 카타

16
그러나 병합이 문제이므로 병합 커밋 이 필요 합니다 bisect. 더 깊이 파고 싶다면 개별 커밋을 찾 blame거나 찾기가 쉽습니다 bisect. 선형 기록을 사용하면 병합이 장부에서 완료되므로 더 이상 잘못된 병합이 문제라고 말할 수 없습니다. 존재하지 않는 인수를 사용하는 바보처럼 보입니다. 특히 인수가 이전에 여러 커밋을 제거한 경우. 당신이 역사를 파괴 할 때 그가 그렇게 할 것인지 알아 내려고 노력 하는 것은 훨씬 더 어렵다.
Karl Bielefeldt

1
이 답변에 동의하지 않습니다. Rebasing은 때때로 bisect의 유틸리티를 파괴합니다. 리베이스의 헤드 커밋을 제외하고 리베이스에서 커밋을 실행할 수 없습니다. 예 : A로 분기하여 B, C, D를 만들었습니다. 누군가 E를 눌렀습니다. B와 C이고 일을했지만 E를 기반으로하면 B와 C는 실행할 수 없거나 실행할 수없고 작동하지 않습니다. 가장 좋은 경우는 포기하는 것이며, 최악의 경우 B 또는 C에 실제로 문제가있을 때 문제가 있다고 생각합니다.
Lan

4
@Izkata 왜 bisect를 사용하고 있습니까? 버그 / 실수가 발생하기 때문입니다. 겸손한 현실입니다. 어쩌면 "마우스를 왼쪽으로 끌어다 놓는 것"은 자동 / 수동 테스트 목록의 일부가 아니었을 것입니다. 지금 우리는 그 기능이 깨졌지만 작동하는 것을 알고 있습니다.
Lan

17

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

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

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

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

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

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

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

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

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

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


5
놀랍게도, 선생님!
Southpaw Hare

10
이것은 사람들이 병합을 두려워하는 이유에 대한 답변이며 왜 많은 사람들이 병합 커밋을 나쁜 것으로 간주하지는 않습니다.
Jay

23
이 답변의 문제점은 이것이 여전히 rebase에 해당 된다는 것입니다 . 결국, rebase는 이미 변경된 코드 줄을 변경 한 곳에서 여전히 병합을 수행하고 있습니다. 나는 이것이 질문이 언급 된 방식의 문제라고 생각합니다.
deworde

1
이 답변은 여전히 ​​웅변 적이기 때문에 요점을 넘어 서기 때문에이 답변을 하향 조정했습니다.
유진

5
이것은 rebase와 merge-merge에서 전혀 맞지 않는 것 같습니다.
Andyz Smith

5

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

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

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


1
따라서 모든 변경 사항 은 유일한 변경 이기 때문에 '잘못된 것'으로 간주되는 수준으로 새로운 변경 사항을 강등하는 것 입니다. 이전의 모든 변경 사항이 merge에 포함 된 경우 모든 변경 사항은 오늘날 '올바른 것'인지 여부에 따라 수준이 다릅니다.
Andyz Smith

@AndyzSmith 새로운 변경 사항을 잘못된 것으로 간주하지 않습니다 . 무엇이 변경되고 추진되어야하는지 결정하려고 할 때, 그것들은 옳습니다 . 리베이스 한 후에는 기본 변경 사항 인 이전 변경 사항을 무시할 수 있습니다.
BillThor

맞습니다. 따라서 기준이 무엇인지 확실하지 않은 경우, 즉 이미 병합 할 준비가되었는지 변경 한 것이 '올바른 것'인지 여부는 변경되지 않습니다.
Andyz Smith

그리고 누군가로부터 변경을 받고 함수 프로토 타입이 기존 프로토 타입과 일치하지 않기 때문에 빌드가 중단되면 rebase로 인해 새로운 사람이 잘못되었음을 즉시 알 수 있습니다. 아마도 새로운 사람이 올바른 프로토 타입을 가지고 있고 이전에 미 기반의 적용되지 않은 changset 변경이 잘못된 프로토 타입을 가진 것일 것이라고 추측 할 필요는 없습니다.
Andyz Smith

4

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

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

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


4
이것은 사람들이 병합을 두려워하는 이유에 대한 답변이며 왜 많은 사람들이 병합 커밋을 나쁜 것으로 간주하지는 않습니다.
Jay

그래서, 당신이 말하는이 트렁크는 rebase와 관련이 있습니까? 설명해주세요
Andyz Smith

@AndyzSmith "트렁크"는 git의 "마스터"브랜치에 해당하는 svn의 용어입니다
Izkata

@ 이즈 카타 그래서이 대답은 병합 또는 rebase를 사용하지 않는 것을 옹호합니까?
Andyz Smith

@AndyzSmith 그래, 나도 그것을 읽는 방법이다. 그리고 나는 그것에 동의하지 않습니다. 제안 된 방식으로 다른 개발자 7 명과 팀을 이루어 작업 한 후에는 제안하지 않습니다. 피처 브랜치를 우리보다 더 많이 사용해야했지만 브라이언 D 홀 (BrianDHall이 그의 답변에 들어감)에 따라 병합을 두려워합니다.
이즈 카타

0

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


1
이것에 대한 자세한 내용은?
Akavall

물론 :-) git flow에서 정기적으로 새로운 릴리스 분기를 만듭니다. 생성 후 버그가 개발 지점에서 수정 git cherry-pick -x <commit>되면 릴리스 지점에 버그 를 적용합니다. 또는을 사용하여 출시를 위해 무언가를 취소 할 수 있습니다 git revert <commit>. 경우 <commit>병합이, 그것은 털이 가져옵니다. 그것은 이다 내가 알고 있지만 쉽게 할 수없는만큼 가능한. 리베이스를 사용할 때 모든 '병합'은 거대하지만 규칙적인 커밋이며 쉽게 채집 및 되돌릴 수 있습니다.
Bruno Schäpper

0

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

  • 브랜치 내부에서 확산 :

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

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

    • 나중에 수정해야 할 실수 커밋을 한 경우 대화식 리베이스를 푸시하지 않으면 커밋을 결합 / 분할 / 변경 / 이동할 수 있습니다. 병합 한 경우에도 그렇게 할 수 있지만 병합 경계를 가로 질러 결합 / 분할 / 변경 / 이동하려는 경우 매우 어려워집니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.