Git 병합 대신 Git rebase를 언제 사용합니까?


1546

Git rebase와 Git merge를 언제 사용하는 것이 좋습니까?

성공적으로 리베이스 한 후에도 병합해야합니까?




6
리베이스를 사용하는 사람들의 한 가지 문제는 코드를 정기적으로 푸시하지 못하게한다는 것입니다. 따라서 깨끗한 기록을 원하면 코드를 공유 할 수 없으므로 더 중요합니다.
static_rtti

9
@static_rtti : 그건 사실이 아닙니다. 변경 사항을 정기적으로 적용하지 못하는 경우 리베이스 기반 흐름을 잘못 사용하고 있습니다.
juzzlin

5
Andrew Arnott의 답변과 Pace의 답변이 이미 많은 표를 얻은 이전 답변보다 더 포괄적 으로이 질문에 답변했기 때문에 Andrew Arnott의 답변과 Pace의 답변이 일찍 게시되지 않은 것은 부끄러운 일입니다 .
Mark Booth

답변:


1135

짧은 버전

  • 병합은 한 브랜치의 모든 변경 사항을 한 커밋에서 다른 브랜치로 병합합니다.
  • Rebase는 내가 시작한 지점이 새로운 시작 지점으로 이동하기를 원한다고 말합니다.

그래서 언제 사용합니까?

병합

  • 단일 기능을 개발할 목적으로 브랜치를 생성했다고 가정하겠습니다. 이러한 변경 사항을 마스터로 되돌리려면 병합을 원할 것입니다 (모든 중간 커밋을 유지 관리하는 데 신경 쓰지 않아도 됨).

리베이스

  • 두 번째 시나리오는 일부 개발을 시작한 후 다른 개발자가 관련이없는 변경을 수행 한 경우입니다. 리포지토리에서 현재 버전의 변경 내용 을 가져 와서 리 베이스하여 변경 하려고 할 수 있습니다.

105
@Rob은 병합 할 때 임시 커밋을 유지하는 것을 언급했습니다. 기본적으로 브랜치 B (작업중 인 기능 브랜치)를 브랜치 M (마스터 브랜치)에 병합하면 두 분기 이후 B에서 이루어진 각 커밋마다 M에서 하나의 커밋이 생성됩니다. 그러나 --squash 옵션을 사용하여 병합하면 분기 B에서 수행 된 모든 커밋이 "함께 일괄 처리"되고 분기 M에서 단일 커밋으로 병합되어 마스터 분기의 로그를 깔끔하고 깨끗하게 유지합니다. 스 쿼싱은 수많은 개발자가 독립적으로 작업하고 마스터로 다시 병합하는 경우 원하는 것입니다.
spaaarky21

19
병합에 대한 @ spaaarky21의 가정이 올바르지 않다고 생각합니다. 분기 B를 마스터 M에 병합하는 경우 일반 병합 또는-스쿼시 병합 사용 여부에 관계없이 M에 단일 커밋이 있습니다 (B에 여러 커밋이있는 경우에도). 스쿼시가 할 일은 부모로서 B에 대한 참조를 제거하는 것입니다. 좋은 시각화는 여기에 있습니다 : syntevo.com/smartgithg/howtos.html?page=workflows.merge
jpeskin

14
@jpeskin 그것은 내가보고있는 것이 아닙니다. 방금 확인하기 위해 빠른 테스트를 수행했습니다. 텍스트 파일, init새 저장소, add파일 및commit . 새 기능 분기 체크 아웃 ( checkout -b feature.) 기능 분기 에 두 개의 새로운 커밋이 있도록 텍스트 파일을 변경하고 커밋하고 반복하십시오. 다음 checkout mastermerge feature. 에서가 log, 내 초기이 기능에서 통합 된이 다음에, 마스터에 커밋을 참조하십시오. 의 경우 merge --squash feature기능이 마스터로 병합되었지만 커밋되지 않았으므로 마스터에 대한 새로운 커밋은 내가 만든 것입니다.
spaaarky21

21
@ spaaarky21 우리 둘 다 맞아 보인다. 빨리 감기 병합이 가능한 경우 (예 에서처럼) git은 기본적으로 기능 분기 B에 모든 커밋을 포함하도록 설정합니다 (또는 제안한대로 --squash를 사용하여 단일 커밋으로 결합 할 수 있음). 그러나 병합하는 분기 분기 M과 B가 두 개있는 경우 git은 분기 M에서 병합 된 경우 (--squash 사용 여부) 모든 개별 커밋을 포함하지 않습니다.
jpeskin

6
왜이 답변에 "(임시 커밋을 유지하는 데 신경 쓰지 않습니까?)"라고 생각합니까? '09 년에는 말이되지 않았고 지금은 말이되지 않습니다. 또한 다른 개발자가 만든 경우에만 리베이스를 원할 것입니다. 필요한 관련 변경 을 수행 한 관련 이없는 변경을 수행 한 경우 기능 브랜치가 충돌없이 쉽게 병합되고 기록이 유지됩니다.
Mark Booth

372

간단 해. 리베이스를 사용하면 다른 브랜치를 작업의 새로운 기반 으로 사용한다고 말합니다 .

예를 들어 master분기가있는 경우 새 기능을 구현하기 위해 분기를 작성하고 이름을 지정하십시오 cool-feature. 물론 마스터 분기는 새 기능의 기본입니다.

이제 특정 지점에서 master지점 에서 구현 한 새 기능을 추가하려고합니다 . 지점으로 전환하여 master병합 할 수 있습니다 cool-feature.

$ git checkout master
$ git merge cool-feature

그러나이 방법으로 새로운 더미 커밋이 추가됩니다. 스파게티 역사를 피하려면 다음과 같이 리베이스하십시오 .

$ git checkout cool-feature
$ git rebase master

그런 다음에 병합하십시오 master.

$ git checkout master
$ git merge cool-feature

이번에는 토픽 브랜치가 동일한 마스터 커밋과 새로운 기능을 가진 커밋을 가지고 있기 때문에 병합은 빨리 진행됩니다.


