programing

다른 Git Merge 전략을 언제 사용하시겠습니까?

skycolor 2023. 5. 22. 20:53
반응형

다른 Git Merge 전략을 언제 사용하시겠습니까?

git-merge의 man 페이지에서 사용할 수 있는 병합 전략은 여러 가지가 있습니다.

  • 해결 - 3방향 병합 알고리즘을 사용하여 두 개의 헤드(즉, 현재 분기와 풀한 다른 분기)만 해결할 수 있습니다.교차 병합 모호성을 주의 깊게 탐지하려고 하며 일반적으로 안전하고 빠른 것으로 간주됩니다.

  • 재귀 - 3방향 병합 알고리즘을 사용하여 두 개의 헤드만 확인할 수 있습니다.3방향 병합에 사용할 수 있는 공통 조상이 둘 이상 있는 경우 공통 조상의 병합 트리를 만들고 이 트리를 3방향 병합에 대한 참조 트리로 사용합니다.이로 인해 Linux 2.6 커널 개발 기록에서 가져온 실제 병합 커밋에 대해 수행된 테스트를 통해 잘못 병합되지 않고 병합 충돌이 더 적게 발생하는 것으로 보고되었습니다.또한 이름 변경과 관련된 병합을 탐지하고 처리할 수 있습니다.이것은 분기 하나를 당기거나 병합할 때의 기본 병합 전략입니다.

  • 문어 - 머리가 두 개 이상인 경우는 해결되지만 수동 해결이 필요한 복잡한 병합은 거부합니다.이 도구는 주로 주제 분기 헤드를 함께 묶는 데 사용됩니다.이것은 둘 이상의 분기를 풀하거나 병합할 때의 기본 병합 전략입니다.

  • 우리 - 헤드 수가 제한되지 않지만 병합 결과는 항상 현재 분기 헤드입니다.사이드 브랜치의 오래된 개발 이력을 대체하기 위해 사용됩니다.

  • 하위 트리 - 수정된 재귀 전략입니다.트리 A와 B를 병합할 때 B가 A의 하위 트리에 해당하는 경우 B는 먼저 트리를 동일한 수준에서 읽는 대신 A의 트리 구조에 맞게 조정됩니다.이 조정은 공통 조상 트리에도 수행됩니다.

기본값과 다른 항목을 지정해야 하는 경우는 언제입니까?각 시나리오가 가장 적합한 시나리오는 무엇입니까?

저는 Resolve에 익숙하지 않지만, 다른 것들을 사용해 보았습니다.

재귀적

재귀는 비고속 병합의 기본값입니다.우리는 모두 그것에 익숙합니다.

문어.

저는 병합이 필요한 나무가 여러 개 있을 때 문어를 사용했습니다.이는 대규모 프로젝트에서 볼 수 있습니다. 대규모 프로젝트에서는 많은 지사가 독립적으로 개발을 진행하고 있으며 모든 지사가 하나의 헤드로 통합될 준비가 되어 있습니다.

문어 가지는 깨끗하게 할 수 있는 한 하나의 커밋에서 여러 개의 머리를 병합합니다.

예를 들어 마스터가 있는 프로젝트가 있고 병합할 세 개의 분기(a, b 및 c라고 함)가 있다고 가정합니다.

일련의 재귀 병합은 다음과 같습니다(재귀를 강제로 수행하지 않았기 때문에 첫 번째 병합은 빠르게 진행되었습니다).

series of recursive merges

그러나 단일 문어 병합은 다음과 같습니다.

commit ae632e99ba0ccd0e9e06d09e8647659220d043b9
Merge: f51262e... c9ce629... aa0f25d...

octopus merge

우리 것

우리들의 == 나는 다른 머리를 끌어들이고 싶지만, 그 머리가 도입하는 모든 변화를 버립니다.

이렇게 하면 분기의 영향 없이 분기의 기록이 유지됩니다.

