coredot.today
Git, 이것만 알면 협업한다 5편 — 충돌 해결과 되돌리기, 실수해도 괜찮은 이유
블로그로 돌아가기
GitGit 입문버전 관리GitHub협업Claude Code충돌 해결merge conflictgit resetgit revertgit reflog

Git, 이것만 알면 협업한다 5편 — 충돌 해결과 되돌리기, 실수해도 괜찮은 이유

두 사람이 같은 줄을 다르게 고치면 git은 멈추고 사람에게 묻습니다. 이번 편에서는 민지와 도윤이 카페라테 가격을 동시에 바꾸며 실제 충돌을 재현하고, 충돌 마커를 읽고 푸는 법을 따라 합니다. 이어서 restore·amend·revert·reset·stash·reflog를 '상황별 되돌리기 지도'로 정리해, 어떤 실수를 해도 침착하게 되돌릴 수 있게 만듭니다.

코어닷투데이2026-10-0382분

충돌 해결과 되돌리기 — Git 입문 5편크게 보기

지난 4편까지 민지는 꽤 많은 것을 배웠습니다. 파일을 고치고 add·commit으로 세이브 포인트를 남기고, push·pull로 도윤과 주고받고, 브랜치를 만들어 따로 작업한 뒤 merge로 합쳤습니다. 그런데 어느 날 아침, 늘 하던 대로 git pull을 쳤더니 처음 보는 빨간 글자가 쏟아집니다.

text
CONFLICT (content): Merge conflict in menu.md
Automatic merge failed; fix conflicts and then commit the result.

메뉴판 파일을 열어 보니 <<<<<<<, =======, >>>>>>> 같은 이상한 기호가 박혀 있습니다. 민지는 "내가 뭘 망가뜨렸나?" 싶어 식은땀이 납니다.

결론부터 말씀드리면, 아무것도 망가지지 않았습니다. 충돌(conflict)은 git이 "두 분이 같은 곳을 다르게 고치셨는데, 어느 쪽이 맞는지 저는 모르겠어요. 사람이 정해 주세요"라고 손을 드는 정상적인 상황입니다. 그리고 설령 정말로 뭔가를 잘못 지웠더라도, git에는 거의 모든 실수를 되돌리는 방법이 있습니다.

이번 편은 두 부분으로 나뉩니다. 1부에서는 충돌을 직접 만들어 보고, 충돌 마커를 읽고, 푸는 절차를 익힙니다. 2부에서는 "방금 한 걸 없던 일로 하고 싶다"는 순간에 쓰는 되돌리기 명령들을 상황별 지도로 정리합니다. 이 편을 마치면 git이 무섭지 않게 됩니다. 실수해도 돌아갈 길이 있다는 걸 알게 되니까요.

🛠️
이 글의 실행 환경 — 모든 터미널 출력은 git 2.55에서 cafe-menu 저장소를 실제로 만들어 재현한 결과입니다. 원격 저장소는 내 컴퓨터 안에 만든 가짜 원격(../origin.git)을 썼지만, GitHub를 원격으로 쓸 때도 메시지는 같습니다. 출력은 영어 기준이며, 컴퓨터 언어가 한국어로 설정돼 있으면 같은 내용이 우리말로 나옵니다(3장에서 한국어 출력도 함께 보여 드립니다).

1부. 충돌 해결

1. 충돌은 왜 생길까 — "같은 줄을 둘이 다르게"

카페 카운터에 메뉴판이 하나 걸려 있다고 해 봅시다. 민지와 도윤은 각자 메뉴판 사본을 집에 가져가 고친 뒤, 다음 날 가게에서 원본에 반영하기로 했습니다.

  • 민지는 아메리카노 줄 아래에 새 메뉴를 한 줄 추가했습니다.
  • 도윤은 바닐라라테 가격을 고쳤습니다.

두 사람이 고친 곳이 다르니, 둘 다 원본에 반영하면 그만입니다. git도 똑같이 생각합니다. 서로 다른 줄, 서로 다른 파일을 고쳤다면 git은 알아서 두 변경을 합칩니다. 4편에서 본 3-way merge가 바로 그 일입니다.

문제는 이런 경우입니다.

  • 민지는 카페라테 4,500원을 5,000원 (오트밀크 변경 가능) 으로 바꿨습니다.
  • 도윤은 같은 카페라테 4,500원을 4,800원으로 바꿨습니다.

원래 한 줄이었던 곳이 두 가지 다른 모습이 됐습니다. 어느 쪽이 맞을까요? 가격 정책은 사장님과 이야기해 봐야 알 수 있습니다. git은 가격 정책을 모릅니다. 그래서 멈추고 사람에게 묻습니다. 이것이 충돌(merge conflict) 입니다.

두 사람이 고친 곳git의 판단결과
서로 다른 파일겹칠 일이 없음자동으로 합침
같은 파일, 떨어진 줄각자 다른 곳을 고쳤으니 둘 다 반영자동으로 합침
같은 파일, 같은 줄을 다르게어느 쪽이 맞는지 알 수 없음충돌 — 사람이 결정
한 사람은 파일을 고치고, 다른 사람은 그 파일을 삭제고친 걸 살릴지, 지울지 알 수 없음충돌 — 사람이 결정
같은 줄을 똑같이 고침결과가 같으니 문제없음자동으로 합침
💡
충돌은 에러가 아니라 질문입니다. git이 내 작업을 지운 것도, 저장소가 고장 난 것도 아닙니다. 두 사람의 변경은 모두 안전하게 보관돼 있고, git은 "어떻게 합칠지"만 물어보고 있습니다. 붉은 글씨에 놀라지 말고, 질문에 답한다고 생각하세요.

충돌이 생기는 순간은 크게 세 가지입니다.

  1. git pull 할 때 — 원격에 동료가 올린 변경과 내 커밋이 같은 줄을 건드렸을 때 (가장 흔함)
  2. git merge 할 때 — 브랜치 두 개가 같은 줄을 다르게 바꿨을 때 (4편의 연장)
  3. git stash pop 할 때 — 잠깐 치워 둔 수정을 다시 꺼냈는데 그사이 같은 줄이 바뀌었을 때 (2부에서 다룹니다)

어느 경우든 푸는 방법은 똑같습니다. 그럼 직접 만들어 봅시다.

2. 충돌 재현하기 — 민지와 도윤의 카페라테 가격

cafe-menu 저장소의 menu.md는 지금 이렇게 생겼습니다.

text
# 오늘의 메뉴

- 아메리카노 4,000원
- 카페라테 4,500원
- 바닐라라테 5,000원

도윤이 먼저 push합니다

도윤은 원두값이 올라 카페라테를 4,800원으로 올리기로 했습니다. 파일을 고치고, 커밋하고, 바로 push합니다.

bash
git commit -am "카페라테 가격 4,800원으로 인상"
git push origin main
text
To ../origin.git
   9102ed9..a594ee0  main -> main

-am은 2편에서 본 것처럼 "이미 추적 중인 파일의 수정을 add하면서 바로 커밋"하는 줄임입니다.