31
but this way a new dummy commit is added, if you want to avoid spaghetti-history-어떻게 나빠?
ア レ ッ ク ス

6
또한 --no-ff merge 플래그는 매우 유용합니다.
Aldo 'xoen'Giambelluca

3
@ ア レ ッ ク ス는 사용자 Sean Schofield가 코멘트에 다음과 같이 말합니다 : "당신이 일단 UR 자료를 마스터로 다시 병합하면 (이미 설명 된 것처럼 사소한 일이지만) ur 커밋 기록의"맨 위 "에 앉아 있기 때문에 Rebase도 좋습니다. 기능이 작성되었지만 몇 주 후에 병합 된 프로젝트의 경우, 역사에서 마스터 방식으로 "채워 지므로"마스터로 병합하고 싶지 않습니다. 개인적으로 git log를 수행하고 볼 수있는 것이 좋습니다. 커밋 날짜는 그대로 유지됩니다. rebase는 해당 정보를 변경하지 않습니다. "
Adrien Be

4
이 모든 용어 (기억 - 나는 여기에 반복 곰 생각 merge, rebase, fast-forward, 등) 방향성 비순환 그래프의 특정 조작을 참조한다. 그들은 그 정신 모델을 염두에두고 추론하기가 더 쉬워집니다.
Roy Tinker

10
@Aldo 재기록 된 역사에 대해 "깨끗한"또는 "정돈 된"것은 없습니다. 실제로 무슨 일이 있었는지 전혀 모르기 때문에 일반적으로 더럽고 IMHO가 끔찍합니다. "가장 깨끗한"Git 이력은 실제로 발생한 것입니다. :)
Marnen Laibow-Koser

269

보완하기 위해 내 자신의 대답은 언급 TSamper에 의해 ,

  • 리베이스는 병합하기 전에 수행하는 것이 좋습니다. 아이디어는 지점 Y에서 B병합 할 지점 의 작업을 지점 에 통합하기 때문입니다.
    그러나 다시, 병합하기 전에, 당신은 어떤 분쟁 해결 하여 ( "의 지점에서 최근 시점부터 내 지점에서 내 작품을 재생으로,"REBASE "즉 지점 B).
    에 지점에서 제대로 후속 병합을 수행하는 경우 분기 B가 빨리 진행될 수 있습니다.

  • 병합은 대상 분기에 직접 영향을 B미치므로 병합이 더 낫다는 것을 의미합니다. 그렇지 않으면 분기 B가 안정 상태로 돌아 오기까지 시간이 오래 걸릴 수 있습니다 (모든 충돌을 해결할 시간)


리베이스 후 합병 점?

내가 설명하는 경우, 나는 B지점에 기반을 B두면서 최근 지점에서 작업을 다시 할 수있는 기회를 가지려고하지만 지점에 머무르는 동안.
이 경우에도 "재생 된"작업을 수행하려면 병합이 여전히 필요합니다.B .

다른 시나리오 ( 예 : Git Ready에서 설명 )는 작업을 B리베이스 를 통해 직접 가져 오는 것입니다 (모든 커밋을 보존하거나 대화식 리베이스를 통해 재주문 할 수있는 기회를 제공함).
이 경우 (B 브랜치에있는 동안 리베이스하는 경우) 맞습니다. 더 이상 병합 할 필요가 없습니다.

병합 또는 리베이스하지 않은 경우 기본적으로 Git 트리

rebase1

우리는 rebasing에 의해 얻을 :

rebase3

두 번째 시나리오는 모든 것입니다. 어떻게 새로운 기능을 마스터로 되돌릴 수 있습니까?

첫 번째 rebase 시나리오를 설명함으로써 나의 요점은 모든 사람들이 rebase를 예비 단계로 사용할 수 있음을 상기시키는 것입니다 ( "새로운 기능을 다시 마스터로 가져 오기").
rebase를 사용하여 먼저 새 기능 분기에 "마스터"를 마스터로 가져올 수 있습니다. rebase는에서 새 기능 커밋을 재생 HEAD master하지만 여전히 새 기능 분기에서 분기 시작점을 이전 마스터 커밋에서 효과적으로 이동시킵니다 HEAD-master.
이를 통해 지점의 모든 충돌을 해결할 수 있습니다 (충돌 해결 단계가 너무 오래 걸리면 마스터가 계속 병렬로 진행할 수 있음).
그런 다음 마스터 및 병합 new-feature(또는 리베이스 전환 할 수 있습니다new-featuremaster당신이 할 커밋을 유지하려는 경우new-feature 분기).

그래서:

  • "rebase vs. merge"는 저작물을 가져 오는 두 가지 방법으로 볼 수 있습니다 master.
  • 그러나 "rebase then merge"는 먼저 분리 된 충돌을 해결 한 다음 작업을 다시 가져 오는 올바른 워크 플로우 일 수 있습니다.

17
리베이스 후 병합은 충돌을 해결하지 않고 사소한 빨리 감기입니다.
obecalp 2009

4
@obelcap : 당신이 모든 문제 갈등을 : 사실,이 종류의 생각입니다 귀하의 환경 (REBASE의 새로운 기능을 갖춘 지점에서 마스터), 다음 공동 마스터 새로운 기능을 병합 : 1 피코 초 (이 빠른 앞으로) 마스터가 진화를하지 않은 경우
VonC

27
Rebase는 또한 일단 당신이 일단 물건을 다시 마스터로 병합하면 (이미 설명 된 것처럼 사소한) 커밋 히스토리의 "맨 위"에 있기 때문에 좋습니다. 기능이 작성 될 수 있지만 몇 주 후에 병합 된 더 큰 프로젝트에서는 이전에 마스터 방식으로 "채워지기"때문에 기능을 마스터로 병합하지 않으려 고합니다. 개인적으로 나는 git log를 수행하고 "top"에서 최근 기능을 볼 수있는 것을 좋아한다. 커밋 날짜는 유지됩니다. rebase는 해당 정보를 변경하지 않습니다.
Sean Schofield

3
@ 조 : 정신적으로, 당신은 "다른 지점 위에서 내 변경 사항을 (개인 지점에서 격리하여 수행하지만 재생이 완료되면 내 개인 지점에 남겨 두십시오)"라고 말합니다. "체크 포인트 커밋", 깨진 이등분 및 잘못된 비난 결과를 피하면서 지역 기록을 정리할 수있는 좋은 기회입니다. "Git 워크 플로우"참조 : sandofsky.com/blog/git-workflow.html
VonC

4
@scoaresco는 최신 업스트림 지점 에서 로컬 변경 사항이 어떻게 호환되는지 확인하는 것이 중요합니다 . 커밋 중 하나가 충돌을 일으키는 경우 즉시 볼 수 있습니다. 병합은 하나의 (병합 된) 커밋을 도입하는데, 이는 자신의 로컬 커밋 중 어느 것이 어떤 충돌을 추가했는지 쉽게 알 수있는 방법없이 많은 충돌을 유발할 수 있습니다. 따라서 더 명확한 히스토리 외에도 , 업스트림 브랜치 (하나의 단일 병합으로 덤프 됨)에 의해 도입 된 모든 변경 사항 과 반대로, 커밋으로 커밋 (리베이스에서 재생)으로 변경 사항 보다 정확하게 볼 수 있습니다 .
VonC

229

TL; DR

의심스러운 경우 merge를 사용하십시오.

짧은 답변

리베이스와 병합의 유일한 차이점은 다음과 같습니다.

  • 히스토리의 결과 트리 구조 (일반적으로 커밋 그래프를 볼 때만 눈에))는 다릅니다 (하나는 가지가 있고 다른 하나는 가지지 않습니다).
  • 병합은 일반적으로 추가 커밋 (예 : 트리의 노드)을 만듭니다.
  • 병합과 리베이스는 충돌을 다르게 처리합니다. Rebase는 병합이 한 번에 하나의 커밋을 표시하는 한 번에 한 번의 커밋을 나타냅니다.

