깃(Git) pull 사용법, 받아오기부터 충돌 해결까지 직접 해 보니
깃 2.54.0으로 로컬 원격 저장소와 두 폴더를 만들어 git pull을 처음부터 끝까지 실행했습니다. 바로 받아지는 경우, 같은 줄을 고쳤을 때 종료 코드 128로 멈추는 오류, 병합 충돌을 고쳐 올리는 순서까지 명령과 출력으로 정리했습니다.
Finn ·

이번 실습에서는 a와 b 두 폴더에서 같은 줄을 서로 다르게 커밋하고 a만 push한 뒤, 합치는 방식을 지정하지 않은 b에서 git pull을 하자 아무것도 합쳐지지 않고 종료 코드 128로 멈췄습니다. 합치는 방식을 정해서 다시 받으면 이번에는 충돌이 나고(종료 코드 1), 충돌 표시를 직접 고쳐 커밋해야 끝나죠. AI 코딩 도구가 한 폴더에서 커밋하는 사이 다른 폴더나 다른 기기에서 같은 파일의 같은 줄을 따로 고치고 한쪽이 먼저 올렸다면, 이 실습을 같은 조건의 사례로 참고할 수 있습니다.
따라 할 순서 한눈에 보기
직접 실행한 순서를 그대로 줄이면 아래 여덟 단계입니다.
- 로컬에 원격 저장소(remote.git)를 하나 만듭니다.
- a 폴더로 복제해 첫 커밋을 올리고, b 폴더로도 복제합니다.
- a에서 고쳐 올린 커밋을 b에서 받습니다. 이때는 빨리 감기(Fast-forward)로 끝납니다.
- a와 b에서 같은 줄을 따로 고쳐 커밋하고, a만 먼저 올립니다.
- b에서 받으면 갈라진 브랜치를 어떻게 합칠지 정하라는 오류가 납니다.
- 병합(merge) 방식으로 다시 받으면 충돌이 납니다.
- 충돌 표시를 지우고 원하는 내용으로 고친 뒤 커밋해서 올립니다.
- a에서 다시 받아 결과를 맞춥니다.
모든 단계는 0.1초 이내에 끝났습니다. 그러니 이 글에서 볼 것은 속도가 아니라 단계마다 나오는 메시지와 종료 코드예요. 명령을 보기 전에 git pull이 안에서 무슨 일을 하는지부터 짚고 넘어가겠습니다.
함께 읽을 글
받아오기는 정확히 무슨 일을 하나요?
git-pull 설명서를 보면 git pull은 두 가지 일을 차례로 합니다. 먼저 git fetch로 원격 브랜치를 가져오고, 그다음 그 브랜치를 지금 브랜치에 합칩니다. 인자 없이 실행하면 지금 브랜치가 따라가는 업스트림(upstream) 브랜치를 합칠 대상으로 삼습니다.
그럼 git fetch만 하면 어떻게 될까요? Git 공식 책 한국어판은 git fetch가 데이터를 모두 가져오지만 자동으로 합치지는 않는다고 설명합니다. 그래서 합치기는 직접 해야 하고, git pull은 이 합치기까지 한 번에 해 주는 명령이죠.
합치는 방식은 크게 네 가지입니다. 기본값인 --ff-only는 빨리 감기만 하고, 로컬 브랜치가 원격과 갈라져 있으면 실패합니다. --rebase는 git rebase를, --no-rebase는 git merge를, --squash는 git merge --squash를 실행합니다. 이 선택은 pull.rebase, pull.squash, pull.ff 설정으로 미리 정해 둘 수도 있어요.