민지는 그 사실을 모른 채 같은 줄을 고칩니다

민지는 사장님께 "오트밀크로 바꿀 수 있게 하고 5,000원으로 하자"는 이야기를 들었습니다. 도윤이 이미 가격을 바꾼 줄은 모르고, 자기 컴퓨터의 menu.md에서 같은 줄을 고쳐 커밋합니다.

bash
git commit -am "카페라테 가격 5,000원, 오트밀크 옵션 추가"
text
[main d97fb15] 카페라테 가격 5,000원, 오트밀크 옵션 추가
 1 file changed, 1 insertion(+), 1 deletion(-)

이제 push해 봅니다.

bash
git push origin main
text
To ../origin.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to '../origin.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

3편에서 본 push 거절입니다. "원격에 네가 아직 안 받은 작업이 있으니 먼저 pull하라"는 뜻입니다. 시키는 대로 pull합니다.

pull하면 — 처음이라면 이 메시지가 먼저 나올 수 있습니다

bash
git pull
text
From ../origin
   9102ed9..a594ee0  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.

이건 아직 충돌이 아닙니다. "내 쪽과 원격 쪽이 서로 다른 커밋을 가지고 갈라졌는데(divergent), 합치는 방식을 정해 주지 않았다"는 안내입니다. 입문 단계에서는 가장 이해하기 쉬운 merge 방식으로 정해 두면 됩니다. 한 번만 설정하면 이후로는 묻지 않습니다.

bash
git config --global pull.rebase false

rebase 방식은 6편에서 개념만 짚고 넘어갑니다. 지금은 "pull = fetch + merge"로 기억하면 충분합니다.

다시 pull하면 — 드디어 충돌

bash
git pull
text
Auto-merging menu.md
CONFLICT (content): Merge conflict in menu.md
Automatic merge failed; fix conflicts and then commit the result.

세 줄을 하나씩 읽어 보겠습니다.

Auto-merging menu.md
git이 menu.md를 자동으로 합치려고 시도했습니다.
CONFLICT (content)
파일 내용에서 충돌이 났습니다. 같은 줄을 양쪽이 다르게 고쳤다는 뜻입니다. 어느 파일인지도 알려 줍니다: menu.md.
Automatic merge failed
자동 병합은 실패했으니 충돌을 고친 뒤 결과를 커밋하라(fix conflicts and then commit the result). 해야 할 일을 정확히 말해 주고 있습니다.

지금 상태를 git status로 확인해 봅시다. 충돌이 나면 가장 먼저 칠 명령이 바로 이것입니다.

bash
git status
text
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" if you want to integrate the remote branch with yours)

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:   menu.md

no changes added to commit (use "git add" and/or "git commit -a")

git status는 친절한 안내원입니다. 핵심만 뽑으면 이렇습니다.

  • You have unmerged paths — 아직 합치지 못한 파일이 있습니다. 지금은 "병합 중" 상태입니다.
  • both modified: menu.md — 양쪽(민지와 도윤) 모두 고친 파일이 menu.md입니다. 충돌 난 파일 목록이 여기 나옵니다.
  • fix conflicts and run "git commit" — 해결 방법: 충돌을 고치고 커밋하라.
  • use "git add <file>..." to mark resolution — 파일을 고친 뒤 git add로 "해결했음"을 표시하라.
  • use "git merge --abort" to abort the merge — 도저히 모르겠으면 병합을 취소하고 pull 전으로 돌아가라.

해결 방법과 탈출구가 모두 적혀 있습니다. 이제 파일을 열어 봅시다.

같은 줄을 다르게 고친 민지와 도윤크게 보기

3. 충돌 마커 읽는 법 — <<<<<<< · ======= · >>>>>>>

에디터로 menu.md를 열면 이렇게 보입니다.

text
# 오늘의 메뉴

- 아메리카노 4,000원
<<<<<<< HEAD
- 카페라테 5,000원 (오트밀크 변경 가능)
=======
- 카페라테 4,800원
>>>>>>> a594ee05575b4b1827021eef145d5093a535bee9
- 바닐라라테 5,000원

이 기호들을 충돌 마커(conflict marker) 라고 부릅니다. git이 파일 안에 직접 써넣은 표시입니다. 충돌이 난 부분만 표시되고, 나머지 줄(아메리카노·바닐라라테)은 이미 문제없이 합쳐져 있다는 점에 주목하세요.

<<<<<<< HEAD — 여기서부터 내 쪽(지금 서 있는 브랜치, 민지의 커밋)
↓
- 카페라테 5,000원 (오트밀크 변경 가능)
↓
======= — 칸막이. 위는 내 쪽, 아래는 들어오는 쪽
↓
- 카페라테 4,800원
↓
>>>>>>> a594ee0… — 여기까지 들어오는 쪽(합치려는 커밋, 도윤의 커밋)

정리하면 이렇습니다.

마커뜻이번 예에서
<<<<<<< HEAD내 쪽 시작. HEAD는 "지금 내가 서 있는 곳"을 가리키는 이름민지의 main
(그 아래 줄들)내 쪽 내용카페라테 5,000원 (오트밀크 변경 가능)
=======두 내용을 가르는 칸막이—
(그 아래 줄들)들어오는 쪽 내용카페라테 4,800원
>>>>>>> 이름들어오는 쪽 끝. 뒤에 커밋 해시나 브랜치 이름이 붙음도윤이 push한 커밋 a594ee0…
🧠
외우는 요령: 위는 나, 아래는 남. HEAD는 늘 "지금 내 브랜치"입니다. 칸막이 ======= 위가 내 것, 아래가 합치려고 가져온 것입니다. pull할 때는 아래가 원격(동료)의 변경, merge할 때는 아래가 합치려는 브랜치의 변경입니다.

브랜치를 merge할 때도 똑같습니다

4편처럼 브랜치를 합칠 때 충돌이 나면 >>>>>>> 뒤에 해시 대신 브랜치 이름이 붙어 더 읽기 쉽습니다. 민지가 vanilla-price 브랜치에서는 바닐라라테를 5,300원으로, main에서는 5,500원으로 바꾼 뒤 합쳐 보면 이렇습니다.

bash
git merge vanilla-price
text
Auto-merging menu.md
CONFLICT (content): Merge conflict in menu.md
Automatic merge failed; fix conflicts and then commit the result.
text
# 오늘의 메뉴

- 아메리카노 4,000원
- 카페라테 4,800원 (오트밀크 변경 가능)
<<<<<<< HEAD
- 바닐라라테 5,500원
=======
- 바닐라라테 5,300원
>>>>>>> vanilla-price

위(HEAD)가 지금 서 있는 main, 아래가 합치려는 vanilla-price입니다.

컴퓨터가 한국어로 설정돼 있다면

macOS나 Linux의 언어가 한국어면 git 메시지도 한국어로 나옵니다. 같은 상황의 실제 출력입니다. 영어 출력과 한 줄씩 짝지어 보면 금방 익숙해집니다.