따라서 짧은 대답은 당신의 역사가 어떻게 보이길 원하는지를 기반으로 리베이스 또는 병합선택하는 것 입니다.

긴 답변

사용할 작업을 선택할 때 고려해야 할 몇 가지 요소가 있습니다.

변경을 받고있는 지점이 팀 외부의 다른 개발자 (예 : 공개 소스, 공개)와 공유됩니까?

그렇다면 리베이스하지 마십시오. Rebase는 지사를 파괴하고 개발자가 사용하지 않는 한 해당 저장소가 손상되었거나 일관되지 않습니다 git pull --rebase. 이것은 다른 개발자들을 빠르게 화나게하는 좋은 방법입니다.

개발팀은 얼마나 능숙합니까?

Rebase는 파괴적인 작업입니다. 즉, 제대로 적용하지 않으면 커밋 된 작업이 손실되거나 다른 개발자 리포지토리의 일관성이 손상 될 수 있습니다.

저는 회사가 지점과 합병에 대한 전담 직원을 감당할 수있는 시점에서 개발자가 모두 온 팀에서 일했습니다. 그 개발자들은 Git에 대해 잘 모르고 많이 알고 싶지 않습니다. 이 팀에서 나는 어떤 이유로 든 rebasing을 추천 할 위험이 없습니다.

지점 자체가 유용한 정보를 나타 냅니까

일부 팀은 각 지점이 기능 (또는 버그 수정 또는 하위 기능 등)을 나타내는 기능별 지점 모델을 사용합니다.이 모델에서 지점은 관련 커밋 세트를 식별하는 데 도움이됩니다. 예를 들어, 해당 분기의 병합을 되돌려 서 피쳐를 신속하게 되돌릴 수 있습니다 (공평하게도 드문 작업입니다). 또는 두 가지를 비교하여 기능을 비교하십시오 (더 일반적). Rebase는 지점을 파괴하고 이것은 간단하지 않습니다.

또한 개발자 당 지점 모델을 사용하는 팀에서 일했습니다 (모두가 있었음). 이 경우 지점 자체는 추가 정보를 전달하지 않습니다 (커밋에는 이미 작성자가 있음). 리베이스에 아무런 해가 없습니다.

어떤 이유로 든 병합을 되 돌리시겠습니까?

취소 취소와 같이 리베이스를 되 돌리는 것은 병합을 되 돌리는 것과 비교하여 상당히 어렵거나 불가능합니다 (리베이스에 충돌이있는 경우). 되돌릴 가능성이 있다고 생각되면 되돌리기를 사용하고 병합을 사용하십시오.

팀에서 일하십니까? 그렇다면이 지점에 대해 전혀 또는 전혀 접근하지 않겠습니까?

Rebase 작업은 해당으로 가져와야합니다 git pull --rebase. 혼자서 작업하는 경우 적절한 시간에 사용해야하는 것을 기억할 수 있습니다. 팀에서 작업하는 경우 조정하기가 매우 어렵습니다. 이것이 대부분의 rebase 워크 플로우가 모든 병합 (및 git pull --rebase모든 풀)에 rebase를 사용하도록 권장하는 이유 입니다.

일반적인 신화

병합은 역사를 파괴합니다 (커밋을 과장)

다음과 같은 병합이 있다고 가정합니다.

    B -- C
   /      \
  A--------D

어떤 사람들은 병합이 커밋 히스토리를 "파괴"한다고 말하는데, 이는 마스터 브랜치 (A-D)의 로그 만 살펴보면 B와 C에 포함 된 중요한 커밋 메시지를 놓치기 때문입니다.

이것이 사실이라면 우리는 이와 같은 질문 이 없을 것 입니다. 기본적으로 B와 C를 보지 않으면 명시 적으로 보지 않는 한 (--first-parent 사용) B가 표시됩니다. 이것은 스스로 시도하기가 매우 쉽습니다.

Rebase를 사용하면보다 안전하고 간단한 병합이 가능합니다

