Skip to content
This page has been auto-translated and may contain errors.View in English

브랜치 병합 및 충돌 해결

docs.scrimba.com

기능 브랜치가 완성되었습니다. 토글이 작동하고, 변경 사항을 커밋했으며, 이제 팀의 나머지 코드가 있는 main에 그 작업을 통합하고 싶습니다. 대부분의 경우 이를 가져오는 것은 빠르고 조용합니다. Git이 두 브랜치를 비교하고, 겹치는 부분이 없음을 발견하고, 추가 단계 없이 작업을 통합합니다. 그러나 때때로 당신과 팀원이 다른 브랜치에서 정확히 같은 줄을 변경하면, Git이 어느 버전을 유지할지 추측할 수 없습니다. 이 상황은 두 명 이상의 사람이 커밋하는 거의 모든 프로젝트에서 나타납니다. 이는 브랜칭의 일반적인 일상적 부분이며, Git이 같은 줄에 대해 두 가지 아이디어를 발견했으며 어느 것이 우선인지 판단해 달라는 것을 의미합니다.

git merge로 브랜치 되돌리기

add-fahrenheit-toggle이라는 자신만의 브랜치에서 화씨 토글을 만들었고, main이 브랜칭 이후 이동하지 않았다고 가정해봅시다. main으로 전환하고 가져올 브랜치 이름과 함께 git merge를 실행합니다:

bash
$ git switch main
$ git merge add-fahrenheit-toggle
Updating 4f2a891..8c3d1a0
Fast-forward
 src/index.js | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

git merge <branch>는 항상 명명된 브랜치를 현재 있는 브랜치로 병합하므로, 먼저 main으로 전환하는 것이 중요합니다. 당신이 있는 브랜치가 병합을 받는 브랜치입니다. 이것을 실행한 후, mainadd-fahrenheit-toggle의 모든 커밋을 가지고 있습니다. 기능 브랜치 자체는 건드려지지 않습니다. 이제 계속 작업하거나 그 작업이 main에도 있으므로 삭제할 수 있습니다.

출력의 Fast-forward 줄을 자세히 읽을 가치가 있습니다. 이는 main이 당신이 브랜칭한 이후 새로운 커밋을 얻지 못했으므로, Git이 아무것도 결합할 필요가 없었다는 의미입니다. main 레이블을 add-fahrenheit-toggle이 이미 가리키고 있는 같은 커밋을 가리키도록 전진시켰습니다. **빠른 전진 병합(fast-forward merge)**은 Git이 레이블을 이미 있어야 할 곳까지 따라잡는 것일 뿐이고, 그 이상이 아닙니다.

당신이 작업하는 동안 main이 다른 커밋을 받았다면, 예를 들어 팀원이 버그 수정을 병합했다면, Git은 더 이상 레이블을 전진시킬 수 없습니다. 왜냐하면 당신이 나뉜 이후로 main과 당신의 브랜치가 다른 방향으로 진행되었기 때문입니다. 대신 두 히스토리를 함께 묶는 새로운 커밋을 만들고, 이를 병합 커밋이라고 부릅니다:

bash
$ git switch main
$ git merge add-fahrenheit-toggle
Merge made by the 'ort' strategy.
 src/index.js | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

이런 병합 후 git log --graph --oneline은 포크와 조인을 보여줍니다. 두 개의 히스토리 라인이 나란히 실행되고, 병합 커밋에서 다시 만납니다. 어느 결과도 당신에게 추가적인 것을 요구하지 않습니다. Git은 브랜치가 분기되었는지 여부에 따라 어느 것이 적용되는지 결정하고, 어느 쪽이든 당신의 기능은 이제 main의 일부입니다.

위의 두 결과 모두 같은 기본 아이디어에 기반합니다: 3-way 병합(3-way merge). 두 브랜치를 결합하기 위해, Git은 두 팁보다 더 많은 것을 비교합니다. 먼저 두 브랜치가 공통 시작점으로 공유하는 커밋인 병합 기반을 찾은 다음 세 개의 스냅샷을 봅니다: 기반과 각 브랜치의 팁입니다. 공유 기반에 대해 각 팁을 비교하면 Git이 각 측면이 변경한 정확한 줄을 알 수 있고, 이것이 매번 당신에게 묻는 대신 두 세트의 편집을 자동으로 결합할 수 있게 합니다.

빠른 전진은 세 개의 스냅샷 중 하나가 중복이 되는 경우입니다: 기반과 한 팁이 같은 커밋이므로, 그 측면에서 결합할 것이 없고, Git이 새로운 것을 만들지 않고 레이블을 이동합니다. 병합 커밋은 일반적인 경우입니다. 여기서 양쪽이 기반 이후 뭔가를 변경했고, Git이 두 개의 부모를 가진 새로운 스냅샷을 기록합니다. 하나는 각 브랜치의 히스토리로 다시 가리킵니다. 다음 섹션에서 만나는 모든 병합 충돌은 이 같은 3-way 비교에서 나오며, 양쪽이 같은 줄을 건드렸지만 자체적으로 결합할 방법이 없습니다.

