이 글에서 정리하는 내용
목표는 같은 행을 서로 다르게 고친 두 브랜치에서 충돌을 재현하고, 양쪽 의도를 합쳐 PR 검토가 가능한 커밋을 만드는 것입니다. 선수 지식은 add·commit·push입니다. bash 또는 Git Bash의 빈 폴더에서 로컬 원격 실습을 먼저 수행하며 실제 GitHub PR은 별도 계정 단계로 구분합니다.
목차
- 브랜치와 PR은 서로 다른 역할입니다
- 두 브랜치에서 같은 제목을 수정하기
- 원격 기준을 가져와 충돌 확인하기
- 두 의도를 보존하고 병합 커밋 만들기
- GitHub에서는 base와 compare를 확인하세요
- 로컬에서 전체 흐름을 끝내고 결과 점검하기
브랜치와 PR은 서로 다른 역할입니다
브랜치는 커밋을 가리키는 이름입니다. git switch -c로 새 작업 흐름을 시작하고 switch로 이동하면 그 브랜치에 맞춰 작업 파일이 바뀝니다. main은 기준 이력, feature/heading은 제목 수정 작업으로 나눠 보겠습니다.

PR은 GitHub에서 변경을 검토하고 대상 브랜치로 합치자고 제안하는 작업 공간입니다. 브랜치를 push해도 main에 바로 반영되지 않습니다. PR 생성, 리뷰, 병합은 각각 별도 단계입니다. 로컬 저장소에서도 Git merge는 가능하지만 GitHub PR 화면·리뷰 기록은 생기지 않습니다.
| 이름 | 무엇을 가리키나요? |
|---|---|
| main | 이 컴퓨터의 main 브랜치 |
| feature/heading | 이 컴퓨터의 작업 브랜치 |
| origin/main | 마지막 fetch 또는 관련 원격 갱신으로 알고 있는 원격 main |
| PR base / compare | 반영 대상 브랜치 / 변경을 제안하는 브랜치 |
이 글은 기본 add·commit·push를 익힌 다음 읽습니다. 새 빈 연습 공간에서 Git Bash 또는 bash로 실행하며, 앞 글의 저장소는 건드리지 않습니다. 작업 중인 파일이 있으면 먼저 status를 보고 커밋할 변경을 정리한 뒤 브랜치를 이동하세요.
두 브랜치에서 같은 제목을 수정하기
아래 명령은 branch-lab 저장소와 로컬 bare 원격을 만듭니다. feature에서는 프론트엔드 역할을, main에서는 퍼블리셔 역할을 제목에 넣습니다. 같은 원본 행에서 갈라진 두 변경이라 다음 단계에서 충돌을 재현할 수 있습니다. 이름과 이메일은 연습용이며 이 저장소에만 적용합니다.
01-branches.sh
mkdir branch-lab
cd branch-lab
git init -b main
git config user.name "Publisher Learner"
git config user.email "learner@example.com"
printf '%s\n' '<h1>Portfolio</h1>' > index.html
git add index.html
git commit -m "Create heading"
git init --bare -b main ../branch-remote.git
git remote add origin ../branch-remote.git
git push -u origin main
git switch -c feature/heading
printf '%s\n' '<h1>Frontend portfolio</h1>' > index.html
git add index.html
git commit -m "Clarify portfolio heading"
git push -u origin feature/heading
git diff main...feature/heading
git switch main
printf '%s\n' '<h1>Publisher portfolio</h1>' > index.html
git add index.html
git commit -m "Describe publisher work"
git push origin main
feature의 push 다음에 실행하는 git diff main...feature/heading는 공통 조상 이후 feature가 제안하는 변경을 보여 줍니다. 세 점은 코드 생략 표시가 아니라 Git 비교 문법입니다. 마지막에는 main의 별도 변경까지 원격에 올라가 있습니다. 각 branch의 커밋은 보존되어 있고 아직 하나로 합치지 않았습니다.
원격 기준을 가져와 충돌 확인하기
작업 브랜치로 돌아와 원격 이력을 가져옵니다. fetch만으로 현재 파일은 병합되지 않습니다. 이어지는 merge가 origin/main을 현재 feature에 합치려다가 같은 제목 행에서 멈춥니다.
02-conflict.sh
git switch feature/heading
git fetch origin
git merge origin/main
여기서 CONFLICT (content)와 merge failed가 나오는 것은 의도한 실습 결과입니다. 다음 명령으로 해결하기 전에는 병합이 진행 중입니다. 에디터에서 index.html을 열면 기본 충돌 표시 형식에서는 다음 내용이 보입니다. 사용자 설정이 diff3 등인 경우 공통 조상 구간이 추가될 수 있습니다.
index.html — 충돌 중
<<<<<<< HEAD
<h1>Frontend portfolio</h1>
=======
<h1>Publisher portfolio</h1>
>>>>>>> origin/main
이 merge에서 HEAD는 현재 feature 쪽입니다. 아래쪽은 합치려던 origin/main입니다. “현재 것 수락” 버튼을 항상 누르면 어느 의도가 사라지는지 모를 수 있으므로 양쪽 수정 목적을 먼저 읽습니다. 충돌 표시와 수동 해결 흐름은 Git: Basic Branching and Merging에서 확인할 수 있습니다.