두 가지 접근 방식은 서로 다르게 병합되지만 하나가 항상 다른 것보다 낫다는 것은 확실하지 않으며 개발자 워크 플로에 따라 달라질 수 있습니다. 예를 들어, 개발자가 정기적으로 커밋하는 경향이있는 경우 (예 : 직장에서 집으로 전환 할 때 하루에 두 번 커밋) 지정된 브랜치에 대해 커밋이 많이있을 수 있습니다. 이러한 커밋의 대부분은 최종 제품과 같이 보이지 않을 수도 있습니다 (기능마다 한두 번 내 접근 방식을 리팩터링하는 경향이 있습니다). 다른 사람이 관련 코드 영역에서 작업하고 있고 내 변경 사항을 리베이스하려고하면 상당히 지루한 작업 일 수 있습니다.

Rebase는 더 시원하고 더 섹시하고 전문적입니다

"시간 절약" 으로 별칭 rm을 지정 rm -rf하려면 rebase가 적합합니다.

내 두 센트

저는 언젠가 Git rebase가 문제를 해결하는 멋진 도구 인 시나리오를 보게 될 것이라고 생각합니다. Git reflog가 내 문제를 해결하는 멋진 도구 인 시나리오를 보게 될 것이라고 생각합니다. 나는 지금 5 년 이상 Git과 함께 일했다. 일어나지 않았습니다.

지저분한 역사는 실제로 나에게 문제가되지 않았습니다. 나는 신나는 소설처럼 내 커밋 역사를 읽지 않습니다. 어쨌든 Git 비난이나 Git bisect를 사용할 역사가 필요합니다. 이 경우 병합 커밋을 갖는 것이 실제로 유용합니다. 병합에서 문제가 발생하면 의미있는 정보가되기 때문입니다.

업데이트 (4/2017)

내 일반적인 조언은 여전히 ​​유효하지만 rebase 사용을 개인적으로 부드럽게했다고 언급 할 의무가 있습니다. 최근에 Angular 2 Material 프로젝트 와 많은 상호 작용을 해왔습니다 . 그들은 rebase를 사용하여 매우 깨끗한 커밋 기록을 유지했습니다. 이를 통해 커밋이 특정 결함을 수정 한 내용과 해당 커밋이 릴리스에 포함되었는지 여부를 매우 쉽게 확인할 수있었습니다. 리베이스를 올바르게 사용하는 좋은 예입니다.


5
검증 된 답변이어야합니다.
Mik378

이것이 가장 좋은 대답입니다. 특히 최신 업데이트에서 설명이 명확합니다. git history를 깨끗하고 깨끗하게 유지하는 데 도움이되지만 안전하게 사용하십시오.
zquintana

5
나는 주로이 답변을 좋아합니다. 그러나 Rebase는 "깨끗한"기록을 만들지 않습니다. 그것은 더 선형적인 역사를 만들어 냈지만, 각 커밋이 숨어있는 많은 "흙"을 누가 알기 때문에 그것은 전혀 같은 것이 아닙니다. 가장 깨끗하고 명확한 Git 히스토리는 분기를 유지하고 무결성을 유지하는 것입니다.
Marnen Laibow-Koser

3
"공통 신화, 당신은 커밋 B와 C를 참조하십시오": 반드시 !! 병합이 빨리 감기 병합 인 경우 실제로 B와 C 만 표시되며 충돌이없는 경우에만 가능합니다. 충돌이있는 경우 단일 커밋을받습니다! 그러나 먼저 마스터를 기능에 병합하고 충돌을 해결할 수 있으며, 그런 다음 기능을 마스터에 병합하면 커밋 B와 C가 커밋되고 마스터의 (첫 번째) 병합에서 히스토리 X 하나가 히스토리의 기능으로 병합됩니다.
Jeremy Benks '

아주 좋은 설명! 더 많이 투표해야합니다!
8

185

여기에 많은 답변이 병합하면 모든 커밋이 하나로 바뀌므로 커밋을 유지하기 위해 rebase를 사용하는 것이 좋습니다. 이것은 올바르지 않습니다. 그리고 당신이 이미 커밋을 푸시했다면 나쁜 생각 입니다.

병합은 커밋을 없애지 않습니다 . 병합은 역사를 보존합니다! Rebase는 기록을 다시 씁니다. 기록을 푸시 한 후 나쁜 일 입니다.

병합을 사용하십시오- 이미 푸시 할 때마다 리베이스하지 마십시오 .

여기 Linus '(Git의 저자)가 사용합니다 (현재 Wayback Machine에서 복구 한대로 내 블로그에서 호스팅 ). 정말 잘 읽었습니다.

또는 아래에서 동일한 아이디어의 내 버전을 읽을 수 있습니다.

마스터에서 브랜치를 리베이스 :

  • 커밋 생성 방법에 대한 잘못된 아이디어를 제공합니다.
  • 잘 테스트되지 않은 중간 커밋으로 마스터를 오염시킵니다.
  • 원래 토픽 브랜치가 생성 된 시점과 리베이스 된 시점 사이에 마스터로 변경된 변경 사항 때문에 실제로 이러한 중간 커밋에 빌드 중단을 도입 할 수 있습니다.
  • 마스터에서 좋은 장소를 찾기가 어렵습니다.
  • 커밋의 타임 스탬프가 트리에서 시간 순서대로 정렬되지 않습니다. 따라서 커밋 A가 마스터에서 커밋 B보다 우선하지만 커밋 B가 먼저 작성되었음을 알 수 있습니다. (뭐?!)
  • 토픽 브랜치의 개별 커밋은 각각 개별적으로 해결해야하는 병합 충돌 (각 커밋에서 발생한 일에 대한 히스토리에 있음)을 포함 할 수 있으므로 더 많은 충돌을 생성합니다.
  • 역사를 다시 쓴 것입니다. 리베이스중인 브랜치가 다른 곳으로 밀려 나면 (자신 이외의 다른 사람과 공유) 히스토리를 다시 쓴 이후 해당 브랜치를 가진 다른 모든 사람을 망쳤습니다.