text
자동 병합: menu.md
충돌 (내용): menu.md에 병합 충돌
자동 병합이 실패했습니다. 충돌을 바로잡고 결과물을 커밋하십시오.
text
병합하지 않은 경로가 있습니다.
  (충돌을 바로잡고 "git commit"을 실행하십시오)
  (병합을 중단하려면 "git merge --abort"를 사용하십시오)

병합하지 않은 경로:
  (해결했다고 표시하려면 "git add <파일>..."을 사용하십시오)
	양쪽에서 수정:  menu.md

both modified가 양쪽에서 수정, unmerged paths가 병합하지 않은 경로입니다. 검색할 때는 영어 문구가 자료가 훨씬 많으니, 막히면 영어 메시지로 검색해 보세요.

4. 해결 절차 — 고치고, 마커 지우고, add, commit

충돌 해결은 네 단계입니다.

① 결정
최종적으로 어떤 내용이 맞는지 사람이 정합니다. 필요하면 동료에게 물어봅니다. 민지는 도윤에게 메시지를 보내 "가격은 4,800원, 오트밀크 옵션은 유지"로 합의했습니다.
② 편집
파일을 열어 합의한 내용만 남기고, <<<<<<< · ======= · >>>>>>> 세 줄을 모두 지웁니다. 한쪽을 고르거나, 둘을 섞거나, 아예 새로 써도 됩니다.
③ git add
고친 파일을 git add합니다. 충돌 중에 add는 "이 파일은 해결했음"이라는 표시입니다.
④ git commit
모든 충돌 파일을 add했으면 git commit으로 병합을 마무리합니다. 병합 커밋 메시지는 git이 미리 채워 줍니다.

② 편집: 합의한 내용만 남기기

민지는 menu.md를 이렇게 고쳤습니다. 도윤의 가격(4,800원)과 민지의 옵션 설명을 섞은 결과입니다. 마커 세 줄은 모두 사라졌습니다.

text
# 오늘의 메뉴

- 아메리카노 4,000원
- 카페라테 4,800원 (오트밀크 변경 가능)
- 바닐라라테 5,000원

충돌 해결의 핵심이 여기 있습니다. "내 것 아니면 남의 것" 둘 중 하나를 고르는 게 아닙니다. 최종적으로 파일이 어떤 모습이어야 맞는지를 정하고, 그 모습대로 고치면 됩니다.

마커가 남았는지 확인하기

파일이 길면 마커 한 줄을 빠뜨리기 쉽습니다. 그런데 git은 마커가 남은 파일도 아무 말 없이 add하고 커밋해 줍니다. 마커도 그냥 글자니까요. 그래서 add하기 전에 한 번 확인하는 습관이 좋습니다. 가장 간단한 방법은 파일에서 마커를 검색하는 것입니다.

bash
grep -n '^<<<<<<<\|^=======\|^>>>>>>>' menu.md

아무것도 출력되지 않으면 마커가 모두 지워진 것입니다. git에게 확인을 맡길 수도 있습니다. 마커가 남은 채로 add해 버렸다면 git diff --cached --check가 알려 줍니다. 바닐라라테 충돌에서 마커를 그대로 둔 채 add했을 때의 실제 출력입니다.

bash
git diff --cached --check
text
menu.md:5: leftover conflict marker
menu.md:7: leftover conflict marker
menu.md:9: leftover conflict marker

"5·7·9번째 줄에 충돌 마커가 남았다(leftover)"는 뜻입니다. 이 경우 파일을 다시 고치고 add하면 됩니다.

③ add, ④ commit

마커가 없는 걸 확인했으면 add합니다.

bash
git add menu.md
git status
text
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" if you want to integrate the remote branch with yours)

All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
	modified:   menu.md

All conflicts fixed but you are still merging — "충돌은 모두 해결됐지만 아직 병합 중"이라는 뜻입니다. 커밋으로 마무리하라고(conclude merge) 알려 줍니다.

bash
git commit --no-edit
text
[main 6abec75] Merge branch 'main' of ../origin

--no-edit은 git이 미리 채워 준 병합 커밋 메시지를 그대로 쓰겠다는 옵션입니다. 그냥 git commit만 치면 에디터가 열리고 메시지가 미리 적혀 있는데, 저장하고 닫으면 같은 결과가 됩니다. 어떤 충돌을 어떻게 풀었는지 한 줄 덧붙여 두면 나중에 동료가 고마워합니다.

기록이 어떻게 합쳐졌는지 그래프로 보면 한눈에 들어옵니다.

bash
git log --oneline --graph
text
*   6abec75 Merge branch 'main' of ../origin
|\  
| * a594ee0 카페라테 가격 4,800원으로 인상
* | d97fb15 카페라테 가격 5,000원, 오트밀크 옵션 추가
|/  
* 9102ed9 메뉴판 첫 버전

민지의 커밋(d97fb15)과 도윤의 커밋(a594ee0)이 갈라졌다가 병합 커밋(6abec75)에서 다시 만났습니다. 두 사람의 기록이 모두 살아 있습니다. 이제 push하면 끝입니다.

bash
git push
text
To ../origin.git
   a594ee0..6abec75  main -> main

아까 거절됐던 push가 이번엔 통과했습니다. 도윤은 다음에 git pull만 하면 합쳐진 결과를 받습니다.

직접 해 보기: 충돌 마커 연습장

아래 연습장에 바닐라라테 충돌이 난 menu.md가 들어 있습니다. VS Code 버튼처럼 한쪽을 골라 보거나, 글상자를 직접 고친 뒤 검사하기를 눌러 보세요. 마커가 남았는지, 메뉴가 두 번 적히지는 않았는지 확인해 줍니다.

5. 멈추고 싶을 때, 그리고 VS Code에서 풀 때

일단 없던 일로: git merge --abort

충돌이 너무 많거나, 누구 말이 맞는지 당장 알 수 없거나, 일단 퇴근해야 할 때가 있습니다. 그럴 땐 병합을 취소하고 pull(또는 merge) 하기 직전 상태로 돌아갈 수 있습니다.

bash
git merge --abort
git status
text
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" if you want to integrate the remote branch with yours)

nothing to commit, working tree clean

파일에서 마커가 사라지고, 민지의 커밋만 있던 상태로 돌아왔습니다. 도윤과 이야기를 끝낸 뒤 다시 git pull하면 같은 충돌이 다시 나고, 그때 풀면 됩니다.

⚠️
pull로 생긴 충돌도 git merge --abort로 취소합니다. pull은 내부적으로 fetch + merge이기 때문입니다(pull.rebase false로 설정한 경우). 다만 병합을 시작하기 전에 커밋하지 않은 수정이 있었다면 그 부분은 깔끔하게 복구되지 않을 수 있으니, pull은 항상 작업을 커밋(또는 stash)한 깨끗한 상태에서 하는 습관을 들이세요.

pull 전에 작업 중이던 파일이 있다면

커밋하지 않은 수정이 있는 상태에서 pull하면, 그 파일이 들어오는 변경과 겹칠 때 git이 아예 병합을 시작하지 않고 거절합니다. 한국어 환경에서 본 실제 메시지입니다.

