금요일 저녁 6시 40분. 배포 직전에 급하게 고친 파일 세 개를 커밋하려다가, 손이 미끄러져 git checkout . 을 눌러버린 신입 개발자 A씨의 이야기입니다. 두 시간 동안 만진 코드가 조용히 사라졌습니다. 에러 메시지도, 확인 창도 없었습니다. Git이 무서운 이유는 어렵기 때문이 아니라, 틀린 명령이 아무 말 없이 성공하기 때문입니다.
Git을 몇 년 써도 "되돌리기"만큼은 매번 검색하게 됩니다. reset, revert, restore, stash, checkout… 이름은 다 비슷한데 결과는 전혀 다릅니다. 오늘은 이 명령들을 외우는 대신, "내가 지금 어디에 있는가"를 먼저 묻는 방식으로 정리해 보겠습니다. 상황을 세 칸으로 나누기만 하면 명령은 저절로 따라옵니다.
먼저 세 칸을 그려보세요
Git에서 파일은 항상 세 곳 중 하나에 있습니다. 이 그림 하나면 절반은 끝납니다.
- 작업 디렉터리(Working Directory): 내가 지금 편집기에서 고치고 있는 실제 파일
- 스테이징 영역(Staging Area, Index):
git add로 "이번 커밋에 포함시키겠다"고 표시해 둔 상태 - 저장소(Repository):
git commit으로 이미 기록이 남은 상태
되돌리기 명령은 결국 "어느 칸에서 어느 칸으로 되돌릴 것인가"를 정하는 일입니다. 예를 들어 아직 add 도 안 한 수정은 작업 디렉터리에만 있으니, 저장소를 건드리는 reset 은 애초에 필요 없습니다. 반대로 이미 원격에 푸시한 커밋은 저장소를 넘어 남의 컴퓨터에도 복사됐으니, 역사를 지우는 방식이 아니라 덮어쓰는 방식을 써야 합니다.
지금 상태가 헷갈릴 땐 무조건 이것부터 칩니다.
git status
git log --oneline -5git status 는 친절해서 "Changes not staged for commit" 아래에 되돌리는 명령까지 알려줍니다. 급할수록 이 두 줄을 먼저 치는 습관이 사고를 막습니다.
되돌리기는 명령을 아는 게 아니라, 내 변경이 지금 어느 칸에 있는지 아는 일이다.
아직 커밋 안 한 수정을 버리고 싶을 때 — restore
main.php 를 이것저것 고쳐봤는데 다 마음에 안 들어서 원래대로 돌리고 싶습니다. 이때는 git restore 입니다.
# 특정 파일 하나만 원래대로
git restore src/main.php
# 작업 디렉터리 전체를 마지막 커밋 상태로
git restore .예전에는 이걸 git checkout -- 파일명 으로 썼습니다. 문제는 checkout 이 브랜치 이동에도 쓰이는 명령이라, 손이 미끄러지면 앞의 A씨처럼 됩니다. 그래서 Git 2.23부터 역할을 쪼개서 파일 되돌리기는 restore, 브랜치 이동은 switch 로 나눴습니다. 지금 새로 배우는 분이라면 checkout 은 잊고 이 둘만 쓰셔도 충분합니다.
다만 명심할 게 있습니다. restore 로 날린 수정은 Git 어디에도 기록이 없어서 복구가 사실상 불가능합니다. 커밋되지 않은 변경은 Git의 세계에서 존재하지 않는 것과 같습니다. 아깝다 싶으면 버리지 말고 다음에 나올 stash 로 잠시 치워두세요.
실수로 add 했을 때 — restore --staged
.env 파일이나 몇 GB짜리 로그를 무심코 git add . 로 올려버린 경우입니다. 커밋 전이라면 아주 쉽습니다.
# 스테이징만 취소 (파일 수정 내용은 그대로 유지)
git restore --staged .env
# 예전 표기 (지금도 동작함)
git reset HEAD .env여기서 중요한 건 수정 내용은 그대로 남는다는 점입니다. "이번 커밋에 넣지 않겠다"는 표시만 떼는 것이지, 파일을 되돌리는 게 아닙니다. 완전히 버리고 싶다면 스테이징을 푼 다음 git restore 까지 한 번 더 해야 합니다. 이 두 단계를 헷갈려서 "분명 취소했는데 왜 아직 남아 있죠?"라고 묻는 경우가 정말 많습니다.
방금 한 커밋을 고치고 싶을 때 — amend와 reset
커밋 메시지에 오타가 났거나, 파일 하나를 빼먹고 커밋했습니다. 아직 푸시하지 않았다면 가장 간단한 답은 --amend 입니다.
# 빠뜨린 파일 추가 후 직전 커밋에 합치기
git add src/util.php
git commit --amend
# 메시지만 바꾸기
git commit --amend -m "회원 가입 유효성 검사 추가"커밋을 아예 취소하고 싶다면 reset 입니다. 옵션 세 개의 차이가 핵심인데, 표로 보면 명확합니다.
| 옵션 | 커밋 취소 | 스테이징 | 내 코드 |
|---|---|---|---|
--soft | O | 유지 | 유지 |
--mixed (기본) | O | 해제 | 유지 |
--hard | O | 해제 | 삭제 |
# 커밋만 취소하고 내용은 add된 채로 (메시지만 다시 쓰고 싶을 때)
git reset --soft HEAD~1
# 커밋 취소, add도 풀고, 코드는 남김 (가장 무난)
git reset HEAD~1
# 전부 없던 일로 (되돌릴 수 없음, 진짜 주의)
git reset --hard HEAD~1실무에서 90%는 --soft 또는 기본값이면 충분합니다. --hard 는 정말 확신이 설 때만 쓰세요. 한 스타트업의 개발자는 --hard 로 사흘치 작업을 날린 뒤로, 팀 규칙에 "hard 치기 전엔 무조건 브랜치 하나 만들기"를 넣었다고 합니다. git branch backup-오늘날짜 한 줄이면 안전망이 생깁니다.
이미 푸시한 커밋이라면 — revert
여기서 방향이 완전히 바뀝니다. 원격에 올라간 커밋을 reset 으로 지우고 강제 푸시하면, 그 사이 같은 브랜치를 받아 간 동료의 저장소와 역사가 어긋납니다. 팀 전체가 충돌 지옥에 빠지는 전형적인 경로입니다.
그래서 공유된 브랜치에서는 지우지 않고, 반대 커밋을 새로 쌓습니다.
# 되돌릴 커밋 해시 확인
git log --oneline -5
# 해당 커밋의 변경을 취소하는 새 커밋 생성
git revert a1b2c3drevert 는 역사를 남깁니다. "이 커밋이 있었고, 그 뒤에 취소했다"는 사실이 그대로 보입니다. 감추는 것보다 이게 낫습니다. 나중에 장애 원인을 추적할 때 지워진 역사만큼 곤란한 게 없기 때문입니다.
기준은 간단합니다. 내 로컬에만 있으면 reset, 남에게 갔으면 revert.
급한 일이 끼어들었을 때 — stash
작업 중인데 갑자기 운영 서버 버그를 고쳐야 합니다. 커밋하자니 어중간하고, 버리자니 아깝습니다. 이럴 때 stash 가 서랍 역할을 합니다.
# 현재 변경을 이름 붙여 치워두기
git stash push -m "결제 화면 작업중"
# 목록 보기
git stash list
# 다시 꺼내기 (꺼내면서 목록에서 제거)
git stash pop
# 꺼내되 목록에 남겨두기
git stash apply stash@{0}stash 는 임시 보관이지 백업이 아닙니다. 며칠씩 묵히면 본인도 뭐가 뭔지 모르게 되니, 하루 이상 갈 작업이라면 차라리 임시 브랜치를 만들어 커밋해 두는 편이 안전합니다.
다 날렸다고 생각될 때 — reflog
마지막 보루입니다. --hard 로 커밋을 지웠거나 브랜치를 잘못 옮겼어도, 커밋된 적이 있는 내용이라면 대부분 살아 있습니다. Git은 HEAD가 움직인 기록을 따로 남겨두기 때문입니다.
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# f4e5d6c HEAD@{1}: commit: 결제 모듈 리팩터링
git reset --hard f4e5d6c기본 설정에서 이 기록은 약 90일간 보관됩니다. 사고가 나면 아무것도 하지 말고 git reflog 부터 치세요. 단, 반복하지만 한 번도 커밋되지 않은 변경은 reflog로도 못 살립니다. 그래서 결론은 늘 같은 곳으로 돌아옵니다. 작게, 자주 커밋하기.
정리하며
되돌리기 명령을 고르는 순서는 이렇게 요약됩니다.
- 아직 add 전 →
git restore 파일 - add까지 했다 →
git restore --staged 파일 - 커밋했지만 푸시 전 →
git commit --amend또는git reset - 이미 푸시했다 →
git revert - 잠깐 치워두고 싶다 →
git stash - 다 날린 것 같다 →
git reflog
Git이 복잡해 보이는 건 명령이 많아서가 아니라, 우리가 상황을 정리하지 않은 채 명령부터 찾기 때문입니다. git status 한 줄로 지금 위치를 확인하는 습관만 들여도 대부분의 사고는 일어나지 않습니다.
그리고 혹시 오늘 뭔가를 날려버리셨다면, 너무 자책하지 마세요. Git으로 코드 한 번 날려보지 않은 개발자는 거의 없습니다. 중요한 건 그다음에 커밋을 조금 더 자주 하게 된다는 것이고, 그 습관이 앞으로의 수백 번을 지켜줍니다. 오늘 하루도 무사히 푸시하시길 바랍니다.