Junogit merge로 브랜치 되돌리기git merge branch-name은 그 브랜치의 커밋을 현재 있는 브랜치로 가져오므로, 먼저 main으로 전환하십시오. 대부분의 병합은 백그라운드에서 조용히 일어나고 당신은 계속 진행됩니다. 기능 브랜치 자체에 대해 아무것도 변하지 않습니다. 계속 사용하거나 main이 작업을 가지고 있으면 삭제할 수 있습니다.
Junogit merge로 브랜치 되돌리기 빠른 전진은 main이 이동하지 않았다는 의미이므로, Git은 레이블을 전진시키기만 합니다. main이 그 사이에 다른 커밋을 받으면, 병합은 대신 두 개의 부모를 가진 실제 병합 커밋을 만듭니다. 어느 쪽이든 명령은 같습니다 git merge branch-name. Git이 어떤 종류의 병합이 적합한지 결정합니다.
Junogit merge로 브랜치 되돌리기 3-way 병합은 공유 기본 커밋을 각 브랜치 팁과 비교하여 각 측면에서 뭐가 변경되었는지 파악하고, 이것이 대부분의 경우 두 브랜치를 결합하는 것을 자동으로 만듭니다. 빠른 전진은 기본과 한 팁이 이미 일치하는 특수한 경우입니다. 나는 여전히 지저분한 그래프에서 어느 커밋이 기본으로 계산되는지 파악하기 위해 일시 정지하고 있으므로, 확실하지 않으면 그려내십시오.

병합 충돌의 모양

충돌은 병합의 양쪽에서 같은 줄이 변경되고 Git이 어느 버전을 원하는지 알 수 있는 방법이 없을 때 발생합니다. 팀원이 main에 로딩 스피너를 병합했고 src/index.js에서 함수를 변경했고, 당신의 add-fahrenheit-toggle 브랜치가 같은 함수를 변경하여 토글을 추가했다고 합시다. 지금 병합하면 완료하는 대신 중간에 중단됩니다:

bash
$ git switch main
$ git merge add-fahrenheit-toggle
Auto-merging src/index.js
CONFLICT (content): Merge conflict in src/index.js
Automatic merge failed; fix conflicts and then commit the result.

이것은 **병합 충돌(merge conflict)**입니다. 파일을 열면 Git이 두 버전이 정확히 어디서 불일치하는지 표시했음을 알 수 있습니다:

<<<<<<< HEAD
  showLoadingSpinner(true);
=======
  const tempF = celsiusToFahrenheit(tempC);
>>>>>>> add-fahrenheit-toggle

<<<<<<< HEAD는 현재 당신의 브랜치(main의 경우)에 있는 것의 시작을 표시합니다. =======는 두 버전을 나눕니다. >>>>>>> add-fahrenheit-toggle은 들어오는 브랜치 버전의 끝을 표시합니다. 마커 사이의 모든 것은 같은 몇 줄이지만 두 가지 다른 방식으로 작성되었습니다.

충돌은 Git이 당신의 판단이 필요하다는 의미입니다. 아무것도 깨지지 않았습니다. Git이 같은 줄에 대한 두 가지 변경을 발견했고 어느 것을 원하는지 추측할 방법이 없으므로, 일시 정지하고 결정하도록 요청합니다. 파일을 편집할 때까지 한 버전, 양쪽 또는 그것들을 결합하는 새로운 것을 유지하는 방식으로 읽도록 한 다음 마커 줄 자체를 삭제합니다:

  showLoadingSpinner(true);
  const tempF = celsiusToFahrenheit(tempC);

파일이 올바르게 보이면, 다른 변경과 정확히 같게 스테이징하고 커밋합니다:

bash
$ git add src/index.js
$ git commit

메시지 없이 git commit을 실행하면 Git이 이미 준비한 병합을 설명하는 메시지로 편집기가 열립니다. 저장하고 닫으면 병합이 완료됩니다.

충돌이 열려 있는 동안, git status는 먼저 확인할 가치가 있습니다. 이것은 "Unmerged paths" 아래에서 주의가 필요한 모든 파일을 나열하므로, 여러 충돌 파일이 있는 더 큰 병합에서 정확히 얼마나 많은 파일을 수정해야 하는지 알 수 있습니다:

bash
$ git status
On branch main
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   src/index.js