(읽음: 그것은 심지어 그 가지들 사이의 변화를 살펴보지도 않았습니다.분기가 병합될 뿐 파일에는 아무 작업도 수행되지 않습니다. 때 "마다 "우리 파일 버전"을 할 수 .git merge -X ours)

하위 트리

하위 트리는 다른 프로젝트에서 현재 프로젝트의 하위 디렉터리로 병합하려는 경우 유용합니다.하위 모듈로 포함하지 않을 라이브러리가 있을 때 유용합니다.

실제로 분기별 변경 사항을 포기하고 분기를 기록에 유지하고 독립 프로젝트를 슈퍼 프로젝트의 하위 디렉터리(예: 'git' 저장소의 'git-gui')에 병합하려면 하위 트리를 선택해야 합니다.

문어 병합은 세 개 이상의 분기를 병합할 때 자동으로 사용됩니다.해결은 주로 과거의 이유로, 그리고 재귀적 병합 전략 코너 사례에 의해 타격을 받는 경우를 위해 여기에 있습니다.

"해결" 대 "재귀적" 병합 전략

Recursive는 현재 기본적인 두 헤드 전략이지만, 검색을 통해 "해결" 병합 전략에 대한 몇 가지 정보를 발견했습니다.

(아마존)이 있는 오라일리 책 버전 컨트롤에서 발췌한 것(문절):

원래 Gitmerges의 기본 전략은 "해결"이었습니다.

둘 이상의 병합 기준이 있을 수 있는 교차 병합 상황에서 해결 전략은 다음과 같이 작동합니다. 가능한 병합 기준 중 하나를 선택하고 최상의 결과를 기대하십시오.이것은 실제로 들리는 것만큼 나쁘지 않습니다.사용자들이 코드의 다른 부분에 대해 작업해 온 것으로 밝혀지는 경우가 많습니다.이 경우 Git는 이미 적용된 일부 변경 사항을 다시 적용하고 있음을 감지하고 중복된 변경 사항을 건너뛰어 충돌을 방지합니다.또는 이러한 사소한 변경이 충돌을 유발하는 경우에는 적어도 개발자가 처리하기 쉬운 충돌이어야 합니다.

기본 재귀 전략에서 실패한 "해결"을 사용하여 트리를 성공적으로 병합했습니다.나는 받고 있었습니다.fatal: git write-tree failed to write a tree오류, 그리고 이 블로그 게시물(미러) 덕분에 "-sresolve"를 시도했는데 효과가 있었습니다.왜 그런지는 아직도 잘 모르겠어요...하지만 두 트리 모두에 중복된 변경 사항이 있어서 제대로 "건너진" 문제를 해결했기 때문이라고 생각합니다.

Git 2.30(2021년 1분기)과 함께 새로운 합병 전략인 ORT("표면적으로 재귀적인 쌍둥이")가 있을 것입니다.

git merge -s ort

이것은 엘리야 뉴렌의 실에서 나온 것입니다.

지금은, 저는 그것을 "표면적으로 재귀적인 쌍둥이" 또는 줄여서 "ort"라고 부르고 있습니다.> 처음에는 사람들이 그것과 현재의 재귀적 전략 사이에 어떤 차이도 눈치채지 못할 것입니다. 제가 생각하기에 (특히 큰 저장소의 경우) 조금 더 빨리 만들 수 있을 것 같다는 것 외에는요.

그러나 현재 설계에서 다루기 어려운 (인정하는 코너 케이스) 버그를 수정할 수 있어야 하며, 터치하지 않는 병합이 가능하다고 생각합니다.$GIT_WORK_TREE또는$GIT_INDEX_FILE몇 가지 재미있는 새로운 기능을 사용할 수 있습니다.
어쨌든 그것이 희망입니다.

문제:

이상적인 세계에서 우리는 다음을 수행해야 합니다.

  • 물어보다unpack_trees()하기 위해 "read-tree -m 없이" -u";

  • 모든 병합-결합 계산을 코어 내에서 수행하고 현재 인덱스를 그대로 유지하면서 결과 인덱스를 준비합니다.

  • 현재 코어 내 지수와 결과 코어 내 지수를 비교하고, 작업 트리에서 추가, 업데이트 또는 제거해야 하는 경로를 확인하고, 변경 사항이 작업 트리에 반영될 때 정보 손실이 없는지 확인합니다.
    예를 들어, 결과는 현재 작업 트리에 확장 불가능한 내용이 있는 디렉터리가 있는 파일을 만들고자 하며, 결과는 작업 트리 파일이 로컬 수정된 파일을 제거하려고 합니다.
    그리고 마지막으로

  • 작업 트리 업데이트를 수행하여 결과 핵심 인덱스의 모양과 일치하도록 합니다.

결과:

커밋 14c4586(2020년 11월 2일), 커밋 fe1a21d(2020년 10월 29일), 커밋 47b1e89, 커밋 17e5574(2020년 10월 27일)를 Elija Newren()newren 참조.
(Junio C Hamano에 의해 합병되었습니다 -- -- -- commit a9595, 2020년 11월 18일)

merge-ort빈 구현을 포함한 새로운 병합 전략의 베어본 API

사인 오프 바이: 엘리야 뉴렌

이것이 새로운 병합 전략의 시작입니다.

API의 차이와 동작의 차이는 있지만, 이는 근본적으로 의 대체 기능으로 사용됩니다.

그러나, 그것은 병합-재귀와 나란히 존재하도록 구축되어 있어서 우리는 사람들이 여전히 병합-재귀로 후퇴할 수 있는 동안 이러한 차이가 실제 세계에서 어떻게 발생하는지 알 수 있는 많은 시간을 가지고 있습니다.
(또한 이 과정에서 merge-recursive를 안정적으로 유지하기 위해 수정하는 것을 피하고 싶습니다.)

여기서 주목할 수 있는 주요 차이점은 작업 트리와 인덱스의 업데이트가 병합 알고리즘과 동시에 수행되는 것이 아니라 별도의 후처리 단계라는 것입니다.
새로운 API는 반복적인 병합(예: 리베이스 또는 체리픽 중)을 수행하고 중간 결과마다 업데이트하는 대신 마지막에 한 번만 인덱스와 작업 트리를 업데이트할 수 있도록 설계되었습니다.

또한 인덱스 또는 작업 트리를 방해하지 않고 인덱스 또는 작업 트리와 일치하지 않는 두 분기 간의 병합을 수행할 수 있습니다.

그리고:

커밋 848a856, 커밋 fd15863, 커밋 23bef2e, 커밋 c8c35f6, 커밋 c12d1f2, 커밋 727c75b, 커밋 489c85f, 커밋 ef52778, 커밋 f06481f(newren2020년 10월 26일)를 참조하십시오.
(주니오 C 하마노에 의해 합병 -- -- 커밋 66c62ea, 2020년 11월 18일)

더티 파일을 사용하여 개선 또는 처리를 t6423, t6436기록합니다.

사인 오프 바이: 엘리야 뉴렌

"재사용적" 백엔드는unpack_trees()단계별 변경사항을 병합에 의해 덮어쓰는지 확인합니다.unpack_trees()이름 변경을 이해하지 못합니다. 일단 반환되면 이미 작업 트리 및 색인에 많은 업데이트를 기록했습니다.
따라서 "재귀적"은 특별한 4방향 병합을 수행해야 했습니다. 여기서 작업 복사본을 추가적인 차이점 소스로 취급해야 했습니다. 이 경우 덮어쓰기를 조심스럽게 방지하고 충돌을 방지하기 위해 파일을 새 위치로 이동해야 했습니다.

대조적으로 "ort" 백엔드는 전체를 메모리에서 병합하고 사후 처리 단계로 인덱스와 작업 복사본만 업데이트합니다.
잘못된 파일이 있으면 병합을 중단할 수 있습니다.

t6423Ort 백엔드에서 향상된 충돌 마커 레이블 기대

사인 오프 바이: 엘리야 뉴렌

충돌 마커에는 REF-OR-COMMIT:FILENAME 형식의 추가 주석이 포함되어 있어 컨텐츠가 어디서 왔는지 구분하는 데 도움이 됩니다.:FILENAME기록의 양쪽 모두에 대해 동일한 경우 조각이 생략됩니다(내용이 충돌하는 이름만 주석의 해당 부분을 전달함).

하지만, 몇몇의 경우들이 있었습니다.:FILENAME모든 특수 대/소문자 코드의 복사본 형식으로 인해 병합-수정의 모든 코드 경로로 인해 주석이 실수로 중단되었습니다.

ort 백엔드에서 이름 변경/삭제 처리가 개선될 것으로 t6404, t6423예상

사인 오프 바이: 엘리야 뉴렌

파일 이름이 변경되고 내용이 충돌할 때, merge-recursive는 인덱스에서 이전 파일 이름에 대한 일부 단계와 새 파일 이름에 대한 일부 단계를 가지지 않고, 대신 이전 파일 이름에 해당하는 모든 단계를 새 파일 이름에 대한 해당 위치로 복사합니다.새 파일 이름에 모두 해당하는 세 개의 고차 단계가 있습니다.

이러한 방식으로 작업을 수행하면 사용자가 다른 버전에 쉽게 액세스하고 충돌을 해결할 수 있습니다. 이전 버전과 git add(man)새 버전을 수동으로 git rm''(man)할 필요가 없습니다.

이름 바꾸기/변경은 유사하게 처리해야 합니다. 이름을 바꾼 파일에는 하나의 단계가 아니라 두 단계가 있어야 합니다.
지금 당장 병합-재귀성을 불안정하게 만들고 싶지 않으므로, 대신 관련 테스트를 업데이트하여 "" 여부에 따라 다른 기대치를 갖도록 합니다.recursive또는 "ort병합 전략이 사용 중입니다.


Git 2.30(2021년 1분기)과 함께, 새로운 합병 전략을 준비합니다.

커밋 848a856, 커밋 fd15863, 커밋 23bef2e, 커밋 c8c35f6, 커밋 c12d1f2, 커밋 727c75b, 커밋 489c85f, 커밋 ef52778, 커밋 f06481f(newren2020년 10월 26일)를 참조하십시오.
(주니오 C 하마노에 의해 합병 -- -- 커밋 66c62ea, 2020년 11월 18일)

향상된 디렉토리/파일 충돌 처리를 merge tests기대합니다.

사인 오프 바이: 엘리야 뉴렌

merge-recursive.c 운영이라는 아이디어를 바탕으로 구축되었습니다.unpack_trees()그리고 나서 결과를 얻기 위해 "작은 터치업"을 합니다.
불행하게도,unpack_trees()즉, 즉시 업데이트 모드로 실행되어 결과적으로 즉각적인 평가와 사용자에 맞게 수정 설계를 수행할 수 있습니다.

디렉터리/파일 충돌과 같은 일부 항목은 인덱스 데이터 구조에서 잘 나타나지 않으며 처리하기 위해 특별한 추가 코드가 필요합니다.
그러나 이름 바꾸기/삭제 충돌이 디렉터리/파일 충돌에도 관련될 수 있다는 것이 발견되면 특수 디렉터리/파일 충돌 처리 코드를 이름 바꾸기/삭제 코드 경로에 복사해야 했습니다.
...그리고 수정/삭제, 이름 변경/수정(1-2) 충돌을 위해 복사해야 했습니다. 하지만 아직 일부가 누락되었습니다.
또한 파일/서브모듈 충돌과 서브모듈/디렉토리 충돌이 발견되었을 때, 코드베이스 전체의 모든 특수한 경우에 특수한 서브모듈 처리 코드를 복사해야 했습니다.

그리고 디렉터리/파일 충돌을 처리하는 것이 최선이 아니라는 것을 알게 되었습니다. 이는 충돌하는 파일의 내용을 저장하기 위해 추적되지 않은 파일을 생성하기 때문입니다. 이 파일은 누군가 " 또는 "git rebase --abort(man)git merge --abort(man)실행할 경우 정리되지 않습니다.

또한 인덱스에서 디렉터리/파일 충돌이 발생한 경우 이러한 파일에 해당하는 인덱스 항목을 추가하거나 제거하는 것이 어렵거나 두려웠습니다.
그러나 코드에 디렉토리/파일/서브모듈 충돌을 처리하기 위해 유사하지만 동일하지 않은 코드를 가진 사이트가 너무 많아서 이러한 문제를 올바르게 처리하기 위해 변경하는 것은 번거로운 일이었습니다.

저는 모든 디렉토리/파일/서브모듈 충돌 처리를 merge-ort 방식으로 단일 코드 경로를 통해 수행하고 추적된 콘텐츠를 저장하기 위해 추적되지 않은 파일을 만들지 않기 위해 노력했습니다.


Git 2.31(2021년 1분기)에서는 병합 백엔드 "done right"가 등장하기 시작합니다.
예:

주니오 C 하마노()gitster의 커밋 6d37ca2(2020년 11월 11일)를 참조하십시오.
커밋 89422d2, 커밋 ef2b369, 커밋 70912f6, 커밋 6681ce5, 커밋 9fefece6, 커밋 4012d, 커밋 9945bb, 커밋 8adffa, 커밋 6a02ddd9, 커밋 291f29c, 커밋 98bf984, 커밋 34e557a, 커밋 885f006, 커밋 0commit commit 0c1999, commit 0805, commit 171, commit 23171, commit 23171, commit 23Elija Newren()newren의해 5b59c3d(2020년 12월 13일)를 커밋합니다.
(주니오 C 하마노에 의해 합병 -- -- 2021년 1월 6일 커밋 f9d29da에서)

의 실행을 merge-ort추가합니다.record_conflicted_index_entries()

사인 오프 바이: 엘리야 뉴렌

끝나고checkout()작업 트리에 적절한 내용이 있고 색인이 작업 복사본과 일치합니다.
즉, 수정되지 않은 모든 파일과 새로 병합된 파일의 인덱스 항목은 올바르지만 충돌하는 항목은 업데이트해야 합니다.

충돌된 항목을 루프하여 경로에 대한 기존 인덱스 항목을 표시함으로써 이 작업을 수행합니다.CE_REMOVE인덱스 끝에 경로에 대해 준비된 새로운 상위 순서 추가(일반 인덱스 정렬 순서 포함), 그리고 루프 끝에서 제거CE_REMOVED-marked항목을 캐시하고 인덱스를 정렬합니다.


Git 2.31(2021년 1분기)에서는 이름 바꾸기 탐지가 "ORT" 병합 전략에 추가되었습니다.

커밋 6fcccbd, 커밋 f1665e6, 커밋 35e47e3, 커밋 2e91ddd, 커밋 53e88a0, 커밋 af1e56c(2020년 12월 15일), 커밋 c2d267d, 커밋 965a7bc, 커밋 f39d05c, 커밋 1a124e, 864075e(newren2020년 12월 14일)을 참조하십시오.
(주니오 C 하마노에 의해 합병 -- -- 2021년 1월 25일 커밋 2856089에서)

예:

merge-ort일반 이름 변경 처리 구현 추가

사인 오프 바이: 엘리야 뉴렌

일반 이름 변경 처리를 구현합니다.
이 코드는 다음을 대체합니다.merge-recurisve.c:

  • 에 관련된 코드RENAME_NORMALprocess_renames()
  • RENAME_NORMAL의 경우.process_entry()

또한 이 경우(또는 다른 이름 바꾸기 사례)에는 더 이상 필요하지 않은 여러 다른 이름 바꾸기 사례에 대한 의 공유 코드가 있습니다.

  • handle_rename_normal()
  • setup_rename_conflict_info()

4개의 개별 코드 경로를 하나로 통합하는 것은 설계 변경에 의해 가능합니다.process_renames()을 조정합니다.conflict_info내의 출품작.opt->priv->paths할 정도로process_entry()그러면 에서는 모든 충돌 유형(디렉토리/파일, 수정/삭제 등)을 또는 전체적으로 처리할 수 있습니다.

이는 충돌 유형의 특정 조합에 대한 특별한 구현을 놓칠 가능성이 훨씬 적다는 것을 의미합니다(66c62ea("Merge branch 'en/merge-tests', 2020-11-18, Git v2.30.0-rc0 -- merge listed in batch #6). 특히 commit52778("Merge tests: 개선된 디렉토리/파일 충돌 처리를 예상", 2020-10-26, 2020-26).Git v2.30.0-rc0 -- 자세한 내용은 배치 #6)에 나열된 병합.

즉, 워크트리/인덱스 업데이트를 에서 직교적으로 처리할 수 있습니다.merge_switch_to_result()다양한 특수 이름 변경 사례의 코드를 획기적으로 간소화하는 기능입니다.

(공정하게 말하면, 일반적인 이름 변경을 처리하는 코드는 이전에는 그렇게 복잡하지 않았지만, 지금은 훨씬 더 간단합니다.)

또한 Git 2.31(Q1 2021), Git 2.31(Q1 2021)에서도 oRT 병합 전략은 병합 충돌에 대한 더 많은 지원을 학습합니다.

커밋 4ef88fc, 커밋 4204cd5, 커밋 70f19c7, 커밋 c73cda7, 커밋 f591c47, 커밋 62fdec1, 커밋 991bdc, 커밋 5a1a1e8, 커밋 23366d2, 커밋 0ccfa4e(newren20년 1월 1일)를 참조하십시오.
(주니오 C 하마노에 의해 합병 -- -- b65b9ff, 2021년 2월 5일 커밋)

merge-ort동일한 경로에 있는 다른 유형의 파일에 대한 처리 추가

사인 오프 바이: 엘리야 뉴렌

다음 유형의 충돌을 명시적으로 고려하는 처리를 추가합니다.

  • 파일/파일 모듈
  • 파일/심링크
  • submodule/symlink> 같은 경로에 충돌로 남겨두면 사용자가 해결하기 어려우므로 둘 중 하나 또는 둘 다 옆으로 이동하여 각자의 경로를 얻을 수 있습니다.

재귀적 처리의 경우(즉, 재귀적 처리의 경우)에 유의하십시오.
call_depth > 0), 충돌 수정/삭제, 이진 파일, 충돌하는 하위 모듈 값 등과 같이 두 병합 기준의 병합 기준을 병합 결과로 사용할 수 있습니다.

언급URL : https://stackoverflow.com/questions/366860/when-would-you-use-the-different-git-merge-strategies

반응형