지금 병합을 취소해야 한다면 충돌 상태에서 git merge --abort를 실행합니다. 이 연습처럼 병합 전에 작업 트리가 깨끗했을 때 되돌아갈 상태가 분명합니다. 취소했다면 이후 해결 단계를 계속하려면 git merge origin/main으로 충돌을 다시 만들어야 합니다.
충돌 마커만 보고 원래 문장을 추측하지 마세요
일반적인 내용 충돌에서는 Git index에 공통 조상과 양쪽 파일이 남습니다. 현재 feature 브랜치에서 origin/main을 merge하는 이 실습에 한해 2번은 feature, 3번은 origin/main입니다. rebase에서는 역할이 다르게 보일 수 있으므로 같은 명칭을 무조건 적용하지 않습니다.
터미널에서 실행할 명령
git ls-files -u -- index.html
git show :1:index.html
git show :2:index.html
git show :3:index.html
차례대로 Portfolio, Frontend portfolio, Publisher portfolio 제목을 관찰합니다. 첫 명령은 같은 파일의 stage 1·2·3 항목을 보여 줍니다. 파일 추가/삭제 충돌에서는 일부 stage가 없을 수 있으므로 모든 충돌에 세 항목이 있다고 가정하지 않습니다.
복구 연습은 해결 내용을 쓰기 전에 합니다. git merge --abort 뒤 git status --porcelain이 비면 병합 전 깨끗한 상태로 복귀한 것입니다. 이어 git merge origin/main을 다시 실행하면 같은 내용 충돌이 나야 합니다. abort는 이미 완료한 merge commit을 되돌리는 명령이 아닙니다.
해결한 뒤 git rev-list --parents -n 1 HEAD에서 현재 해시 뒤 부모 해시 두 개를 확인하세요. 충돌 표시가 사라진 것과 양쪽 변경 의도가 보존된 것은 별개이므로 최종 제목도 함께 읽습니다.
두 의도를 보존하고 병합 커밋 만들기
이번 제목은 퍼블리셔와 프론트엔드 포트폴리오를 함께 설명하도록 결정합니다. 아래 printf는 index.html 전체를 최종 한 줄로 저장하므로 충돌 표시도 제거합니다. 실제 프로젝트에서는 주변 코드를 유지하며 충돌 구간을 직접 편집해야 합니다.
03-resolve.sh
git status --short
cat index.html
printf '%s\n' '<h1>Publisher and frontend portfolio</h1>' > index.html
git diff --check
git add index.html
git diff --cached
git commit -m "Resolve portfolio heading conflict"
git push origin feature/heading
git log --oneline --graph --all
처음 status의 UU는 아직 해결하지 않은 파일을 뜻합니다. git diff --check는 남은 충돌 표시나 공백 오류를 찾는 보조 검사입니다. HTML 내용이 올바른지는 파일과 화면을 별도로 검토해야 합니다. add는 최종 내용을 선택하면서 이 파일을 해결했다고 Git에 알립니다. commit 전 cached diff가 의도한 최종 제목인지 확인하세요.
완료 후 feature의 끝에는 부모 커밋이 둘인 병합 커밋이 생깁니다. 원격에 feature를 다시 push하면 이미 열려 있는 PR에도 새 커밋이 반영됩니다. 같은 제목 수정 때문에 새 PR을 여러 개 만들 필요는 없습니다.
GitHub에서는 base와 compare를 확인하세요
앞 GitHub 실습을 마친 본인 저장소에서 실제 PR을 만들 때는 깨끗한 main을 출발점으로 git switch -c feature/heading하고 제목 수정→add→commit→git push -u origin feature/heading을 진행합니다. 이 글의 branch-lab 원격은 로컬 폴더이므로 그 저장소에는 GitHub PR 버튼이 없습니다.
GitHub 저장소의 Pull requests → New pull request에서 base: main, compare: feature/heading을 고릅니다. Files changed에서 의도한 파일만 보이는지 확인하고 왜 제목을 바꾸는지 설명합니다. 검토 전이면 Draft로 만들 수 있습니다. UI 절차는 GitHub: Creating a pull request를 기준으로 확인하세요.
- 제목: 포트폴리오 제목에 담당 역할 표시
- 설명: 방문자가 작업 분야를 알 수 있도록 제목을 구체화했습니다.
- 검증: 실제 확인한 항목만 적습니다. 예를 들어 index.html의 최종 제목과 Git diff를 검토했습니다.
- 충돌이 있으면 feature에서 fetch origin → merge origin/main → 해결 → commit → push 순서로 갱신합니다.
리뷰·필수 검사와 저장소 규칙을 충족한 뒤 허용된 병합 방식으로 합칩니다. merge commit, squash, rebase는 이력 모양이 달라지므로 팀 기준을 따르세요. 아래 동기화 예시는 GitHub에서 PR 병합을 끝낸 뒤에만 실행합니다. GitHub: Merging a pull request
git switch main
git pull --ff-only origin main
git log --oneline -3
pull --ff-only가 멈추면 로컬 main에도 별도 커밋이 있는지 확인합니다. 무조건 reset하거나 강제 push하지 않습니다. 작업 브랜치 정리는 반영 결과를 확인한 다음 진행합니다. squash 병합에서는 git branch -d가 원래 커밋이 미병합이라고 거절할 수 있으므로 이력을 이해하지 못한 상태에서 -D로 바꾸지 마세요.
로컬에서 전체 흐름을 끝내고 결과 점검하기
GitHub에 연결하지 않은 이 글의 branch-lab에서는 아래 명령으로 기준 브랜치에 합치는 동작을 재현합니다. 실제 PR을 병합한 저장소에서는 이 블록을 중복 실행하지 않습니다. --no-ff는 합쳐진 지점을 별도 커밋으로 남깁니다.
04-local-merge.sh
git switch main
git merge --no-ff feature/heading -m "Merge heading improvement"
git push origin main
git status -sb
git log --oneline --graph --all
git branch -d feature/heading
정상 종료 시 main의 제목은 Publisher and frontend portfolio이고 status에는 미기록 변경이 없습니다. 로컬 feature 이름을 -d로 지워도 main에 포함된 커밋이 함께 삭제되지는 않습니다. 로컬 브랜치 삭제와 원격 브랜치 삭제는 별개여서 원격 feature는 남아 있습니다.
이 글의 로컬 검증에서는 분기·push·동일 행 충돌·수동 해결·두 부모 병합 커밋·로컬 원격 반영을 실제 실행했습니다. 추가로 GitHub API를 통해 검증 전용 브랜치의 PR 생성, 변경 파일 검토, 병합, 병합된 파일 재조회를 확인했습니다. 검증 전용 base 브랜치에 병합했으며 기존 main 커밋은 유지되었습니다. GitHub 화면의 버튼 조작이나 다른 사람의 리뷰 승인은 검증 범위에 포함하지 않았습니다.
이제 변경 파일이 예상보다 많으면 출발 브랜치와 diff를, 충돌이면 양쪽의 변경 의도를, push 거절이면 원격 이력을 확인할 수 있습니다. 이 순서가 익숙해진 뒤 기존 Git alias 모음을 적용하면 단축 명령의 의미도 추적하기 쉬워집니다.
공식 근거와 실습 확인 범위
공식 문서 확인일: 2026-09-13. 작성·검수 기준: 1.0(2026-09-12). 아래 문서는 이 글에서 사용하는 명령·설정의 근거입니다.
Git 2.51.1의 격리된 임시 저장소에서 staging·bare 원격 push·충돌·abort·해결·alias 관련 실습을 실행했습니다. GitHub 인증·공개 PR UI는 실행 검증 범위에 포함하지 않았습니다.
실습 파일 내려받기
Git·개발환경 실습 ZIP에 독립된 예제 폴더와 README를 제공합니다. 본문에 해당하는 폴더만 사용하고, 기존 프로젝트에 전체 압축을 덮어쓰지 마세요. Git은 같은 터미널에서 단계별 명령을 이어 실행하며 오류 재현 단계의 비정상 종료는 README의 예상 결과와 대조합니다.
이 글이 도움이 되었나요?
Git·GitHub 학습 순서
필수 2개 · 전체 3개
읽음 기록 관리
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.