text
error: 다음 파일의 로컬 변경 사항을 병합 때문에 덮어 쓰게 됩니다:
	menu.md
병합하기 전에 변경 사항을 커밋하거나 스태시하십시오.
중지함

"병합하면 네가 저장 안 한 수정을 덮어쓰게 되니, 먼저 커밋하거나 스태시(stash)하라"는 뜻입니다. git이 내 작업을 지키려고 멈춘 것이니 고마운 에러입니다. 영어로는 Your local changes to the following files would be overwritten by merge로 시작합니다. stash는 2부 13장에서 다룹니다.

VS Code에서 버튼으로 풀기

VS Code로 충돌 난 파일을 열면, 충돌 부분이 색으로 칠해지고 바로 위에 작은 글자 버튼들이 나타납니다.

버튼하는 일이번 예에서 결과
Accept Current Change현재(내 쪽, HEAD) 내용만 남김카페라테 5,000원 (오트밀크 변경 가능)
Accept Incoming Change들어오는 쪽 내용만 남김카페라테 4,800원
Accept Both Changes두 내용을 위아래로 모두 남김카페라테 줄이 두 개 — 이번엔 틀린 선택
Compare Changes두 내용을 나란히 비교해 보여 줌—

버튼을 누르면 마커가 자동으로 지워지고 고른 내용만 남습니다. 한국어 언어 팩을 쓰면 버튼 이름이 우리말로 번역되어 보입니다. 이름은 달라도 순서와 뜻은 같습니다.

버튼이 편하긴 하지만 두 가지는 기억해 두세요.

  1. 버튼은 편집을 대신해 줄 뿐, 결정을 대신해 주지 않습니다. 이번 민지처럼 "둘을 섞은 결과"가 정답이면 버튼 하나로는 안 됩니다. 버튼으로 한쪽을 고른 뒤 직접 손보면 됩니다.
  2. 버튼을 누른 뒤에도 git add와 git commit은 해야 합니다. VS Code의 소스 제어(Source Control) 패널에서 파일 옆 +를 누르는 것이 add이고, 메시지 칸 위의 커밋 버튼이 commit입니다.

파일 전체를 한쪽으로: --ours / --theirs

충돌 난 파일이 이미지처럼 줄 단위로 합칠 수 없는 파일이거나, 파일 전체를 한쪽 것으로 통째로 정하고 싶을 때는 명령으로 고를 수 있습니다.

bash
git restore --theirs menu.md

--theirs는 들어오는 쪽, --ours는 내 쪽(HEAD) 버전으로 파일 전체를 덮어씁니다. 바닐라라테 충돌에서 --theirs를 쓰면 마지막 줄이 vanilla-price의 - 바닐라라테 5,300원이 됩니다. 이 역시 끝나면 git add·git commit으로 마무리합니다. 파일 안의 다른 충돌까지 한꺼번에 한쪽으로 정해지니, 무엇이 버려지는지 알 때만 쓰세요.

6. 충돌을 줄이는 습관

충돌은 협업하면 반드시 생깁니다. 없앨 수는 없지만, 작고 드물게 만들 수는 있습니다. 충돌은 두 사람이 같은 곳을 오래 따로 고칠수록 커집니다.

🔄
자주 pull하기
작업을 시작할 때, 그리고 push하기 직전에 pull합니다(3편의 하루 루틴). 동료의 변경을 일찍 받을수록 내가 그 위에서 작업하게 되어 같은 줄을 다르게 고칠 일이 줄어듭니다. 일주일 묵힌 뒤 pull하면 충돌도 일주일치가 쌓입니다.
✂️
작은 커밋, 짧은 브랜치
한 커밋에 한 가지 일만 담고, 브랜치는 며칠 안에 합칩니다. 충돌이 나도 범위가 좁아 어떤 의도였는지 금방 알 수 있습니다. "파일 전체 줄바꿈 정리" 같은 대규모 변경은 따로, 미리 알리고 합니다.
🤝
역할 나누고 말하기
"메뉴 가격은 민지, 페이지 디자인(index.html)은 도윤"처럼 담당을 나누면 같은 줄을 만질 일 자체가 줄어듭니다. 같은 파일을 고쳐야 하면 "지금 menu.md 고친다"고 한마디 남기세요. 가장 싼 충돌 방지책은 대화입니다.

그리고 충돌이 났을 때는 혼자 추측하지 말고 상대의 의도를 확인하세요. 도윤이 왜 4,800원으로 바꿨는지 모른 채 민지가 자기 가격으로 덮어쓰면, git 충돌은 풀렸어도 가게의 충돌은 풀리지 않습니다. 충돌 해결은 기술 문제이기 전에 소통 문제입니다.


2부. 되돌리기

7. 되돌리기 지도 — 상황부터 고르세요

git의 되돌리기 명령은 여러 개라서 헷갈립니다. 그런데 명령을 외우는 대신 "무엇을, 어디까지 되돌리고 싶은가" 를 먼저 정하면 명령은 저절로 골라집니다. 1편에서 본 네 공간(작업 폴더 → 스테이징 → 로컬 저장소 → 원격 저장소)을 떠올리면서 보세요.

되돌리기 지도 — 상황에 맞는 서랍 고르기크게 보기

상황명령무슨 일이위험도
파일을 고쳤는데 고치기 전으로 (add 전)git restore <파일>작업 폴더의 수정을 버림높음 — 되살릴 수 없음
add한 파일을 이번 커밋에서 빼기git restore --staged <파일>스테이징에서만 내림, 수정은 유지낮음
마지막 커밋 메시지 오타·빠뜨린 파일 (push 전)git commit --amend마지막 커밋을 고쳐 다시 만듦낮음 (push 전이라면)
이미 push한 커밋을 되돌리기git revert <해시>반대 내용의 새 커밋을 쌓음낮음 — 가장 안전
push 안 한 커밋 취소, 변경은 스테이징에git reset --soft HEAD~1커밋만 취소낮음
push 안 한 커밋 취소, 변경은 작업 폴더에git reset HEAD~1커밋 + 스테이징 취소중간
push 안 한 커밋과 수정 모두 버리기git reset --hard HEAD~1커밋 + 스테이징 + 작업 폴더 모두 되돌림높음
작업 중인 걸 잠깐 치워 두기git stash / git stash pop수정을 서랍에 넣었다 꺼냄낮음
reset --hard로 날린 커밋 되찾기git reflogHEAD가 지나간 기록에서 찾아 복구중간

그리고 이 표 전체를 한 문장으로 줄이면 이렇습니다.

⭐
황금률: push한 것은 revert, 안 한 것은 reset. 아직 내 컴퓨터에만 있는 커밋은 reset으로 지워도 아무도 모릅니다. 하지만 이미 push해서 동료가 받아 갔을 수 있는 커밋을 reset으로 지우면, 동료의 기록과 원격의 기록이 어긋나 모두가 곤란해집니다. 공유된 기록은 지우지 말고, 되돌리는 커밋을 하나 더 쌓으세요.

