'git merge'는 세부적으로 어떻게 작동합니까?


96

'git merge'뒤에있는 정확한 알고리즘 (또는 그 근처)을 알고 싶습니다. 최소한 다음 하위 질문에 대한 답변이 도움이 될 것입니다.

  • git은 충돌하지 않는 특정 변경의 컨텍스트를 어떻게 감지합니까?
  • git은 이러한 정확한 줄에 충돌이 있음을 어떻게 알 수 있습니까?
  • git 자동 병합 기능은 무엇입니까?
  • 브랜치 병합을위한 공통 기반이 없을 때 git은 어떻게 수행합니까?
  • 브랜치를 병합하기위한 공통 기반이 여러 개인 경우 git은 어떻게 수행합니까?
  • 한 번에 여러 분기를 병합하면 어떻게됩니까?
  • 병합 전략의 차이점은 무엇입니까?

그러나 전체 알고리즘에 대한 설명이 훨씬 더 좋습니다.


8
나는 ... 당신이이 답변이 전체 책을 채울 수 추측
다니엘 Hilgarth에게

2
아니면 그냥 가서 코드를 읽을 수 있습니다. "전체 알고리즘을 설명하는 것"만큼 오래 걸립니다
Nevik Rehnel 2013

3
@DanielHilgarth 어딘가에 이미 그런 책이 있다면 기뻐할 것입니다. 참조를 환영합니다.
abyss.7 2013

5
@NevikRehnel 예, 할 수 있습니다. 그러나 누군가 이미이 코드의 이론을 알고 있다면 훨씬 쉬워 질 수 있습니다.
abyss.7 2013

1. "특정 충돌하지 않는 변경의 맥락"이란 무엇입니까? 포인트 2와 3은 동일하지만 부정적입니다.이 두 질문을 병합 해 보겠습니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

답변:


65

3 방향 병합 알고리즘에 대한 설명을 찾는 것이 가장 좋습니다. 높은 수준의 설명은 다음과 같습니다.

  1. 적합한 병합 기반을 찾으십시오 B-두 새 버전 ( XY) 의 조상 인 파일의 버전 과 일반적으로 가장 최근의 해당 기반 (다음 중 하나 인 더 뒤로 돌아 가야하는 경우도 있음) git의 기본 recursive병합 기능 )
  2. 의 차이점 수행 XBY와를 B.
  3. 두 개의 diff에서 식별 된 변경 블록을 살펴보십시오. 양측이 동일한 지점에서 동일한 변경 사항을 도입하면 둘 중 하나를 수락합니다. 한 사람이 변경 사항을 도입하고 다른 사람이 해당 지역을 떠나면 최종 변경 사항을 도입하십시오. 둘 다 지점에서 변경 사항을 도입했지만 일치하지 않는 경우 충돌을 수동으로 해결하도록 표시하십시오.

