실수하지 않는 것보다 중요한 것
Git을 잘한다는 것은 실수하지 않는다는 뜻이 아닙니다. 오히려 반대에 가깝습니다. Git을 잘하는 사람은 실수했을 때 무엇을 되돌려야 하는지 알고, 안전한 방법으로 복구할 수 있습니다.
협업을 하다 보면 충돌은 자연스럽게 발생합니다. 내가 나쁜 코드를 작성해서가 아닙니다. 여러 사람이 같은 프로젝트를 동시에 수정하기 때문입니다. 특히 같은 파일의 같은 줄을 서로 다르게 바꾸면 Git은 어느 쪽을 선택해야 할지 스스로 판단하지 못합니다. 이때 발생하는 것이 merge conflict, 즉 병합 충돌입니다.
이번 장에서는 충돌을 읽고 해결하는 방법, 커밋 전 변경 사항을 되돌리는 방법, reset과 revert의 차이, 그리고 하던 작업을 잠시 보관하는 stash까지 다룹니다.

충돌은 왜 발생할까
예를 들어 README.md 파일에 다음 문장이 있다고 해봅시다.
이 프로젝트는 할 일 관리 앱입니다.현재 main 브랜치에서 동료가 이 문장을 이렇게 바꾸고 커밋했습니다.
이 프로젝트는 팀을 위한 할 일 관리 앱입니다.그런데 나는 feature-intro 브랜치에서 같은 줄을 이렇게 바꾸었습니다.
이 프로젝트는 개인 일정 관리를 위한 앱입니다.두 변경 모두 같은 파일의 같은 줄을 수정했습니다. Git 입장에서는 둘 다 정상적인 변경입니다. 하지만 둘을 자동으로 합치기는 어렵습니다.
동료의 문장을 남겨야 할까?
내 문장을 남겨야 할까?
둘을 섞어서 새 문장을 만들어야 할까?
Git은 이 판단을 사람에게 맡깁니다. 그래서 충돌이 발생하면 파일 안에 충돌 표시를 남겨 둡니다.
충돌 표시 읽기
브랜치를 병합하다가 충돌이 나면 파일 안에 다음과 비슷한 표시가 생깁니다.
<<<<<<< HEAD
이 프로젝트는 팀을 위한 할 일 관리 앱입니다.
=======
이 프로젝트는 개인 일정 관리를 위한 앱입니다.
>>>>>>> feature-intro처음 보면 복잡해 보이지만 구조는 단순합니다.
<<<<<<< HEAD아래: 현재 내가 있는 브랜치의 내용=======: 두 내용을 나누는 구분선>>>>>>> feature-intro위: 병합하려는 다른 브랜치의 내용
여기서 HEAD는 지금 내가 작업 중인 위치를 뜻합니다. 예를 들어 main 브랜치에서 feature-intro를 병합하고 있다면, HEAD 쪽은 main의 내용이고 아래쪽은 feature-intro의 내용입니다.
충돌 해결은 이 표시를 지우고 최종으로 남길 내용만 정리하는 작업입니다.
예를 들어 두 내용을 합쳐 이렇게 바꿀 수 있습니다.
이 프로젝트는 팀과 개인이 함께 사용할 수 있는 할 일 관리 앱입니다.중요한 점은 충돌 표시 자체를 남기면 안 된다는 것입니다. <<<<<<<, =======, >>>>>>>가 파일에 남아 있으면 아직 충돌이 해결되지 않은 상태입니다.
충돌 해결 실습 흐름
실제로 충돌이 생겼을 때는 보통 다음 순서로 해결합니다.
먼저 현재 상태를 확인합니다.
git statusGit은 어떤 파일에서 충돌이 났는지 알려줍니다.
both modified: README.md이 말은 양쪽 브랜치에서 README.md를 모두 수정했다는 뜻입니다.
이제 파일을 열어 충돌 표시를 확인합니다.
<<<<<<< HEAD
이 프로젝트는 팀을 위한 할 일 관리 앱입니다.
=======
이 프로젝트는 개인 일정 관리를 위한 앱입니다.
>>>>>>> feature-intro원하는 최종 문장만 남깁니다.
이 프로젝트는 팀과 개인이 함께 사용할 수 있는 할 일 관리 앱입니다.수정이 끝났다면 파일을 다시 스테이징합니다.
git add README.md그리고 병합 결과를 커밋합니다.
git commit상황에 따라 Git이 자동으로 커밋 메시지를 제안하기도 합니다. 그대로 저장해도 괜찮고, 이해하기 쉽게 바꿔도 됩니다.
예를 들면 다음처럼 작성할 수 있습니다.
git commit -m "Resolve README merge conflict"충돌 해결의 핵심은 어렵지 않습니다.
충돌 난 파일을 연다.
HEAD,=======, 브랜치명 표시를 읽는다.최종으로 남길 내용만 정리한다.
git add로 해결 완료를 알린다.다시 커밋한다.
충돌 해결을 중단하고 싶을 때
충돌을 해결하다 보면 "일단 병합을 취소하고 처음으로 돌아가고 싶다"는 상황이 생길 수 있습니다. 이때는 파일을 아무렇게나 지우기보다, 진행 중인 작업 종류에 맞는 중단 명령을 사용하는 편이 안전합니다.
병합 중이었다면 다음 명령을 사용할 수 있습니다.
git merge --abort이 명령은 진행 중이던 merge를 중단하고, 가능하면 병합하기 전 상태로 되돌립니다.
rebase 중이었다면 명령이 다릅니다.
git rebase --abort초보자는 충돌이 났을 때 먼저 git status를 확인하세요. Git은 현재 merge 중인지, rebase 중인지, 어떤 파일을 고쳐야 하는지 알려 줍니다. 상태를 보고 해결할지, 중단할지 결정하면 됩니다.
아직 커밋하지 않은 변경 되돌리기: git restore
작업하다 보면 파일을 잘못 수정할 때가 있습니다. 아직 커밋하지 않았고 충돌 해결 중도 아니라면 비교적 쉽게 되돌릴 수 있습니다.
예를 들어 app.js를 수정했는데 마음에 들지 않는다고 해봅시다. 먼저 상태를 확인합니다.
git status다음처럼 표시될 수 있습니다.
modified: app.js이 변경을 마지막 커밋 상태로 되돌리려면 다음 명령어를 사용합니다.
git restore app.js그러면 app.js의 커밋하지 않은 수정 내용이 사라집니다.
주의할 점이 있습니다. git restore로 되돌린 내용은 기본적으로 복구하기 어렵습니다. 아직 커밋하지 않은 변경은 Git에 안전하게 저장된 기록이 아니기 때문입니다.
따라서 명령어를 실행하기 전에 꼭 확인하세요.
git diff app.jsgit diff는 아직 커밋하지 않은 변경 내용을 보여줍니다. 내용을 보고 정말 버려도 되는 변경인지 확인한 뒤 git restore를 사용하는 습관이 좋습니다.
여러 파일을 모두 되돌리고 싶다면 다음처럼 실행할 수 있습니다.
git restore .하지만 초보자라면 처음에는 파일명을 직접 지정하는 방식이 안전합니다.
git restore README.md한 파일씩 확인하며 되돌리는 편이 실수를 줄입니다.
스테이징한 파일 되돌리기
git add까지 했지만 아직 커밋하지 않은 경우도 있습니다. 이때는 변경 내용이 사라진 것이 아니라, 커밋 후보에 올라간 상태입니다.
예를 들어 다음 명령어를 실행했다고 해봅시다.
git add app.js그런데 아직 커밋하고 싶지 않다면 스테이징만 취소할 수 있습니다.
git restore --staged app.js이 명령은 파일 내용을 되돌리지 않습니다. 단지 git add한 상태만 취소합니다.
정리하면 다음과 같습니다.
git restore app.js: 파일 내용을 마지막 커밋 상태로 되돌림git restore --staged app.js: 스테이징만 취소하고 파일 내용은 유지
이 둘은 자주 헷갈립니다. 파일 내용까지 버릴 것인지, 커밋 후보에서만 뺄 것인지 먼저 구분해야 합니다.
reset과 revert의 차이
커밋한 뒤에 실수를 발견할 수도 있습니다. 이때 자주 나오는 명령어가 git reset과 git revert입니다.
둘 다 과거 상태로 돌아가는 데 쓰이지만 방식이 다릅니다.
git reset: 히스토리 자체를 이전 위치로 옮김git revert: 기존 커밋은 남겨 두고, 되돌리는 새 커밋을 만듦
비유하면 이렇습니다.
reset은 공책에서 마지막 페이지를 찢어내는 것에 가깝습니다. 기록 자체가 바뀝니다.
revert는 마지막 페이지는 그대로 두고, 다음 페이지에 "앞의 내용을 취소한다"라고 새로 적는 방식입니다. 기록이 남습니다.
개인 브랜치에서 아직 다른 사람에게 공유하지 않은 커밋이라면 reset을 사용할 수 있습니다.
git reset --soft HEAD~1이 명령은 마지막 커밋 하나를 취소하되, 파일 변경 내용은 남겨 둡니다. 다시 수정해서 새 커밋을 만들고 싶을 때 유용합니다.
반대로 변경 내용까지 모두 버리고 마지막 커밋 전 상태로 돌아가고 싶다면 다음 명령을 사용할 수 있습니다.
git reset --hard HEAD~1하지만 --hard는 매우 조심해야 합니다. 커밋과 작업 내용이 함께 사라질 수 있습니다. 초보자라면 실행 전 반드시 현재 브랜치와 상태를 확인하세요.
git status
git log --oneline이미 GitHub에 푸시했고 다른 사람과 공유한 커밋이라면 보통 revert가 더 안전합니다.
git revert 커밋해시예를 들어 커밋 목록이 다음과 같다고 해봅시다.
git log --onelinea1b2c3d Add wrong login message
9f8e7d6 Add login page잘못된 커밋이 a1b2c3d라면 다음처럼 되돌립니다.
git revert a1b2c3d그러면 Git은 a1b2c3d의 변경을 취소하는 새 커밋을 만듭니다. 기존 기록은 사라지지 않습니다. 협업에서는 이 점이 중요합니다. 이미 공유된 히스토리를 마음대로 바꾸면 다른 사람의 작업 흐름이 꼬일 수 있기 때문입니다.
간단히 기억하세요.
혼자 작업 중이고 아직 푸시하지 않았다면
reset을 고려이미 공유한 커밋이라면
revert가 안전reset --hard는 정말 버려도 되는지 확인한 뒤 사용
하던 작업을 잠시 보관하기: git stash
작업 중 갑자기 다른 브랜치로 이동해야 할 때가 있습니다.
예를 들어 feature-profile 브랜치에서 프로필 화면을 수정하고 있는데, 급하게 main 브랜치에서 버그를 확인해야 한다고 해봅시다.
하지만 아직 작업이 끝나지 않았습니다. 커밋하기에는 애매합니다.
이때 바로 브랜치를 이동하려고 하면 Git이 막을 수 있습니다.
git switch main파일 변경 내용이 다른 브랜치 이동에 영향을 줄 수 있기 때문입니다.
이럴 때 사용하는 것이 git stash입니다. stash는 현재 작업 내용을 임시 보관함에 넣어 두는 기능입니다.
git stash그러면 작업 중이던 변경 사항이 잠시 사라지고, 마지막 커밋 상태처럼 깨끗해집니다.
이제 다른 브랜치로 이동할 수 있습니다.
git switch main급한 일을 처리한 뒤 다시 원래 브랜치로 돌아옵니다.
git switch feature-profile그리고 보관해 둔 작업을 다시 꺼냅니다.
git stash pop그러면 아까 작업하던 내용이 다시 적용됩니다.
보관 목록을 확인하고 싶다면 다음 명령어를 사용합니다.
git stash list여러 작업을 보관할 수도 있습니다. 메시지를 붙이면 나중에 알아보기 쉽습니다.
git stash push -m "profile page layout work"stash는 매우 유용하지만 영구 저장소는 아닙니다. 오래 보관할 중요한 작업이라면 커밋으로 남기는 편이 더 안전합니다.
실전 상황으로 정리하기
다음 상황을 생각해봅시다.
당신은 feature-login 브랜치에서 로그인 문구를 수정하고 있습니다. 그런데 동료가 main 브랜치에서 같은 파일의 같은 줄을 이미 수정했습니다. 최신 main을 가져와 병합하자 충돌이 발생했습니다.
먼저 상태를 확인합니다.
git status충돌 난 파일을 열어 봅니다.
<<<<<<< HEAD
로그인 후 팀 대시보드로 이동합니다.
=======
로그인하면 개인 홈 화면으로 이동합니다.
>>>>>>> main둘 중 하나만 선택할 수도 있고, 두 의미를 합쳐 새 문장으로 만들 수도 있습니다.
로그인 후 사용자 유형에 맞는 화면으로 이동합니다.충돌 표시를 모두 지운 뒤 저장합니다.
git add README.md
git commit -m "Resolve login guide conflict"만약 충돌 해결을 계속하기 어렵다면 병합 자체를 중단할 수 있습니다.
git merge --abort이 명령은 "이번 병합은 없던 일로 하고, 다시 정리해서 시도하겠다"는 선택입니다. 반대로 단순히 커밋하지 않은 일반 수정만 버리고 싶을 때는 git diff로 확인한 뒤 git restore 파일명을 사용합니다.
아직 완료되지 않은 작업을 잠시 치워 두고 다른 브랜치로 가야 한다면 stash를 사용합니다.
git stash
git switch main다시 돌아와 작업을 꺼냅니다.
git switch feature-login
git stash pop이 흐름을 익히면 실수를 두려워하지 않아도 됩니다. Git은 작업을 망치지 않기 위한 도구이기도 하지만, 더 중요한 것은 망가진 것처럼 보이는 상황에서 다시 정리할 수 있게 해주는 도구입니다.
꼭 기억할 명령어
충돌과 실수 복구에서 자주 쓰는 명령어는 많지 않습니다.
git status현재 상태를 확인합니다. 충돌이 났을 때 가장 먼저 실행합니다.
git diff아직 커밋하지 않은 변경 내용을 확인합니다.
git restore 파일명커밋하지 않은 일반 변경을 되돌립니다.
git restore --staged 파일명스테이징만 취소합니다.
git merge --abort진행 중인 병합을 중단하고 병합 전 상태로 돌아갑니다.
git reset --soft HEAD~1마지막 커밋을 취소하되 변경 내용은 남깁니다.
git revert 커밋해시공유된 커밋을 안전하게 되돌리는 새 커밋을 만듭니다.
git stash하던 작업을 잠시 보관합니다.
git stash pop보관한 작업을 다시 꺼냅니다.
처음부터 모든 명령어를 완벽하게 외울 필요는 없습니다. 중요한 것은 상황을 구분하는 것입니다.
아직 커밋하지 않았는가?
이미 커밋했는가?
GitHub에 푸시했는가?
다른 사람과 공유한 기록인가?
지금 merge나 rebase가 진행 중인가?
이 질문에 답할 수 있으면 어떤 명령어를 써야 할지 훨씬 쉽게 판단할 수 있습니다. Git의 핵심은 기록입니다. 기록을 어떻게 남기고, 언제 고치고, 언제 새 기록으로 되돌릴지 알면 협업 중 생기는 대부분의 실수를 안전하게 처리할 수 있습니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.