질문 몇 개로 내 상황에 맞는 명령을 찾아 주는 길잡이입니다.

이제 표의 줄을 하나씩, 실제로 실행해 보겠습니다.

8. 커밋하기 전: git restore와 git restore --staged

저장 안 한 수정 버리기: git restore <파일>

민지가 메뉴판에 케이크를 추가해 보다가, 실수로 아메리카노 가격에 0을 하나 더 붙였습니다. 뭐가 바뀌었는지 먼저 확인합니다.

bash
git status
git diff
text
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   menu.md

no changes added to commit (use "git add" and/or "git commit -a")
diff --git a/menu.md b/menu.md
index 8696ace..d6dd315 100644
--- a/menu.md
+++ b/menu.md
@@ -1,5 +1,6 @@
 # 오늘의 메뉴
 
-- 아메리카노 4,000원
+- 아메리카노 40,000원
 - 카페라테 4,800원 (오트밀크 변경 가능)
 - 바닐라라테 5,300원
+- 오늘의 케이크: 치즈케이크

git status가 이미 힌트를 줬습니다: use "git restore <file>..." to discard changes in working directory. 작업 폴더의 수정을 버리려면 restore를 쓰라는 뜻입니다.

bash
git restore menu.md
git status
text
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

menu.md가 마지막 커밋 상태로 돌아왔습니다. 아메리카노는 다시 4,000원이고, 케이크 줄도 사라졌습니다.

🚨
git restore <파일>은 되돌릴 수 없습니다. 커밋한 적 없는 수정은 git 어디에도 기록이 없기 때문입니다. 이번처럼 케이크 줄은 살리고 싶었다면 restore 대신 에디터에서 그 줄만 고치는 게 맞습니다. restore 전에는 꼭 git diff로 무엇이 사라질지 보세요. 예전 방식인 git checkout -- <파일>도 같은 일을 합니다. 오래된 자료에서 보이면 같은 뜻으로 읽으면 됩니다.

add 취소하기: git restore --staged <파일>

이번엔 케이크 줄을 제대로 추가하고, 개인 메모 파일 memo.txt도 만들었습니다. 그런데 git add .로 한꺼번에 add하다 보니 메모까지 스테이징에 올라갔습니다.

bash
git add .
git status
text
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   memo.txt
	modified:   menu.md

여기서도 git status가 방법을 알려 줍니다: use "git restore --staged <file>..." to unstage.

bash
git restore --staged memo.txt
git status
text
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   menu.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	memo.txt

memo.txt가 스테이징에서 내려와 "추적하지 않는 파일(Untracked)"로 돌아갔습니다. 파일 자체는 그대로 남아 있습니다. --staged는 스테이징만 건드리므로 안전합니다. 메모 같은 파일이 매번 걸린다면 2편의 .gitignore에 넣어 두세요.

🧩
restore의 두 얼굴 정리 — 옵션 없이 쓰면 작업 폴더를 되돌리고(수정 버림, 위험), --staged를 붙이면 스테이징만 되돌립니다(add 취소, 안전). 스테이징에 올린 수정을 작업 폴더에서까지 완전히 버리고 싶으면 두 단계를 차례로 하면 됩니다: 먼저 --staged, 그다음 옵션 없이.

9. 마지막 커밋 고치기: git commit --amend

민지가 케이크 줄을 커밋했는데 메시지에 오타가 났습니다.

bash
git commit -am "케이크 메뉴 추거"
git log --oneline -1
text
5ad9d8e 케이크 메뉴 추거

"추거"가 아니라 "추가"입니다. 아직 push하지 않았다면 --amend로 마지막 커밋을 고칠 수 있습니다.

bash
git commit --amend -m "케이크 메뉴 추가"
text
[main 78a9b8e] 케이크 메뉴 추가
 Date: Sun Sep 27 11:42:40 2026 +0900
 1 file changed, 1 insertion(+)

커밋 해시가 5ad9d8e에서 78a9b8e로 바뀐 것을 보세요. amend는 기존 커밋을 수정하는 게 아니라, 고친 내용으로 새 커밋을 만들어 바꿔 끼우는 동작입니다. Date: 줄은 원래 커밋한 시각을 그대로 유지했다는 표시입니다.

빠뜨린 파일 넣기

"오늘의 케이크" 안내를 index.html에도 넣어야 했는데 깜빡했습니다. 새 커밋을 하나 더 만들어도 되지만, 같은 일의 일부이니 마지막 커밋에 합치는 게 깔끔합니다.

bash
git add index.html
git commit --amend --no-edit
text
[main 5d7287d] 케이크 메뉴 추가
 Date: Sun Sep 27 11:42:40 2026 +0900
 2 files changed, 2 insertions(+)

--no-edit은 메시지는 그대로 두겠다는 뜻입니다. 이제 커밋 하나에 index.html과 menu.md 두 파일이 들어 있습니다(2 files changed).

⚠️
amend는 push하기 전에만. 해시가 바뀌므로, 이미 push한 커밋을 amend하면 원격에 있는 커밋과 내 커밋이 달라져 다음 push가 거절됩니다. 이걸 억지로 밀어붙이는 게 git push --force인데, 동료의 작업을 덮어쓸 수 있는 위험한 명령입니다. 이미 push했다면 오타는 그냥 두거나, 다음 커밋에서 바로잡으세요.

10. 이미 push한 커밋 되돌리기: git revert

민지가 아메리카노 가격을 고친다는 게 400원으로 잘못 적어 커밋하고, push까지 해 버렸습니다.

bash
git commit -am "아메리카노 가격 수정"
git push
text
To ../origin.git
   5d7287d..a4f08af  main -> main

도윤이 이미 pull해 갔을 수도 있습니다. 이럴 때 쓰는 것이 revert입니다. revert는 지정한 커밋과 정반대 내용의 새 커밋을 만들어 효과를 지웁니다. 비유하자면 장부에 잘못 적은 줄을 지우개로 지우는 게 아니라, 아래에 "위 항목 취소"라는 줄을 새로 적는 것입니다. 장부의 기록은 모두 남아 있으니 누구의 장부와도 어긋나지 않습니다.

먼저 되돌릴 커밋의 해시를 찾습니다.

bash
git log --oneline -3
text
a4f08af 아메리카노 가격 수정
5d7287d 케이크 메뉴 추가
38e25d2 Merge branch 'vanilla-price'

되돌릴 커밋은 a4f08af입니다. 마지막 커밋이므로 HEAD라고 써도 됩니다.

bash
git revert --no-edit HEAD
text
[main f21d3db] Revert "아메리카노 가격 수정"
 Date: Sun Sep 27 11:42:48 2026 +0900
 1 file changed, 1 insertion(+), 1 deletion(-)

--no-edit을 빼면 에디터가 열리고 Revert "아메리카노 가격 수정"이라는 메시지가 미리 적혀 있습니다. 왜 되돌리는지 한 줄 덧붙이면 더 좋습니다. 무슨 커밋이 생겼는지 봅시다.