반대로 주제 분기를 마스터로 병합 :

  • 마스터에서 토픽 브랜치로의 병합을 포함하여 토픽 브랜치가 작성된 위치의 히스토리를 보존합니다. 실제로 개발자가 빌드 할 때 어떤 코드를 사용했는지 정확하게 알 수 있습니다.
  • master는 주로 병합으로 구성된 지점이며, 이러한 병합 커밋 각각은 일반적으로 기록에서 안전한 '좋은 포인트'입니다.이 지점은 토픽 지점이 통합 될 준비가 되었기 때문입니다.
  • 토픽 브랜치에 있다는 사실을 포함하여 토픽 브랜치의 모든 개별 커밋이 유지되므로 이러한 변경을 분리하는 것은 자연스럽고 필요한 곳에서 드릴 할 수 있습니다.
  • 병합 충돌은 병합 시점에서 한 번만 해결하면되므로 토픽 브랜치에서 수행 된 중간 커밋 변경은 독립적으로 해결 될 필요가 없습니다.
  • 여러 번 부드럽게 할 수 있습니다. 토픽 브랜치를 주기적으로 마스터하도록 통합하면 사람들은 토픽 브랜치를 계속 구축 할 수 있으며 독립적으로 병합 될 수 있습니다.

3
또한 git merge에는 "--no-ff"(빨리 감기 안함) 옵션이있어 특정 병합에 의해 도입 된 모든 변경 사항을 정말 쉽게 되돌릴 수 있습니다.
Tiago

3
좀 더 명확하게 설명하면 : '이미 밀었을 때마다'상황을 참조하십시오. 대담해야합니다. Linus에 대한 링크 게시물은 훌륭합니다.
honzajde

2
그러나 PR을 통해 토픽 브랜치를 마스터로 병합하기 전에 (마스터가 아닌 브랜치의 충돌을 해결하기 위해) 마스터에서 토픽 브랜치로 "업데이트"하는 것이 가장 좋은 방법이 아닙니까? 우리는 대부분의 토픽 브랜치가 브랜치 마스터를 토픽으로 병합하는 마지막 커밋으로 사용하고 있습니다. 그러나 여기서 이것은 rebasing의 "기능"으로 나열되어 있으며 아무도 병합에 대해 언급하지 않습니다.
ProblemsOfSumit

2
@AndrewArnott "대부분의 토픽 브랜치는 충돌없이 대상 브랜치로 병합 할 수 있어야합니다."20 명의 개발자가 30 개의 브랜치를 작업 할 때 어떻게 가능합니까? 당신이 작업하는 동안 병합이있을 것입니다. 물론 PR을 만들기 전에 대상에서 주제 분기를 업데이트해야합니다 ... 아니요?
ProblemsOfSumit

3
일반적으로 @Sumit은 아닙니다. Git은 브랜치 중 하나 또는 둘 다에 변경 사항이 있더라도 어느 방향으로도 잘 병합 할 수 있습니다. 두 분기에서 동일한 코드 줄 (또는 매우 가까운)을 수정 한 경우에만 충돌이 발생합니다. 팀에서 자주 발생하는 경우 팀은 갈등을 해결하는 것이 세금이며 속도를 늦추기 때문에 업무를 어떻게 분배하는지 다시 생각해야합니다.
Andrew Arnott

76

TLDR : 그것은 가장 중요한 것에 달려있다-깔끔한 역사 또는 개발 순서의 진정한 표현

깔끔한 이력이 가장 중요한 경우 먼저 리베이스 한 다음 변경 사항을 병합하여 새 코드가 무엇인지 명확하게 알 수 있습니다. 이미 지사를 밀었다면 그 결과를 다룰 수 없다면 기지를 세우지 마십시오.

시퀀스의 진정한 표현이 가장 중요한 경우에는 리베이스하지 않고 병합합니다.

병합 의미 : 변경 사항을 대상으로 병합하는 새 커밋 하나를 만듭니다. 참고 : 이 새로운 커밋에는 두 개의 부모가 있습니다-커밋 문자열에서 최신 커밋과 병합중인 다른 지점의 최신 커밋.

Rebase 의미 : 현재 커밋 세트를 힌트로 사용하여 완전히 새로운 일련의 커밋을 만듭니다. 다시 말해, 내가 근거를 둔 지점에서 변경을 시작한 경우 내 변경 내용이 어떻게 보일지 계산하십시오. 리베이스 후, 변경 사항을 다시 테스트해야 할 수도 있고, 리베이스 중에 약간의 충돌이있을 수 있습니다.

이것이 주어진 이유는 무엇입니까? 개발 이력을 명확하게 유지하기 위해. 기능 X에서 작업 중이고 작업이 끝나면 변경 사항을 병합한다고 가정 해 봅시다. 대상에 "Added feature X"줄에 무언가를 나타내는 단일 커밋이 생겼습니다. 이제 병합하는 대신 리베이스 한 다음 병합하면 대상 개발 기록에 모든 개별 커밋이 단일 논리적 진행으로 포함됩니다. 이렇게하면 나중에 변경 사항을 훨씬 쉽게 검토 할 수 있습니다. 50 명의 개발자가 항상 다양한 기능을 통합하고 있다면 개발 이력을 검토하기가 얼마나 힘든지 상상해보십시오.

즉, 이미 업스트림에서 작업중인 지점을 푸시 한 경우 리베이스하지 말고 병합해야합니다. 업스트림으로 푸시되지 않은 브랜치의 경우 리베이스, 테스트 및 병합합니다.

리베이스하려는 또 다른 시간은 업스트림을 추진하기 전에 지점에서 커밋을 제거하려는 경우입니다. 예를 들어, 초기에 디버깅 코드를 소개하는 커밋과 해당 코드를 정리하는 다른 커밋이 있습니다. 이를 수행하는 유일한 방법은 대화식 리베이스를 수행하는 것입니다.git rebase -i <branch/commit/tag>

업데이트 : 또한 Git을 사용하여 비선형 히스토리를 지원하지 않는 버전 제어 시스템 ( 예 : Subversion) 에 인터페이스 할 때 rebase를 사용하려고합니다 . git-svn 브리지를 사용할 때 Subversion으로 병합하는 변경 사항은 트렁크의 가장 최근 변경 사항에 대한 순차적 인 변경 사항 목록이어야합니다. 이를 수행하는 방법은 두 가지뿐입니다. (1) 변경 사항을 수동으로 다시 작성하고 (2) rebase 명령을 사용하면 훨씬 빠릅니다.