연습용 원격 저장소와 두 폴더 만들기
원격 저장소가 꼭 인터넷에 있어야 할까요? 공식 책은 원격 저장소가 같은 로컬 시스템에 있을 수도 있고, 그래도 Push와 Pull은 똑같이 쓴다고 적고 있습니다. 그래서 GitHub 없이 bare 저장소 하나로 실습할 수 있습니다. 먼저 빈 실습 폴더를 하나 만들어 그 안으로 들어가세요. 이 글의 명령 블록은 모두 이 실습 폴더(remote.git이 생기는 곳)에서 각각 실행합니다. 블록마다 cd a나 cd b로 시작하므로, 이전 블록이 a나 b 폴더 안에서 끝났다면 실습 폴더로 돌아온 뒤 다음 블록을 실행하세요.
$ git init --bare -q -b main remote.git && ls remote.git
HEAD
config
description
hooks
info
objects
refs이어서 a 폴더로 복제해 첫 커밋을 올립니다. 빈 저장소를 복제했다는 경고가 나오지만 종료 코드는 0이었어요.
$ git clone -q remote.git a && cd a && git config user.name "Finn" && git config user.email "lab@solodevnote.com" && printf '# 메모\n첫 줄\n' > README.md && git add README.md && git commit -q -m "첫 커밋" && git push origin main && git log --oneline
warning: 빈 저장소를 복제한 것처럼 보입니다.
To ./remote.git
* [new branch] main -> main
2d8257f 첫 커밋두 번째 사람 역할을 할 b 폴더도 복제합니다. 작성자 설정은 --global 없이 각 폴더에만 적용했습니다.
$ git clone remote.git b && cd b && git config user.name "Finn" && git config user.email "lab@solodevnote.com" && git log --oneline
'b'에 복제합니다...
완료.
2d8257f 첫 커밋다른 폴더에서 올린 커밋 받기
이제 a에서 한 줄을 더해 올립니다.
$ cd a && printf '둘째 줄(a에서 추가)\n' >> README.md && git commit -q -am "a에서 둘째 줄 추가" && git push && git log --oneline
To ./remote.git
2d8257f..d54a5fc main -> main
d54a5fc a에서 둘째 줄 추가
2d8257f 첫 커밋b는 그동안 아무것도 고치지 않았으니 git pull 한 번으로 받아집니다. 출력에 보이는 Fast-forward는 병합 커밋을 만들지 않고 브랜치 위치만 앞으로 옮겼다는 뜻이에요.
$ cd b && git pull && git log --oneline && cat README.md
./remote URL에서
2d8257f..d54a5fc main -> origin/main
업데이트 중 2d8257f..d54a5fc
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
d54a5fc a에서 둘째 줄 추가
2d8257f 첫 커밋
# 메모
첫 줄
둘째 줄(a에서 추가)같은 줄을 고치면 왜 오류가 나나요?
이 절에서는 git pull이 오류를 내며 멈추는 장면을 재현합니다. 아래처럼 a와 b가 첫 줄을 각자 다르게 고쳐 커밋하고, a만 먼저 올렸습니다.
$ cd a && sed -i '' 's/^첫 줄$/첫 줄(a가 고침)/' README.md && git commit -q -am "a가 첫 줄 수정" && git push && cd ../b && sed -i '' 's/^첫 줄$/첫 줄(b가 고침)/' README.md && git commit -q -am "b가 첫 줄 수정" && git log --oneline
To ./remote.git
d54a5fc..ed0bfb4 main -> main
526e570 b가 첫 줄 수정
d54a5fc a에서 둘째 줄 추가
2d8257f 첫 커밋이 상태에서 b가 git pull을 하면 가져오기는 되지만, 합치기 단계에서 종료 코드 128로 멈춥니다.
$ cd b && git pull
./remote URL에서
d54a5fc..ed0bfb4 main -> origin/main
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: Need to specify how to reconcile divergent branches.메시지를 풀면 이렇습니다. 브랜치가 갈라졌으니 병합할지, 리베이스할지, 빨리 감기만 할지 정하라는 거죠. 설정으로 정해 두거나 명령을 칠 때 --rebase, --no-rebase, --ff-only를 붙이면 됩니다. 빨리 감기만 하는 기본 방식은 갈라진 브랜치에서 실패한다는 설명서 내용과 맞아떨어지는 결과예요.