bash
git show HEAD
text
commit f21d3dbb4112cdd0d07671d3f9ba5f0d9a3d40ce
Author: 민지 <minji@cafe.example>
Date:   Sun Sep 27 11:42:48 2026 +0900

    Revert "아메리카노 가격 수정"
    
    This reverts commit a4f08af9638f55981c57c84a077a31e9ba6fc087.

diff --git a/menu.md b/menu.md
index d6ff3d2..fe14ff9 100644
--- a/menu.md
+++ b/menu.md
@@ -1,6 +1,6 @@
 # 오늘의 메뉴
 
-- 아메리카노 400원
+- 아메리카노 4,000원
 - 카페라테 4,800원 (오트밀크 변경 가능)
 - 바닐라라테 5,300원
 - 오늘의 케이크: 치즈케이크 6,500원

400원을 4,000원으로 되돌리는 변경이 새 커밋으로 들어갔고, 메시지에는 어떤 커밋을 되돌렸는지(This reverts commit a4f08af…)까지 자동으로 적혔습니다. 이제 평소처럼 push합니다.

bash
git push
text
To ../origin.git
   a4f08af..f21d3db  main -> main

기록은 앞으로만 늘어났으니(a4f08af..f21d3db) 거절될 일이 없습니다. 도윤도 평소처럼 pull하면 됩니다.

bash
git log --oneline -4
text
f21d3db Revert "아메리카노 가격 수정"
a4f08af 아메리카노 가격 수정
5d7287d 케이크 메뉴 추가
38e25d2 Merge branch 'vanilla-price'

실수한 커밋도, 그걸 되돌린 커밋도 모두 기록에 남았습니다. "언제 무슨 실수가 있었고 어떻게 바로잡았는지"가 투명하게 보이는 것, 이것이 협업에서 revert를 쓰는 이유입니다.

💡
꼭 마지막 커밋이 아니어도 됩니다. git revert <해시>로 몇 커밋 전의 것도 콕 집어 되돌릴 수 있습니다. 다만 그 뒤에 같은 줄을 또 고친 커밋이 있으면, revert 도중에도 충돌이 날 수 있습니다. 푸는 법은 1부와 똑같습니다(고치고 → add → git revert --continue, 포기하려면 git revert --abort).

11. 로컬 커밋 취소: git reset --soft / --mixed / --hard

이번엔 아직 push하지 않은 커밋을 취소하는 경우입니다. 민지가 쿠키 메뉴를 추가하고 커밋했는데, 사장님이 "쿠키는 다음 달부터"라고 하십니다.

bash
git commit -am "쿠키 메뉴 추가"
git log --oneline -3
text
5045a58 쿠키 메뉴 추가
f21d3db Revert "아메리카노 가격 수정"
a4f08af 아메리카노 가격 수정

push 전이니 revert로 기록을 늘릴 필요 없이, reset으로 커밋 자체를 없앨 수 있습니다. reset은 "브랜치가 가리키는 커밋을 뒤로 옮기는" 명령입니다. 4편에서 브랜치는 커밋을 가리키는 포인터라고 했지요. reset은 그 포인터를 과거로 되감습니다.

HEAD~1은 "HEAD에서 한 칸 전 커밋"이라는 뜻입니다. 두 칸 전은 HEAD~2입니다.

reset에는 세 가지 모드가 있고, 차이는 취소한 커밋의 변경 내용을 어디까지 남겨 두느냐입니다.

모드③ 커밋 기록② 스테이징① 작업 폴더언제 쓰나
--soft커밋 취소변경 남음 (add된 상태)변경 남음메시지·구성만 다시 해서 바로 재커밋
--mixed (기본)커밋 취소비움변경 남음커밋을 여러 개로 나눠 다시 하고 싶을 때
--hard커밋 취소비움변경도 버림커밋과 수정을 완전히 없던 일로

아래 토글로 세 모드를 번갈아 눌러 보면 차이가 한눈에 들어옵니다.

실제로 하나씩 실행해 보겠습니다.

--soft: 커밋만 취소

bash
git reset --soft HEAD~1
git log --oneline -2
git status
text
f21d3db Revert "아메리카노 가격 수정"
a4f08af 아메리카노 가격 수정
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   menu.md

"쿠키 메뉴 추가" 커밋은 기록에서 사라졌지만, 변경은 Changes to be committed(스테이징)에 그대로 있습니다. 바로 git commit하면 다시 커밋됩니다. "커밋 메시지를 완전히 새로 쓰고 싶다", "직전 커밋 두세 개를 하나로 합치고 싶다(HEAD~3으로 soft reset 후 한 번에 커밋)" 같은 때 씁니다.

--mixed: 커밋과 add 취소 (옵션 없이 쓰면 이것)

bash
git reset HEAD~1
git status
text
Unstaged changes after reset:
M	menu.md
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   menu.md

no changes added to commit (use "git add" and/or "git commit -a")

이번엔 변경이 Changes not staged for commit, 즉 작업 폴더에만 남았습니다. 파일 내용은 그대로입니다. 한 커밋에 여러 일을 섞어 버려서 다시 나눠 add·commit하고 싶을 때 좋습니다.

--hard: 전부 되돌리기

bash
git reset --hard HEAD~1
text
HEAD is now at f21d3db Revert "아메리카노 가격 수정"
bash
git status
text
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

커밋도, 스테이징도, 파일 속 쿠키 줄도 모두 사라졌습니다. menu.md는 한 칸 전 커밋과 똑같아졌습니다.

🚨
git reset --hard는 커밋 안 한 수정을 영영 지웁니다. 취소한 커밋은 12장의 reflog로 한동안 되살릴 수 있지만, 작업 폴더에 있던 커밋하지 않은 수정은 git에 기록된 적이 없어 되살릴 방법이 없습니다. 실행 전에 반드시 git status로 저장 안 한 수정이 없는지 보고, 있으면 먼저 커밋하거나 stash하세요. 확신이 없으면 --soft나 --mixed부터 쓰세요. 그 둘은 파일을 하나도 지우지 않습니다.

push한 커밋을 reset하면 생기는 일

황금률을 어기면 어떻게 되는지 직접 봅시다. 이미 push한 커밋을 reset으로 지우고 다시 push하면 이렇게 거절됩니다.

text
To ../origin.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '../origin.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

원격에는 그 커밋이 여전히 있는데 내 쪽에는 없으니, git 입장에선 "네가 뒤처졌다"로 보입니다. 여기서 시키는 대로 pull하면 지운 커밋이 다시 돌아오고, --force로 밀어붙이면 원격의 기록을 덮어써서 그사이 동료가 올린 커밋까지 날릴 수 있습니다. 그래서 push한 것은 revert입니다. 실수로 이 상태가 됐다면 git reset --hard origin/main으로 원격과 같은 상태로 되돌린 뒤 revert를 쓰세요(물론 이 명령도 커밋 안 한 수정을 지우니 status부터 확인).

12. 구명줄 git reflog — 사라진 커밋 되찾기