업데이트 2 : rebase를 생각하는 또 다른 방법은 개발 스타일에서 커밋하는 리포지토리에 허용되는 스타일로 일종의 매핑을 가능하게한다는 것입니다. 작고 작은 덩어리로 커밋한다고 가정 해 봅시다. 오타를 고치려는 커밋과 사용되지 않은 코드 등을 제거하는 커밋이 있습니다. 해야 할 일을 마칠 때까지 일련의 커밋이 있습니다. 이제 커밋하는 리포지토리가 큰 커밋을 장려한다고 가정 해 봅시다. 따라서 수행중인 작업에 대해 하나 또는 두 개의 커밋이 필요합니다. 커밋 문자열을 어떻게 받아 예상대로 압축합니까? 대화식 리베이스를 사용하고 작은 커밋을 더 큰 덩어리로 스쿼시합니다. 스타일이 약간 큰 커밋 인 경우 반대가 필요한 경우에도 마찬가지입니다. 그러나 저장소에는 작은 커밋이 긴 문자열이 필요했습니다. 리베이스를 사용하여 그렇게 할 수도 있습니다. 대신 병합 한 경우 커밋 스타일을 기본 리포지토리에 이식했습니다. 개발자가 많은 경우 일정 시간이 지난 후 여러 가지 커밋 스타일로 역사를 따르는 것이 얼마나 어려운지 상상할 수 있습니다.

업데이트 3 : Does one still need to merge after a successful rebase?그렇습니다. 그 이유는 리베이스가 본질적으로 커밋의 "이동"을 수반하기 때문입니다. 위에서 말했듯이, 이러한 커밋은 계산되지만 분기 지점에서 14 개의 커밋이 있고 rebase에 아무런 문제가 없다고 가정하면 14 번 커밋이 진행됩니다 (당신이 기초를 두는 지점). 리베이스가 완료되었습니다. 리베이스 전에 지점이있었습니다. 이후에 같은 길이의 가지가 생깁니다. 변경 사항을 게시하기 전에 병합해야합니다. 다시 말해, 원하는만큼 여러 번 리베이스하십시오 (다시, 변경 사항을 업스트림으로 푸시하지 않은 경우에만). 리베이스 한 후에 만 ​​병합하십시오.


1
마스터와 병합하면 빨리 감을 수 있습니다. 기능 브랜치에는 약간의 버그가 있거나 컴파일하지 않는 커밋이있을 수 있습니다. 기능 분기에서 단위 테스트 만 수행하는 경우 통합시 일부 오류가 발생합니다. 마스터와 병합하기 전에 통합 테스트가 필요하며 일부 버그가 표시 될 수 있습니다. 이것이 수정되면 기능이 통합 될 수 있습니다. 버그가있는 코드를 마스터하기 위해 커밋하지 않으려면 모든 커밋이 빨리 진행되는 것을 막기 위해 리베이스가 필요합니다.
mbx

1
@mbx git merge--no-ff강제로 병합 커밋을 수행 하는 옵션을 지원합니다 .
Gavin S. Yancey

63

병합은 변경 사항을 통합하는 가장 쉽고 가장 일반적인 방법이지만 유일한 방법은 아닙니다. Rebase 는 대체 통합 방법입니다.

조금 더 잘 병합 이해

Git은 병합을 수행 할 때 세 가지 커밋을 찾습니다.

  • (1) 공통 조상 커밋. 프로젝트에서 두 분기의 히스토리를 따르는 경우 항상 하나 이상의 공통 커밋이 있습니다.이 시점에서 두 분기는 동일한 컨텐츠를 가졌으며 다르게 진화했습니다.
  • (2) + (3) 각 지점의 끝점. 통합의 목표는 두 지점의 현재 상태를 결합하는 것입니다. 따라서 각각의 최신 개정판에 특별한 관심이 있습니다. 이 세 가지 커밋을 결합하면 우리가 목표로하는 통합이 이루어집니다.

빨리 감기 또는 병합 커밋

아주 간단한 경우, 분기가 발생한 이후 두 분기 중 하나에 새로운 커밋이 없습니다. 최신 커밋은 여전히 ​​공통 조상입니다.

여기에 이미지 설명을 입력하십시오

이 경우 통합을 수행하는 것은 매우 간단합니다. Git은 공통 상위 커밋 위에 다른 분기의 모든 커밋을 추가 할 수 있습니다. Git에서이 가장 간단한 통합 형태를 "빨리 감기"병합이라고합니다. 두 가지 모두 정확히 같은 역사를 공유합니다.

여기에 이미지 설명을 입력하십시오

그러나 많은 경우에 두 지점이 개별적으로 전진했습니다.

여기에 이미지 설명을 입력하십시오

통합을하려면 Git은 차이점을 포함하는 새로운 커밋을 만들어야합니다 (병합 커밋).

여기에 이미지 설명을 입력하십시오

인간 커밋 및 병합 커밋

일반적으로 커밋은 인간에 의해 신중하게 만들어집니다. 관련 변경 사항 만 래핑하고 주석으로 주석을 달 수있는 의미있는 단위입니다.

병합 커밋은 약간 다릅니다. 개발자가 만드는 대신 Git에서 자동으로 만듭니다. 관련 변경 사항을 래핑하는 대신 매듭처럼 두 가지를 연결하는 것이 목적입니다. 나중에 병합 조작을 이해하려면 분기 및 해당 커밋 그래프의 히스토리를 살펴 봐야합니다.

Rebase와 통합

어떤 사람들은 그러한 자동 병합 커밋없이 가기를 선호합니다. 대신, 그들은 프로젝트의 역사가 마치 하나의 직선으로 진화 한 것처럼 보이기를 원합니다. 어떤 지점에서 여러 지점으로 분할되었다는 표시는 없습니다.

여기에 이미지 설명을 입력하십시오

리베이스 작업을 단계별로 살펴 보겠습니다. 시나리오는 이전 예제와 동일합니다. 우리는 브랜치 B에서 브랜치 A로 변경 사항을 통합하려고하지만 이제 rebase를 사용합니다.