git add는 Git에 파일의 충돌이 해결되었음을 알립니다. 실제로 마커를 제거했는지 확인하지 않으므로, 스테이징하기 전에 파일을 다시 읽으십시오. 병합이 예상보다 지저분해 보이거나 잘못된 브랜치를 선택한 경우, git merge --abort는 완전히 백아웃하고 작업 디렉토리를 git merge를 실행하기 전의 모양으로 반환하고, 반쯤 완성된 것은 남기지 않습니다.

충돌은 git merge에 고유하지 않습니다. **Rebase(리베이스)**는 한 줄의 히스토리를 다른 줄로 최신 상태로 가져오는 다른 방법이며, 해결된 후 다른 결과를 가진 같은 종류의 충돌을 겪습니다. 병합은 두 부모를 가진 병합 커밋으로 두 히스토리를 함께 묶고, 브랜치가 존재했다는 사실과 정확히 언제 다시 결합되었는지를 보존합니다. 리베이스는 대신 당신의 브랜치의 커밋을 한 번에 하나씩 다른 브랜치의 팁에 재생하여, 병합 커밋 없이 히스토리의 직선을 생성하고 두 개가 분기되었다는 흔적이 없습니다:

bash
$ git switch add-fahrenheit-toggle
$ git rebase main

트레이드오프는 실제입니다. 리베이스의 선형 히스토리는 git log에서 깨끗하게 읽고, 한 커밋 다음 다른 커밋, 깔끔한 이야기를 선호하는 일부 팀. 병합은 어떤 일이 일어났는지의 진정한 모양을 유지하고, 기능 브랜치가 main에 다시 결합되는 정확한 지점을 포함하며, 정확성을 선호하는 일부 팀. 어느 선택도 틀리지 않았습니다. 팀당 하나의 규칙을 선택하고 일관성을 유지하십시오.

한 가지 규칙은 어느 규칙을 선택하든 관계없이 유지됩니다: 다른 사람들이 이미 풀했거나 위에 작업을 했던 브랜치를 리베이스하지 마십시오. 리베이스는 새로운 ID를 가진 새로운 커밋으로 커밋을 다시 작성하고, 공유된 브랜치의 히스토리가 협력자 아래에서 변경되면, 그들의 로컬 복사본과 다시 작성된 원격 복사본은 더 이상 동의하지 않고, 그들의 끝에서 수동 수정을 강제합니다. 누구도 아직 풀하지 않은 자신의 브랜치에서 자유롭게 리베이스합니다. 일단 공유되면, 병합합니다.

Juno병합 충돌의 모양 병합 충돌은 두 브랜치가 같은 줄을 변경했고 Git이 결과를 선택해야 한다는 의미입니다. 파일을 열고 <<<<<<<, =======, 및 >>>>>>>를 찾고, 원하는 방식으로 읽을 때까지 편집한 다음 마커 줄을 삭제합니다. git addgit commit으로 마무리합니다. 나의 첫 충돌은 응급 상황처럼 느껴졌습니다. Git이 나에게 꽤 일반적인 질문을 하고 있었습니다.
Juno병합 충돌의 모양git status는 병합되지 않은 경로 아래의 모든 해결되지 않은 파일을 나열하고, 충돌이 하나 이상의 순간 정확할 때 편리합니다. git add는 당신의 작업을 확인하지 않고 파일을 해결된 것으로 표시하므로, 스테이징하기 전에 읽으십시오. 병합이 옆으로 간다면, git merge --abort는 당신을 반쯤 완성된 것이 없는 깨끗한 상태로 돌립니다.
Juno병합 충돌의 모양 리베이스는 병합과 같은 충돌을 겪습니다. 히스토리를 함께 묶는 대신 새로운 기본으로 커밋을 재생하고, 보존된 브랜치 모양을 위해 직선을 트레이딩합니다. 당신이 사용하는 브랜치에만 그 트레이드를 유지하십시오. 브랜치가 공유되는 순간, 리베이스로 커밋을 다시 작성하면 이미 풀한 누구에게나 생활이 어려워지므로, 대신 병합에 손을 뻗으십시오.

다시 함께 가져오기

병합은 브랜칭을 할 가치 있게 만드는 것입니다. 브랜치에 다루어진 당신만의 히스토리 줄에서 안전하게 실험하고, 준비가 되면 충돌이 있든 없든 해당 작업을 main으로 다시 접으십시오. GitHub에서, 같은 작업은 일반적으로 당신의 머신에서 로컬 git merge 대신 풀 요청을 통해 일어납니다. 풀 요청 흐름 챕터는 공유 프로젝트에서 변경을 제안하고, 검토하고, 병합하는 것을 다룹니다. 이미 커밋한 후 병합이 의도하지 않은 곳에 어딘가에 도착하면, 사항 취소는 안전하게 백아웃하는 방법을 다룹니다.