병합으로 다시 받자 같은 줄에서 충돌했습니다
그래서 이번에는 병합 방식인 --no-rebase를 붙여 다시 받았습니다. 그런데 같은 줄을 서로 다르게 고쳤으니 자동 병합이 실패하고 종료 코드 1이 나옵니다.
$ cd b && git pull --no-rebase
자동 병합: README.md
충돌 (내용): README.md에 병합 충돌
자동 병합이 실패했습니다. 충돌을 바로잡고 결과물을 커밋하십시오.파일을 열어 보면 충돌 표시가 들어가 있습니다. UU는 양쪽이 모두 고쳐서 합쳐지지 않은 파일이라는 표시예요.
$ cd b && git status --short && cat README.md
UU README.md
# 메모
<<<<<<< HEAD
첫 줄(b가 고침)
=======
첫 줄(a가 고침)
>>>>>>> ed0bfb4e482ac8673e6e7eb2bde0dde06fdadece
둘째 줄(a에서 추가)<<<<<<< HEAD부터 =======까지는 지금 폴더(b)의 내용이고, >>>>>>>까지는 받아 온 커밋(a의 ed0bfb4)의 내용입니다. 지금 당장 고치고 싶지 않다면 어떻게 할까요? 설명서는 git merge --abort(리베이스 중이면 git rebase --abort)로 안전하게 취소할 수 있다고 안내합니다.
충돌을 고쳐 올리고 다른 폴더에서 받기
이제 표시 세 줄을 지우고 원하는 내용 한 줄만 남긴 뒤 커밋해서 올립니다. 그래프를 보면 두 갈래가 병합 커밋 하나로 다시 만나죠.
$ cd b && printf '# 메모\n첫 줄(a와 b가 함께 고침)\n둘째 줄(a에서 추가)\n' > README.md && git add README.md && git commit -q -m "충돌 해결" && git push && git log --oneline --graph
To ./remote.git
ed0bfb4..f5811cd main -> main
* f5811cd 충돌 해결
|\
| * ed0bfb4 a가 첫 줄 수정
* | 526e570 b가 첫 줄 수정
|/
* d54a5fc a에서 둘째 줄 추가
* 2d8257f 첫 커밋
마지막으로 a에서 받으면 다시 빨리 감기로 끝납니다. a의 마지막 커밋 ed0bfb4가 병합 커밋의 한쪽 부모로 이미 들어 있으니, a 쪽에서는 갈라진 것이 없기 때문이에요.
$ cd a && git pull && git log --oneline --graph
./remote URL에서
ed0bfb4..f5811cd main -> origin/main
업데이트 중 ed0bfb4..f5811cd
Fast-forward
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
* f5811cd 충돌 해결
|\
| * ed0bfb4 a가 첫 줄 수정
* | 526e570 b가 첫 줄 수정
|/
* d54a5fc a에서 둘째 줄 추가
* 2d8257f 첫 커밋기본값은 어떻게 정해 둘까요?
매번 옵션을 붙이기 번거롭다면 오류 메시지가 알려 준 설정 가운데 하나를 고르면 됩니다. git config 대신 git config --global로 실행하면 모든 저장소의 기본값이 됩니다.
git config pull.rebase false # merge
git config pull.rebase true # rebase
git config pull.ff only # fast-forward only그런데 리베이스는 조심해야 합니다. 설명서는 --rebase가 히스토리를 다시 쓰기 때문에 이미 공개한 히스토리에는 위험할 수 있다고 경고해요. AI 에이전트에게 커밋과 받기를 맡기고 있다면, 병합 기록이 그대로 남는 pull.rebase false부터 검토해 보는 편이 무난합니다. 이번 실습에서는 합치는 방식을 정하지 않았을 때 종료 코드 128과 함께 fatal: Need to specify how to reconcile divergent branches. 메시지가 나왔습니다. 이 측정만으로는 128이 이 원인에서만 나온다고 볼 수 없으니, 에이전트에게는 종료 코드만 보지 말고 fatal 메시지 내용까지 확인하게 하는 편이 좋습니다. 병합 충돌이 났을 때는 이번 실습에서 종료 코드 1이 나왔습니다. 원인을 가를 때는 종료 코드 하나로 판단하지 말고 fatal 메시지, 충돌 출력, git status를 함께 확인하도록 설계하는 편이 안전합니다.
자주 하는 실수도 정리해 두면 좋습니다.
- 받지 않고 바로 올리기: 공식 책은 다른 사람이 먼저 Push 했다면 내 Push는 되지 않고, 먼저 가져와 Merge 한 뒤 올려야 한다고 설명합니다.
- 충돌 표시를 남긴 채 커밋하기:
<<<<<<<,=======,>>>>>>>줄을 모두 지웠는지 확인하세요. - 이미 올린 커밋에 리베이스 쓰기: 히스토리를 다시 쓰므로 설명서를 먼저 읽어야 합니다.
받기를 마친 뒤에는 이렇게 점검해 보세요.
git status --short에UU표시가 남아 있지 않은가git log --oneline --graph에 의도한 병합 커밋이 보이는가- 다른 폴더에서 받았을 때 빨리 감기로 끝나는가
측정 조건과 한계
이 기록은 맥 한 대(darwin arm64)에서 git version 2.54.0으로 새 임시 폴더를 만들고, 명령을 적힌 순서대로 한 번씩 실행한 결과입니다. 원격 저장소는 GitHub가 아니라 같은 컴퓨터의 bare 저장소(remote.git)였고, a와 b 두 폴더로 두 사람이 함께 쓰는 상황을 흉내 냈습니다. 기록된 출력은 대체로 한국어였지만, 8단계의 갈라진 브랜치 오류에 붙은 hint: 안내는 영어였습니다.
한 번만 실행했으니 걸린 시간은 참고용일 뿐입니다. GitHub 같은 원격 서버, SSH나 토큰 인증, --rebase와 --squash로 받는 경우는 이 실습에 들어 있지 않습니다. 서버를 쓰는 저장소라면 인증 단계가 더해질 수 있으니, 같은 순서를 연습용 저장소에서 먼저 돌려 보세요.
참고 자료
- git-scm.com/docs/git-pull git-scm.com
- 2.5 Git의 기초 - 리모트 저장소 git-scm.com