며칠 밤을 새워 만든 기능을 커밋한 순간, 뭔가 잘못됐다는 걸 깨달은 적이 있을 것이다. 방금 지운 파일이 사실은 지우면 안 되는 것이었거나, 커밋 메시지를 엉뚱하게 적었거나, 아직 손도 안 댄 다른 사람의 코드까지 통째로 덮어써 버렸거나. 그때 머릿속에 떠오르는 단어는 하나다. 되돌리기.
그런데 막상 검색해 보면 reset, revert, restore, checkout, stash… 되돌리는 명령어가 왜 이렇게 많은지 혼란스럽다. 급한 마음에 아무거나 복사해서 붙여 넣었다가 오히려 작업을 통째로 날려 먹는 일도 흔하다. 이 글에서는 상황별로 "지금 나는 무엇을 되돌리고 싶은가"를 기준으로, 헷갈리는 Git 되돌리기 명령을 정리해 본다.
먼저 알아야 할 세 개의 공간
Git에서 파일은 항상 세 개의 공간을 오간다. 이 구조를 모르면 어떤 되돌리기 명령을 써야 하는지 영원히 헷갈린다.
첫째는 작업 디렉터리(Working Directory)다. 지금 에디터로 열어서 실제로 고치고 있는 파일들이 여기 있다. 둘째는 스테이징 영역(Staging Area, 또는 Index)이다. git add로 "이건 다음 커밋에 포함할게"라고 표시해 둔 파일들이 잠시 머무는 대기실이다. 셋째는 저장소(Repository)로, git commit으로 확정되어 역사에 기록된 상태다.
되돌리기의 핵심은 딱 하나다. "나는 지금 어느 공간을 되돌리려는가?"
이 질문에 답할 수 있으면 명령어는 저절로 정해진다. 아직 add도 안 한 파일인지, add는 했지만 commit은 안 한 상태인지, 이미 commit까지 끝난 상태인지에 따라 완전히 다른 도구를 써야 하기 때문이다.
아직 커밋 전 — 수정한 내용을 버리고 싶다
파일을 잔뜩 고쳐 놨는데 "아, 이건 그냥 처음부터 다시 하는 게 낫겠다" 싶을 때가 있다. 아직 git add도 하지 않은, 작업 디렉터리에만 있는 변경을 원래대로 돌리는 경우다.
# 특정 파일 하나만 마지막 커밋 상태로 되돌리기
git restore app.php
# 변경한 모든 파일을 한꺼번에 되돌리기
git restore .여기서 반드시 기억할 점이 있다. git restore로 되돌린 변경은 복구할 수 없다. 스테이징도 커밋도 안 한 변경은 Git이 기록해 둔 적이 없으므로, 되살릴 히스토리 자체가 존재하지 않는다. 정말 버려도 되는지 한 번 더 확인하고 실행하는 습관을 들이자. 예전에는 이 용도로 git checkout -- 파일명을 썼지만, checkout이 브랜치 이동과 파일 복원이라는 전혀 다른 두 일을 겸하는 바람에 혼란이 많아, 요즘 Git은 restore로 역할을 분리했다.
add는 했는데 커밋 전 — 스테이징만 취소하고 싶다
git add를 했더니 실수로 로그 파일이나 .env 같은 걸 같이 올려 버렸다. 파일 내용은 그대로 두고 "다음 커밋 대상"에서만 빼고 싶을 때다.
# 스테이징에서만 내리기 (파일 수정 내용은 유지)
git restore --staged secret.env--staged 옵션이 핵심이다. 이 명령은 스테이징 영역에서만 파일을 내릴 뿐, 작업 디렉터리에 있는 실제 수정 내용은 손대지 않는다. 그래서 방금 add한 걸 취소해도 애써 고친 코드가 사라지지 않는다. 옛 방식인 git reset HEAD 파일명과 결과는 같지만, 이름만 봐도 의도가 분명한 restore --staged 쪽이 훨씬 안전하다.
이미 커밋했다 — 여기서부터가 진짜 갈림길
가장 사고가 잦은 구간이다. 이미 커밋을 해 버렸는데 되돌리고 싶다면, 그 커밋을 아직 공유(push)했는지 여부에 따라 도구가 완전히 달라진다. 이 구분을 놓치면 팀 전체의 저장소를 헝클어뜨릴 수 있다.
혼자 작업 중이고 아직 push하지 않았다면 git reset을 쓸 수 있다. reset은 브랜치가 가리키는 지점 자체를 과거로 되감는, 말하자면 역사를 조용히 고쳐 쓰는 방식이다.
# 커밋만 취소하고, 그 변경은 스테이징에 남겨 둔다 (--soft)
git reset --soft HEAD~1
# 커밋도 취소하고 스테이징도 풀되, 파일 수정은 유지 (--mixed, 기본값)
git reset HEAD~1
# 커밋·스테이징·파일 수정까지 전부 날린다 (--hard, 위험)
git reset --hard HEAD~1세 옵션의 차이가 곧 사고와 안전을 가른다. --soft는 "커밋 메시지만 다시 쓰고 싶다" 같은 가벼운 수정에 좋다. 기본값인 --mixed는 커밋을 통째로 다시 구성하고 싶을 때 쓴다. 문제는 --hard다. 이건 작업 디렉터리의 수정 내용까지 지워 버리므로, 확실하지 않으면 절대 쓰지 말아야 하는 옵션이다. 실수로 --hard를 날렸다면 곧바로 git reflog를 확인하자. Git은 브랜치가 지나온 자취를 한동안 기록해 두기 때문에, 운이 좋으면 날아간 커밋의 주소를 찾아 되살릴 수 있다.
이미 공유한 커밋이라면 — revert가 정답이다
문제의 커밋을 이미 push해서 동료들이 받아 갔다면 이야기가 다르다. 여기서 reset으로 역사를 되감아 버리면, 내 저장소와 남의 저장소의 역사가 어긋나면서 충돌 지옥이 열린다. 이럴 때 쓰는 것이 git revert다.
# 특정 커밋의 변경을 '취소하는 새 커밋'을 만든다
git revert a1b2c3drevert는 과거를 지우지 않는다. 대신 "그 커밋이 한 일을 정확히 반대로 실행하는 새 커밋"을 하나 더 얹는다. 역사를 고쳐 쓰는 게 아니라, 역사 위에 정정 기록을 덧붙이는 셈이다. 그래서 이미 공유된 브랜치에서도 안전하다. 얼핏 "취소인데 커밋이 오히려 늘어난다"는 게 어색해 보이지만, 여러 사람이 같은 코드를 보는 협업 환경에서는 이 투명함이야말로 가장 큰 미덕이다.
| 상황 | 명령 | 핵심 |
|---|---|---|
| add 전 수정 버리기 | git restore 파일 | 복구 불가, 신중히 |
| add만 취소 | git restore --staged 파일 | 수정 내용은 유지 |
| 커밋 취소(공유 전) | git reset | soft/mixed/hard 구분 |
| 커밋 취소(공유 후) | git revert | 되돌리는 새 커밋 생성 |
되돌리기 전에, 일단 숨겨 두는 법
되돌릴지 말지 결정을 못 하겠다면, 성급하게 지우기보다 잠시 치워 두는 방법이 있다. git stash는 지금까지 고친 내용을 서랍에 넣어 두고 작업 디렉터리를 깨끗한 상태로 만들어 준다.
git stash # 현재 변경을 서랍에 넣고 깨끗하게
git stash list # 넣어 둔 목록 확인
git stash pop # 다시 꺼내서 복원급하게 다른 브랜치로 넘어가야 하는데 지금 작업은 아직 커밋하기 애매할 때 특히 요긴하다. 버리는 것도 아니고 커밋하는 것도 아닌, 잠깐 보류하는 안전한 중간 지대인 셈이다.
정리하며
Git 되돌리기가 어렵게 느껴지는 이유는 명령어가 많아서가 아니라, "어느 공간을, 어느 시점으로" 되돌릴지를 스스로에게 먼저 묻지 않기 때문이다. 아직 add 전이면 restore, 커밋했지만 안 나눴으면 reset, 이미 공유했으면 revert. 이 세 갈래만 몸에 익혀 두면 대부분의 상황은 당황하지 않고 넘길 수 있다.
그리고 무엇보다, --hard나 restore처럼 되돌릴 수 없는 명령 앞에서는 항상 한 박자 멈추는 습관을 들이자. 오늘 저지른 실수가 두려워 서두르다 더 큰 실수를 하는 경우가 훨씬 많다. 겁내지 말되, 딱 한 번만 더 확인하자. 그거면 충분하다.
댓글 0