여기에 이미지 설명을 입력하십시오

우리는 세 단계로 이것을 할 것입니다

  1. git rebase branch-A // Synchronises the history with branch-A
  2. git checkout branch-A // Change the current branch to branch-A
  3. git merge branch-B // Merge/take the changes from branch-B to branch-A

먼저, Git은 분기가 시작된 후 (공통 상위 커밋 이후) 발생한 A 분기의 모든 커밋을 "실행 취소"합니다. 그러나 물론, 그것들은 버리지 않을 것입니다. 대신에 당신은 그 커밋들을 "일시적으로 저장되었다"고 생각할 수 있습니다.

여기에 이미지 설명을 입력하십시오

다음으로, 통합하려는 분기 B의 커밋을 적용합니다. 이 시점에서 두 가지 모두 똑같이 보입니다.

여기에 이미지 설명을 입력하십시오

마지막 단계에서 브랜치 A의 새 커밋이 이제 다시 적용되지만 브랜치 B의 통합 커밋 위에 새 위치에 있습니다 (다시 기반으로 함).

결과는 개발이 직선으로 일어난 것처럼 보입니다. 모든 결합 된 변경 사항을 포함하는 병합 커밋 대신 원래 커밋 구조가 유지되었습니다.

여기에 이미지 설명을 입력하십시오

마지막으로 원치 않는 자동 생성 커밋이없는 깨끗한 브랜치 브랜치 A 를 얻습니다 .

참고 : 멋진에서 촬영 포스트 에 의해 git-tower. 단점 의이 rebase같은 게시물 읽기 좋다.


매우 멋진 다이어그램의 경우 +1 나는 항상 운이없는 비슷한 방법으로 git flow 예제를 설명하고 싶었습니다.
Mikayil Abdullayev

60

병합 / 리베이스하기 전에 :

A <- B <- C    [master]
^
 \
  D <- E       [branch]

git merge master:

A <- B <- C
^         ^
 \         \
  D <- E <- F

git rebase master:

A <- B <- C <- D' <- E'

(A, B, C, D, E 및 F는 커밋입니다)

Git에 대한이 예제와 훨씬 더 잘 설명 된 정보는 Git The Basics Tutorial 에서 찾을 수 있습니다. .


30

이 문장은 그것을 얻는다 :

일반적으로 두 세계를 최대한 활용하는 방법은 자신이 만든 로컬 변경 사항을 리베이스 (rebase)하는 것입니다. 그러나 스토리를 정리하기 위해 푸시하기 전에 아직 공유하지는 않았지만 어딘가에 푸시 한 것을 리베이스하지 마십시오. .

출처 : 3.6 Git Branching-Rebasing, Rebase vs. Merge


25

이 답변은 Git Flow를 중심으로 광범위하게 진행 됩니다. 테이블은 nice ASCII Table Generator 로 생성 되었으며이 멋진 명령을 사용하여 히스토리 트리 ( 별명git lg )는 다음 과 같습니다.

git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

테이블은 히스토리 트리와보다 일관되게 역순으로 정렬됩니다. 첫 번째 git mergegit merge --no-ff첫 번째 의 차이점도 참조하십시오 (일반적으로 git merge --no-ff역사가 현실에 더 가깝게 보이 도록 사용하려고합니다 ).

git merge

명령 :

Time          Branch "develop"             Branch "features/foo"
------- ------------------------------ -------------------------------
15:04   git merge features/foo
15:03                                  git commit -m "Third commit"
15:02                                  git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

결과:

* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
|           Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
|           Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge --no-ff

명령 :

Time           Branch "develop"              Branch "features/foo"
------- -------------------------------- -------------------------------
15:04   git merge --no-ff features/foo
15:03                                    git commit -m "Third commit"
15:02                                    git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

결과:

*   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/            Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge vs git rebase

첫 번째 요점 : 항상 기능을 개발에 병합하고 기능에서 개발을 리베이스하지 마십시오 . 이것은 Rebasing의 황금률의 결과입니다 .

황금률 git rebase공공 지점 에서 절대 사용하지 않는 것 입니다.

:

당신이 어딘가에 밀었던 것을 리베이스하지 마십시오.

저는 개인적으로 다음과 같이 덧붙 입니다. 기능 지점이 아니고 귀하와 귀하의 팀이 결과를 인식하지 않는 한 .

따라서 git mergevs 의 문제 git rebase는 거의 피처 브랜치에만 적용됩니다 (다음 예제에서는 --no-ff병합 할 때 항상 사용되었습니다). 더 나은 솔루션이 있는지 확실하지 않기 때문에 ( 토론이 존재 함 ) 두 명령의 작동 방식 만 제공합니다. 내 경우 git rebase에는 더 멋진 기록 트리를 생성하므로 사용 하는 것이 좋습니다. :)

기능 분기 간

git merge

명령 :

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

결과:

*   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\            Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | |           Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | |           Fourth commit - Christophe
* | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \            Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

명령 :

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git rebase features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

결과:

*   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

에서 develop기능 지점에

git merge

명령 :

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git merge --no-ff develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

결과:

*   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\            Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | |           Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ /            Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

명령 :

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git rebase develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

결과:

*   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
*   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\            Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

사이드 노트

git cherry-pick

하나의 특정 커밋이 필요할 때 git cherry-pick좋은 해결책입니다 (이 -x옵션 은 원래 커밋 메시지 본문에 " (체리에서 선택되었습니다 ...) " 행을 추가 하므로 일반적으로 사용하는 것이 좋습니다.git log <commit_sha1> 보고 그것):

명령 :

Time           Branch "develop"              Branch "features/foo"                Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git cherry-pick -x <second_commit_sha1>
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

결과:

*   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| |           Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git pull --rebase

나는 Derek Gourlay 보다 더 잘 설명 할 수 있는지 잘 모르겠습니다 ... 기본적으로 git pull --rebase대신 git pull:을 사용 하십시오.) 기사에서 누락 된 것은 기본적으로 활성화 할 수 있다는 것입니다 .

git config --global pull.rebase true

git rerere