전체 알고리즘은이를 훨씬 더 자세히 다루며 일부 문서 ( https://github.com/git/git/blob/master/Documentation/technical/trivial-merge.txt for one, with the git help XXXpages)도 있습니다. XXX는 중 하나입니다 merge-base, merge-file, merge, merge-one-file그리고 아마도 몇 가지 다른). 충분히 깊지 않으면 항상 소스 코드가 있습니다.


11

브랜치를 병합하기위한 공통 기반이 여러 개인 경우 git은 어떻게 수행합니까?

이 기사는 매우 유용했습니다 : http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html (여기 2 부 ).

Recursive는 diff3을 재귀 적으로 사용하여 조상으로 사용될 가상 분기를 생성합니다.

예 :

(A)----(B)----(C)-----(F)
        |      |       |
        |      |   +---+
        |      |   |
        |      +-------+
        |          |   |
        |      +---+   |
        |      |       |
        +-----(D)-----(E)

그때:

git checkout E
git merge F

2 개의 가장 좋은 공통 조상 (다른 조상이 아닌 공통 조상)이 C있으며 D. Git은이를 새로운 가상 브랜치로 병합 V한 다음 V기본으로 사용 합니다.

(A)----(B)----(C)--------(F)
        |      |          |
        |      |      +---+
        |      |      |
        |      +----------+
        |      |      |   |
        |      +--(V) |   |
        |          |  |   |
        |      +---+  |   |
        |      |      |   |
        |      +------+   |
        |      |          |
        +-----(D)--------(E)

나는 Git이 더 좋은 공통 조상이 있다면 계속해서 V다음 조상과 병합한다고 생각 합니다.

이 기사는 가상 분기 Git을 생성하는 동안 병합 충돌이 발생하면 충돌 마커를 그대로두고 계속한다고 말합니다.

한 번에 여러 분기를 병합하면 어떻게됩니까?

@Nevik Rehnel이 설명했듯이 전략에 따라 다르며 man git-merge MERGE STRATEGIES섹션에서 잘 설명됩니다 .

예를 들어 한 번에 여러 분기 병합 만 octopusours/ theirs지원 recursive하지 않습니다.

octopus충돌이있을 경우 병합을 거부 ours하고 사소한 병합이므로 충돌이 없을 수 있습니다.

이 명령은 새 커밋을 생성하면 상위가 2 개 이상입니다.

내가 한 짓 merge -X octopus이 어떻게되는지에 충돌없이 힘내 1.8.5에.

초기 상태 :

   +--B
   |
A--+--C
   |
   +--D

동작:

git checkout B
git merge -Xoctopus C D

새로운 상태 :

   +--B--+
   |     |
A--+--C--+--E
   |     |
   +--D--+

예상대로 E3 명의 부모가 있습니다.

TODO : 문어가 단일 파일 수정에 대해 정확히 어떻게 작동하는지. 재귀 적 2x2 3 방향 병합?

브랜치 병합을위한 공통 기반이 없을 때 git은 어떻게 수행합니까?

@Torek는 2.9 이후로 병합이 --allow-unrelated-histories.

Git 1.8.5에서 경험적으로 시도했습니다.

git init
printf 'a\nc\n' > a
git add .
git commit -m a

git checkout --orphan b
printf 'a\nb\nc\n' > a
git add .
git commit -m b
git merge master

a 포함 :

a
<<<<<<< ours
b
=======
>>>>>>> theirs
c

그때:

git checkout --conflict=diff3 -- .

a 포함 :

<<<<<<< ours
a
b
c
||||||| base
=======
a
c
>>>>>>> theirs

해석:

  • 베이스가 비어있다
  • 기본이 비어 있으면 단일 파일에서 수정 사항을 해결할 수 없습니다. 새 파일 추가와 같은 것만 해결할 수 있습니다. 위의 충돌은 a\nc\n단일 라인 추가 로베이스와의 3 방향 병합에서 해결됩니다.
  • 내가 생각하는 기본 파일이없는 3 방향 병합 그냥 DIFF 인 2 웨이 병합이라는 것을

1
이 질문에 대한 새로운 SO 링크가 있으므로이 답변을 살펴 보았고 (아주 좋습니다) 최근 Git 변경 사항이 마지막 섹션이 약간 오래되었음을 알았습니다. Git 버전 2.9 (commit e379fdf34fee96cd205be83ff4e71699bdc32b18) 부터 .NET 을 추가하지 않는 한 병합 기반이없는 경우 Git은 이제 병합을 거부합니다 --allow-unrelated-histories.
torek

1
다음은 @Ciro가 게시 한 기사의 후속 기사입니다. blog.plasticscm.com/2012/01/…
adam0101

마지막으로 시도한 이후로 동작이 변경되지 않은 --allow-unrelated-histories경우 병합하려는 분기간에 공통 파일 경로가 없으면 생략 할 수 있습니다.
Jeremy List

작은 수정 : ours병합 전략이 있지만 theirs병합 전략이 없습니다 . recursive+ theirs전략은 두 개의 분기 만 해결할 수 있습니다. git-scm.com/docs/git-merge#_merge_strategies
nekketsuuu

9

나도 관심이 있습니다. 답은 모르겠지만 ...

작동하는 복잡한 시스템은 작동하는 단순한 시스템에서 진화 한 것으로 항상 밝혀졌습니다.

나는 git의 병합이 매우 정교하고 이해하기 매우 어려울 것이라고 생각합니다. 그러나 이에 접근하는 한 가지 방법은 그 선구자로부터 관심의 핵심에 초점을 맞추는 것입니다. 즉, 공통 조상이없는 두 개의 파일이 주어지면 git merge가 어떻게 파일을 병합하는지, 충돌이있는 위치를 파악합니까?

몇 가지 선구자를 찾아 보자. 에서 git help merge-file:

git merge-file is designed to be a minimal clone of RCS merge; that is,
       it implements all of RCS merge's functionality which is needed by
       git(1).

wikipedia에서 : http://en.wikipedia.org/wiki/Git_%28software%29- > http://en.wikipedia.org/wiki/Three-way_merge#Three-way_merge- > http : //en.wikipedia .org / wiki / Diff3- > http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf

마지막 링크는 diff3알고리즘을 자세히 설명하는 문서의 PDF입니다 . 다음은 google pdf-viewer 버전 입니다. 길이가 12 페이지에 불과하고 알고리즘은 몇 페이지에 불과하지만 완전한 수학적 처리입니다. 너무 형식적으로 보일 수 있지만 git의 병합을 이해하려면 먼저 간단한 버전을 이해해야합니다. 아직 확인하지 않았지만 같은 이름으로 diff3diff ( 가장 긴 공통 하위 시퀀스 알고리즘 사용)를 이해해야 할 것입니다 . 그러나 diff3Google이 있다면 더 직관적 인 설명이있을 수 있습니다 .


이제 저는 diff3과 비교 실험 을했습니다 git merge-file. 그들은 같은 3 개 개의 입력 파일을 가지고 버전 1 oldversion 버전 2 로, 그리고 마크 충돌하는 방식은 동일 <<<<<<< version1, =======, >>>>>>> version2( diff3도있다 ||||||| oldversion), 공통의 유산을 보여주는.

나는에 대한 빈 파일을 사용 oldversion 등에 대한 거의 동일한 파일 버전 1버전 2 단지 하나의 추가 라인에 추가 된 버전 2 .

결과 : git merge-file변경된 단일 라인이 충돌로 식별되었습니다. 그러나 diff3두 파일 전체를 충돌로 취급했습니다. 따라서 diff3처럼 정교하고 git의 병합은 가장 단순한 경우에도 훨씬 더 정교합니다.

실제 결과는 다음과 같습니다 (텍스트에 @twalberg의 답변을 사용했습니다). 필요한 옵션을 확인합니다 (해당 맨 페이지 참조).

$ git merge-file -p fun1.txt fun0.txt fun2.txt

You might be best off looking for a description of a 3-way merge algorithm. A
high-level description would go something like this:

    Find a suitable merge base B - a version of the file that is an ancestor of
both of the new versions (X and Y), and usually the most recent such base
(although there are cases where it will have to go back further, which is one
of the features of gits default recursive merge) Perform diffs of X with B and
Y with B.  Walk through the change blocks identified in the two diffs. If both
sides introduce the same change in the same spot, accept either one; if one
introduces a change and the other leaves that region alone, introduce the
change in the final; if both introduce changes in a spot, but they don't match,
mark a conflict to be resolved manually.
<<<<<<< fun1.txt
=======
THIS IS A BIT DIFFERENT
>>>>>>> fun2.txt

The full algorithm deals with this in a lot more detail, and even has some
documentation (/usr/share/doc/git-doc/technical/trivial-merge.txt for one,
along with the git help XXX pages, where XXX is one of merge-base, merge-file,
merge, merge-one-file and possibly a few others). If that's not deep enough,
there's always source code...

$ diff3 -m fun1.txt fun0.txt fun2.txt

<<<<<<< fun1.txt
You might be best off looking for a description of a 3-way merge algorithm. A
high-level description would go something like this:

    Find a suitable merge base B - a version of the file that is an ancestor of
both of the new versions (X and Y), and usually the most recent such base
(although there are cases where it will have to go back further, which is one
of the features of gits default recursive merge) Perform diffs of X with B and
Y with B.  Walk through the change blocks identified in the two diffs. If both
sides introduce the same change in the same spot, accept either one; if one
introduces a change and the other leaves that region alone, introduce the
change in the final; if both introduce changes in a spot, but they don't match,
mark a conflict to be resolved manually.

The full algorithm deals with this in a lot more detail, and even has some
documentation (/usr/share/doc/git-doc/technical/trivial-merge.txt for one,
along with the git help XXX pages, where XXX is one of merge-base, merge-file,
merge, merge-one-file and possibly a few others). If that's not deep enough,
there's always source code...
||||||| fun0.txt
=======
You might be best off looking for a description of a 3-way merge algorithm. A
high-level description would go something like this:

    Find a suitable merge base B - a version of the file that is an ancestor of
both of the new versions (X and Y), and usually the most recent such base
(although there are cases where it will have to go back further, which is one
of the features of gits default recursive merge) Perform diffs of X with B and
Y with B.  Walk through the change blocks identified in the two diffs. If both
sides introduce the same change in the same spot, accept either one; if one
introduces a change and the other leaves that region alone, introduce the
change in the final; if both introduce changes in a spot, but they don't match,
mark a conflict to be resolved manually.
THIS IS A BIT DIFFERENT

The full algorithm deals with this in a lot more detail, and even has some
documentation (/usr/share/doc/git-doc/technical/trivial-merge.txt for one,
along with the git help XXX pages, where XXX is one of merge-base, merge-file,
merge, merge-one-file and possibly a few others). If that's not deep enough,
there's always source code...
>>>>>>> fun2.txt

이것에 진정으로 관심이 있다면 약간의 토끼 구멍입니다. 나에게 그것은 정규 표현식, diff 의 가장 긴 공통 하위 시퀀스 알고리즘, 문맥 자유 문법 또는 관계 대수 만큼 깊어 보입니다 . 당신이 그것의 바닥에 도달하고 싶다면, 나는 당신이 할 수 있다고 생각하지만 결정적인 연구가 필요할 것입니다.



0

git은 충돌하지 않는 특정 변경의 컨텍스트를 어떻게 감지합니까?
git은 이러한 정확한 줄에 충돌이 있음을 어떻게 알 수 있습니까?

병합의 양쪽에서 동일한 선이 변경된 경우 충돌입니다. 그렇지 않은 경우 한쪽 (존재하는 경우)의 변경이 수락됩니다.

git 자동 병합 기능은 무엇입니까?

충돌하지 않는 변경 사항 (위 참조)

브랜치를 병합하기위한 공통 기반이 여러 개인 경우 git은 어떻게 수행합니까?

Git merge-base 정의에 따라 단 하나 (최신 공통 조상) 만 있습니다.

한 번에 여러 분기를 병합하면 어떻게됩니까?

이는 병합 전략에 따라 다릅니다 ( octopusours/ theirs전략 만 두 개 이상의 분기 병합을 지원함).

병합 전략의 차이점은 무엇입니까?

이것은 git merge맨 페이지에 설명되어 있습니다.


2
'같은 선'은 무엇을 의미합니까? 다른 두 줄 사이에 비어 있지 않은 새 줄을 삽입하고 병합하면 어떤 줄이 동일합니까? 한 분기에서 일부 행을 삭제하면 다른 분기에서 '동일한'행이 무엇입니까?
abyss.7 2013

1
텍스트로 대답하는 것은 약간 까다 롭습니다. Git은 [diffs] (en.wikipedia.org/wiki/Diff)를 사용하여 두 파일 (또는 파일의 두 개정판) 간의 차이를 표현합니다. 컨텍스트 (기본적으로 3 줄)를 비교하여 줄이 추가 또는 제거되었는지 감지 할 수 있습니다. "동일한 행"은 추가 및 삭제를 염두에두고 컨텍스트를 의미합니다.
Nevik Rehnel 2013

1
"동일한 줄"변경이 충돌을 나타낼 것이라고 제안합니다. 자동 병합 엔진은 실제로 라인 기반입니까? 아니면 덩어리 기반입니까? 공통 조상이 하나뿐입니까? 그렇다면 왜 git-merge-recursive존재합니까?
Edward Thomson

1
@EdwardThomson : 예, 해상도는 라인 기반입니다 (한 줄만 남을 때까지 덩크를 더 작은 덩어리로 나눌 수 있음). 기본 병합 전략은 최신 공통 조상을 참조로 사용하지만 다른 것을 사용하려는 경우 다른 것이 있습니다. 그리고 나는 무엇 git-merge-recursive을 해야할지 모르겠습니다 (맨 페이지가 없으며 Google은 아무것도 산출하지 않습니다). 이에 대한 자세한 정보는 git mergegit merge-baseman 페이지 에서 찾을 수 있습니다 .
Nevik Rehnel 2013

1
git-merge매뉴얼 페이지 및 git-merge-base당신이 지적하는 다수의 공통 조상과 재귀 병합을 논의 할 것으로 매뉴얼 페이지를 참조하십시오. 나는 당신의 대답이 그러한 논의 없이는 불완전하다고 생각합니다.
Edward Thomson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.