이제 가장 무서운 상황입니다. 민지가 쿠키 메뉴와 영업시간 안내를 커밋해 두었는데,

bash
git log --oneline -3
text
2e5f045 영업시간 안내 추가
9da2c61 쿠키 메뉴 추가
f21d3db Revert "아메리카노 가격 수정"

한 칸만 되돌린다는 게 HEAD~2를 쳐서 두 커밋을 한꺼번에 날렸습니다.

bash
git reset --hard HEAD~2
git log --oneline -2
text
HEAD is now at f21d3db Revert "아메리카노 가격 수정"
f21d3db Revert "아메리카노 가격 수정"
a4f08af 아메리카노 가격 수정

git log에서 두 커밋이 감쪽같이 사라졌습니다. 하지만 커밋은 아직 저장소 안에 있습니다. 단지 어떤 브랜치도 가리키지 않아 log에 안 보일 뿐입니다. 그걸 찾는 도구가 reflog(참조 기록) 입니다. reflog는 HEAD가 옮겨 다닌 모든 자리, 즉 커밋·reset·merge·switch 등을 할 때마다 한 줄씩 적어 두는 내 컴퓨터만의 일지입니다.

reflog — 사라진 커밋을 끌어올리는 구명줄크게 보기

bash
git reflog -4
text
f21d3db HEAD@{0}: reset: moving to HEAD~2
2e5f045 HEAD@{1}: commit: 영업시간 안내 추가
9da2c61 HEAD@{2}: commit: 쿠키 메뉴 추가
f21d3db HEAD@{3}: reset: moving to HEAD~1

위에서부터 최근 순입니다.

  • HEAD@{0} — 지금. 방금 한 reset: moving to HEAD~2
  • HEAD@{1} — 그 직전. 영업시간 안내 추가 커밋(2e5f045)에 있었음 ← 여기로 돌아가면 됩니다
  • HEAD@{2} — 그 전. 쿠키 메뉴 추가 커밋

reset 직전 자리인 HEAD@{1}(또는 해시 2e5f045)로 다시 reset합니다.

bash
git reset --hard HEAD@{1}
git log --oneline -3
text
HEAD is now at 2e5f045 영업시간 안내 추가
2e5f045 영업시간 안내 추가
9da2c61 쿠키 메뉴 추가
f21d3db Revert "아메리카노 가격 수정"

두 커밋이 모두 돌아왔습니다. 파일 내용도 복구됐습니다.

지금 브랜치를 옮기는 게 부담스럽다면, 찾은 커밋에 새 브랜치 이름을 붙여 구조할 수도 있습니다. 이 방법은 현재 작업 폴더를 건드리지 않아 더 안전합니다.

bash
git branch rescue 9da2c61
git log --oneline -1 rescue
text
9da2c61 쿠키 메뉴 추가
🛟
reflog가 구할 수 있는 것과 없는 것
✅ 한 번이라도 커밋했던 것 — reset --hard로 날린 커밋, amend 전의 원래 커밋, 지운 브랜치의 커밋
❌ 커밋한 적 없는 수정 — restore나 reset --hard로 버린 작업 폴더의 수정
❌ 다른 사람의 컴퓨터에서 일어난 일 — reflog는 내 저장소에만 있고 push되지 않음
⏳ 영원하지 않음 — 기본 설정에서 이렇게 떨어져 나간 커밋의 기록은 30일, 그 밖의 기록은 90일이 지나면 정리될 수 있음
그래서 교훈은 하나입니다. 자주 커밋하세요. 커밋만 해 두면 git은 거의 무엇이든 되살려 줍니다.

13. 잠깐 치워 두기: git stash

커밋을 무사히 되찾은 민지가 이번엔 가을 신메뉴를 준비하며 menu.md를 고치고 new-menu-draft.md 초안도 만들던 참입니다. 그때 도윤에게서 연락이 옵니다. "README에 가게 연락처 좀 급히 넣어 줄 수 있어요?"

지금 작업은 반쯤 한 상태라 커밋하기 애매합니다. 이럴 때 stash(서랍에 넣어 두기) 를 씁니다. 수정 내용을 임시 서랍에 넣고 작업 폴더를 깨끗하게 만든 뒤, 급한 일을 마치고 다시 꺼내는 것입니다.

bash
git status --short
text
 M menu.md
?? new-menu-draft.md

M은 수정된 파일, ??는 새로 만들어 아직 추적하지 않는 파일입니다. 그냥 git stash를 해 보면 함정이 하나 보입니다.

bash
git stash
git status --short
text
Saved working directory and index state WIP on main: 2e5f045 영업시간 안내 추가
?? new-menu-draft.md

menu.md는 서랍에 들어갔지만, 새 파일 new-menu-draft.md는 그대로 남았습니다. stash는 기본적으로 git이 추적 중인 파일만 넣기 때문입니다. 새 파일까지 넣으려면 -u(untracked 포함)를 붙입니다. 다시 꺼낸 뒤 제대로 넣어 봅시다. 나중에 뭘 넣었는지 알아보기 쉽게 -m으로 이름도 붙입니다.

bash
git stash pop
git stash push -u -m "가을 메뉴 작업 중"
git status
git stash list
text
Saved working directory and index state On main: 가을 메뉴 작업 중
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
stash@{0}: On main: 가을 메뉴 작업 중

작업 폴더가 깨끗해졌고, 서랍 목록(stash list)에 stash@{0}으로 들어가 있습니다. 이제 마음 놓고 급한 일을 처리합니다.

bash
git commit -am "README에 문의 연락처 추가"

그리고 서랍에서 작업을 다시 꺼냅니다.

bash
git stash pop
text
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   menu.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	new-menu-draft.md

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (5e3b125065e82db50d03006e049d3816409add1d)

menu.md 수정과 new-menu-draft.md 초안이 모두 돌아왔고, 마지막 줄 Dropped refs/stash@{0}은 "꺼냈으니 서랍에서 지웠다"는 뜻입니다.