다시 여기에 잘 설명되어 있습니다 . 그러나 간단히 말하면, 활성화하면 더 이상 동일한 충돌을 여러 번 해결할 필요가 없습니다.


15

프로 힘내 책은에 정말 좋은 설명이 리베이스 페이지를 .

기본적으로 병합은 두 개의 커밋을 수행하고 결합합니다.

리베이스는 두 사람의 공통 조상으로 가서 서로 위에 변경 사항을 점차적으로 적용합니다. 이것은 더 깨끗하고 선형적인 역사를 만듭니다.

그러나 리베이스하면 이전 커밋을 포기하고 새 커밋을 만듭니다. 따라서 공용 저장소를 리베이스하지 마십시오. 저장소에서 일하는 다른 사람들이 당신을 미워할 것입니다.

그런 이유로 나는 거의 독점적으로 합병합니다. 내 지점의 99 %가 그다지 다르지 않으므로 충돌이있는 경우 한두 곳에서만 발생합니다.


1
병합은 커밋을 결합하지 않습니다. 기록을 다시 작성하는 것입니다. Rebase는 그렇게합니다.
kellyfj 2016 년

4

Git rebase는 히스토리 정리 및 저장소 구조의 분기 경로를 선형으로 만드는 데 사용됩니다.

또한 변경 사항을 서버에 적용하고 푸시 한 후 서버에 변경 사항을 적용한 후 분기를 삭제하면 작업 한 분기에 대한 증거가 없으므로 사용자가 만든 분기를 비공개로 유지하는 데 사용됩니다. 따라서 지사는 이제 지역의 관심사입니다.

리베이스를 수행 한 후 정상적인 병합을 수행하는지 확인하는 추가 커밋도 제거합니다.

예, rebase 명령은 rebase 중에 언급 한 브랜치 위에 작업을 배치하고 master와 같이 브랜치의 첫 번째 커밋을 마스터 브랜치의 직계 자손으로 만들기 때문에 여전히 성공적인 rebase 후에 병합을 수행해야합니다. . 이는 이제이 분기에서 마스터 분기로 변경 사항을 가져 오기 위해 빨리 감기 병합을 수행 할 수 있음을 의미합니다.


4

개발자가 한 명인 경우 병합 대신 rebase 를 사용 하여 명확한 기록을 가질 수 있습니다.

여기에 이미지 설명을 입력하십시오


3

Gerrit가 대규모 개발과 다소 관련이있는 실제 사례 가 검토 및 전달 통합에 사용되는 :

기능 분기를 새로운 원격 마스터로 향상시킬 때 병합합니다. 이것은 최소한의 향상 작업을 제공하고 예를 들어 gitk 와 같은 기능 개발의 역사를 쉽게 따라갈 수 있습니다.

git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature

배달 커밋을 준비 할 때 병합합니다.

git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master

배달 커밋이 어떤 이유로 든 통합에 실패하면 리베이스하고 새로운 원격 마스터로 업데이트해야합니다.

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master

3

rebase와 merge가 무엇인지 여러 번 설명되었지만 언제 무엇을 사용해야합니까?

언제 rebase를 사용해야합니까?

  • 지점을 밀지 않았을 때 / 다른 사람이 작업하지 않습니다.
  • 당신은 전체 역사를 원합니다
  • 자동으로 생성 된 "merged .."커밋 메시지를 모두 피하려고합니다.

Git 리베이스가 역사를 바꾼다. 따라서 다른 사람이 같은 지점에서 작업 할 때 / 밀어 넣은 경우에는 사용하지 마십시오. 그러나 로컬 지점이있는 경우 분기를 다시 마스터로 병합하기 전에 병합 리베이스 마스터를 수행하여 더 깨끗한 기록을 유지할 수 있습니다. 이렇게하면 마스터 브랜치에 병합 한 후에는 마스터 브랜치에서 브랜치를 사용한 것을 볼 수 없습니다. "머지 .."를 자동 생성하지는 않았지만 기록은 "깨끗합니다". "merged .."커밋을 자동 생성하지 않고 마스터 브랜치의 전체 기록.

그러나 git merge feature-branch --ff-only기능을 기본으로 병합 할 때 단일 커밋을 작성하는 데 충돌이 없는지 확인 하는 데 사용하십시오. 기능 분기의 히스토리를 가져올 때 작업하는 모든 태스크에 대해 기능 분기를 사용하지만 "병합 된 .."커미트가 아닌 경우 이는 흥미 롭습니다.

두 번째 시나리오는 지점에서 분기하고 기본 지점에서 변경된 사항을 알고 자하는 경우입니다. Rebase는 모든 단일 커밋을 포함하므로 정보를 제공합니다.

언제 병합을 사용해야합니까?

  • 지점을 밀었을 때 / 다른 사람들도 그 지점에서 일하고 있습니다.
  • 당신은 전체 역사가 필요하지 않습니다
  • 단순히 합치는 것만으로도 충분합니다

마스터 브랜치에 피처 브랜치의 모든 기록이 필요하지 않거나 원하는 경우 또는 다른 브랜치가 동일한 브랜치에서 작업중인 경우 / 추천했습니다. 여전히 히스토리를 유지하려면 기능 분기를 마스터로 병합하기 전에 마스터를 기능 분기로 병합하십시오. 그러면 마스터에 기능 분기 히스토리 (마스터를 병합하여 기능 분기에 있던 병합 커미트 포함)가있는 곳에서 빨리 감기 병합이 수행됩니다.


-4

언제 사용 git rebase합니까? 역사를 다시 쓰기 때문에 거의 절대로. git merge프로젝트에서 실제로 일어난 일을 존중하기 때문에 거의 항상 선호하는 선택입니다.


1
@benjaminhull 감사합니다! — 내 대답이 사실에 근거하기를 바랍니다. 이럴 의견은 이런 종류의 작은 장소가 있습니다 : 그것은의 사실 실제 역사를 잃고 나중에 어렵게 만드는 것이다.
Marnen Laibow-Koser 2016 년

1
동의하다. (당신이 당신의 밀어 커밋 리베이스하는 경우) 병합 등 손상된 역사로 이어질하지 않습니다
나 서프 라이더를
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.