명령하는 일
git stash push -u -m "이름"추적 안 하는 새 파일까지 포함해 서랍에 넣고 이름 붙이기
git stash list서랍 목록 보기 (가장 최근이 stash@{0})
git stash pop가장 최근 것을 꺼내 펼치고 서랍에서 지움
git stash apply꺼내 펼치되 서랍에도 남겨 둠
git stash drop꺼내지 않고 서랍에서 지움 (내용 버림)
💡
stash는 잠깐만. 서랍은 편하지만 쌓이기 시작하면 뭐가 뭔지 잊어버립니다. 며칠 이상 묵힐 작업이라면 stash 대신 브랜치를 하나 만들어 커밋해 두는 편이 안전합니다(4편). 그리고 pop할 때 그사이 같은 줄이 바뀌었으면 충돌이 날 수 있는데, 푸는 법은 1부와 같습니다. 충돌이 나면 stash는 서랍에 남아 있으니, 해결한 뒤 git stash drop으로 직접 지우면 됩니다.
🤖
AI 에이전트와 함께 쓸 때
· 충돌 해결은 맡겨도, 결정은 검토하세요. Claude Code 같은 에이전트는 충돌 마커를 읽고 양쪽 의도를 추정해 꽤 잘 풀어 줍니다. 하지만 "카페라테는 4,800원인가 5,000원인가"처럼 코드 밖의 사정이 걸린 결정은 에이전트가 알 수 없습니다. 해결 후 git diff HEAD나 병합 커밋의 git show로 무엇을 남기고 버렸는지 꼭 확인하세요. 에이전트에게는 "충돌 난 파일마다 양쪽 내용과 어떻게 합쳤는지 설명해 줘"라고 요청하면 검토가 쉬워집니다.
· 파괴적인 명령에는 확인을 거치게 하세요. git reset --hard, git checkout ., git restore ., git clean -fd(추적 안 하는 파일 삭제), git push --force는 커밋 안 한 작업이나 동료의 기록을 되돌릴 수 없게 지웁니다. 에이전트가 이런 명령을 제안하면 실행 전에 "무엇이 사라지는지 먼저 보여 줘"라고 하세요. git clean은 -n을 붙이면 지울 목록만 보여 줍니다(예: Would remove new-menu-draft.md).
· 되돌리기의 황금률은 에이전트에게도 그대로입니다. "push한 커밋은 revert로, push 안 한 커밋만 reset으로"를 프로젝트 규칙으로 알려 두면 사고를 크게 줄일 수 있습니다. 에이전트의 권한을 설정으로 제한하는 방법은 7편에서 다룹니다.

자주 하는 질문(FAQ)

Q1. 충돌이 났는데 어느 쪽이 "내 것"인지 헷갈립니다.

<<<<<<< HEAD부터 =======까지가 지금 서 있는 브랜치(내 쪽), =======부터 >>>>>>>까지가 들어오는 쪽입니다. pull이면 들어오는 쪽이 원격(동료), merge면 합치려는 브랜치입니다. >>>>>>> 뒤에 붙은 이름(브랜치명이나 커밋 해시)으로도 확인할 수 있습니다.

Q2. 충돌을 푸는 도중에 파일을 엉망으로 만들었어요. 처음부터 다시 하고 싶습니다.

git merge --abort로 병합 전 상태로 돌아간 뒤, 다시 git pull(또는 git merge)하면 같은 충돌이 처음 상태로 다시 나타납니다. 병합 자체는 유지한 채 특정 파일만 충돌 직후 모습으로 돌리고 싶다면 git checkout --merge menu.md도 있지만, 입문 단계에서는 abort 후 다시 시작하는 편이 이해하기 쉽습니다.

Q3. revert와 reset, 결국 뭐가 다른가요?

revert는 기록을 앞으로 늘려서 되돌립니다(반대 커밋 추가). 기록이 지워지지 않으니 공유된 커밋에도 안전합니다. reset은 기록을 뒤로 되감아서 되돌립니다(커밋 제거). 내 컴퓨터에만 있는 커밋에 쓰면 깔끔하지만, 공유된 커밋에 쓰면 다른 사람과 기록이 어긋납니다. 그래서 황금률: push한 것은 revert, 안 한 것은 reset.

Q4. git reset --hard를 했더니 커밋 안 한 작업까지 날아갔어요. 방법이 없나요?

안타깝지만 git으로는 되살릴 수 없습니다. 커밋한 적 없는 수정은 git에 기록이 없기 때문입니다. 에디터의 되돌리기(실행 취소) 기록이나 로컬 히스토리 기능, 운영체제의 백업(Time Machine 등)에 남아 있는지 확인해 보세요. 다음부터는 위험한 명령 전에 git status 확인, 또는 일단 git stash로 치워 두는 습관을 들이세요. stash에 넣은 것은 커밋처럼 기록되어 되살릴 수 있습니다.

Q5. git checkout으로 되돌리라는 글이 많던데 restore와 같은 건가요?

네. 예전에는 git checkout이 브랜치 이동과 파일 되돌리기를 모두 맡았는데, 헷갈린다는 이유로 git 2.23부터 브랜치 이동은 git switch, 파일 되돌리기는 git restore로 나뉘었습니다. git checkout -- menu.md는 git restore menu.md와 같은 일을 합니다. 특히 git checkout .은 작업 폴더의 모든 수정을 버리는 명령이니, 오래된 글을 따라 할 때 주의하세요.

Q6. 충돌이 날 때마다 너무 스트레스예요. 아예 안 나게 할 수는 없나요?

두 사람 이상이 같은 파일을 고치는 한 완전히 없앨 수는 없습니다. 하지만 6장의 세 가지 — 자주 pull하기, 작은 커밋과 짧은 브랜치, 역할 분담과 소통 — 만 지켜도 충돌은 드물어지고, 나더라도 한두 줄짜리로 작아집니다. 작은 충돌은 1분이면 풉니다.

이번 편 요약 (치트시트)

하고 싶은 일명령메모
충돌 상태·충돌 파일 확인git status"both modified"가 충돌 파일
마커 남았는지 검사git diff --check / git diff --cached --check"leftover conflict marker"
충돌 해결 표시git add <파일>마커 세 줄 모두 지운 뒤
병합 마무리git commit메시지는 미리 채워짐
병합 취소 (pull·merge 전으로)git merge --abortpull 충돌에도 사용
파일 통째로 한쪽 선택git restore --ours / --theirs <파일>이후 add·commit
저장 안 한 수정 버리기git restore <파일>되살릴 수 없음
add 취소git restore --staged <파일>파일 내용은 유지
마지막 커밋 고치기git commit --amendpush 전에만
push한 커밋 되돌리기git revert <해시>새 커밋으로 안전하게
로컬 커밋 취소 (스테이징 유지)git reset --soft HEAD~1파일 안 지움
로컬 커밋 취소 (작업 폴더 유지)git reset HEAD~1기본값 --mixed
로컬 커밋과 수정 모두 버리기git reset --hard HEAD~1status 먼저 확인
잠깐 치워 두기 / 꺼내기git stash push -u -m "이름" / git stash pop-u는 새 파일 포함
사라진 커밋 찾기git reflog → git reset --hard HEAD@{N}커밋했던 것만 구조 가능

git의 역사나 내부 구조(커밋이 실제로 어떻게 저장되는지, reflog가 왜 가능한지)까지 궁금하다면 Git 완전 정복을 함께 읽어 보세요.

다음 편 예고

이제 민지는 충돌이 나도, 실수를 해도 당황하지 않습니다. 그런데 도윤이 이런 제안을 합니다. "main에 바로 push하지 말고, 브랜치에서 작업한 다음에 Pull Request로 서로 봐 주고 합치면 어때요? 그러면 400원 같은 실수는 push 전에 잡히잖아요."

6편 「Pull Request」 에서는 GitHub Flow라는 협업 흐름을 따라, 브랜치를 올리고 Pull Request를 열고 리뷰를 주고받고 합치는 과정을 배웁니다. merge·squash·rebase 세 가지 합치기 방식의 차이와, main을 실수로부터 지키는 보호 규칙도 함께 다룹니다.