TL;DR: 마지막 커밋을 되돌리되 변경 사항은 남기려면 git reset --soft HEAD~1(변경 사항이 스테이지에 유지됨) 또는 git reset HEAD~1(변경 사항이 작업 트리에 유지됨)을 실행하세요. 이미 push한 커밋이라면 대신 git revert HEAD — 히스토리를 재작성하지 않고 되돌리는 새 커밋을 만들어 줍니다. 이 세 명령어면 “커밋을 너무 일찍 해버렸다”의 거의 모든 순간을 커버합니다. 이 가이드의 나머지 부분에서는 각 상황을 복사해 쓸 수 있는 명령어와 함께 짚고, --soft와 --mixed, --hard의 차이를 설명하고, 뭔가 잘못됐을 때 되살리는 방법까지 보여 줍니다. 아래 명령어는 Linux, macOS, Windows의 최신 Git이라면 어디서든 동작합니다.
마지막 커밋을 되돌리되 변경 사항은 어떻게 남기죠?
가장 흔한 상황: 커밋을 했더니 오타를 발견했거나, 파일 하나가 빠졌거나, 애초에 다른 커밋에 들어가야 할 변경이라는 걸 깨달았습니다. 아직 push는 안 한 상태. 커밋을 되돌리고 모든 것을 제자리로 돌려놓으세요:
# 커밋은 히스토리 어디에도 남지 않음 — 변경 사항은 스테이징 영역으로 복귀
git reset --soft HEAD~1
# 대신 변경 사항이 작업 트리로 돌아감(언스테이지 상태)
git reset HEAD~1 HEAD~1은 “HEAD가 가리키는 곳에서 한 커밋 이전”입니다. 어느 명령어든 실행 후에도 파일들은 디스크에 그대로 — 움직인 것은 브랜치 포인터뿐입니다. git status로 확인해 보세요: --soft를 쓰면 변경 사항이 스테이지된 상태라 수정을 덧입혀 바로 다시 커밋할 수 있고, 플래그 없이 쓰면 언스테이지 상태라 먼저 자유롭게 고칠 수 있습니다.
마지막 커밋에 파일만 덧붙이고 싶다면, 애초에 되돌리지 않는 쪽이 더 안전한 습관입니다:
git add forgotten-file.txt
git commit --amend --no-edit --amend는 마지막 커밋을 그 자리에서 교체합니다(여기서는 커밋 메시지 변경 없음). 단, push된 커밋을 amend하면 히스토리를 재작성하게 됩니다 — 아래에서 다룹니다.
—soft, —mixed, —hard의 차이는 무엇일까요?
외워 둘 만한 부분입니다. 플래그가 변경 사항의 최종 행선지를, 그리고 잃어버릴 수 있는지를 결정합니다:
| 플래그 | 커밋 제거? | 디스크상 변경 사항 | 스테이지 상태? | 대표적인 용도 |
|---|---|---|---|---|
--soft | 예 | 유지 | 예 | 수정을 덧입혀 다시 커밋 |
--mixed (기본값) | 예 | 유지 | 아니요 | 변경 사항을 다시 묶고 선택적으로 스테이지 |
--hard | 예 | 삭제됨 | — | 작업을 완전히 버릴 때 |
git revert | 아니요(새 커밋) | 유지 | — | 이미 push한 커밋을 되돌릴 때 |
위험한 것은 --hard뿐입니다: 커밋과 변경 사항을 함께 버립니다. reset --hard 전에는 지금 것을 stash하거나 브랜치로 백업하세요:
git branch backup-before-reset # 값싼 보험
git reset --hard HEAD~1 # 커밋 + 변경 사항 소멸 이미 위험한 쪽을 실행했다고 해서 끝이 아닙니다 — git reflog가 HEAD가 거쳐간 자취를 기억합니다:
git reflog # 잃어버린 커밋 해시를 찾는다
git reset --hard HEAD@{1} # 또는: git reset --hard <hash> reflog는 dangling 커밋을 기본 90일 정도 유지합니다. 그러니 “실수로 hard-reset 했다”는 garbage collection이 돌기 전에 움직이면 거의 항상 복구 가능한 사고입니다.
이미 커밋을 push했다면?
커밋이 공유 브랜치(자신만의 feature 브랜치가 아닌 무엇이든)에 있다면 히스토리를 재작성하지 마세요. 반대 변경을 계산해 커밋해 주는 git revert를 쓰세요:
git revert HEAD
git push pull하는 모두는 그저 이전 커밋의 변경을 지우는 새 커밋을 받을 뿐입니다. force-push도 없고, 동료가 망가지는 일도 없습니다. 연달은 커밋을 되돌려야 한다면 범위로 revert하세요: git revert --no-commit HEAD~3..HEAD && git commit.
대안인 git reset --hard HEAD~1 && git push --force-with-lease는 아무도 그 위에서 작업하지 않는 브랜치에서만 허용되며, --force-with-lease(일반 --force는 절대 금지)만이 안전한 형태입니다 — 누군가 그사이 push했다면 거부하기 때문입니다. 공유 브랜치에 강제 push하는 것이 바로 팀이 커밋을 잃는 방식이고, “CI 로그가 아무의 체크아웃과도 안 맞는” 미스터리의 원천입니다.
마지막 커밋을 지우지 않고 따로 보관하려면?
커밋 자체는 좋은 작업인데 주소가 틀린 경우가 있습니다 — 잘못된 브랜치에 있거나, 너무 이른 경우. 되돌리는 대신 옮기세요:
git branch stash-commit # 커밋을 새 브랜치에 잠시 주차
git reset --hard HEAD~1 # 그다음 현재 브랜치를 정리 또는 현재 브랜치는 건드리지 않은 채 커밋만 다른 브랜치로 가져가세요:
git cherry-pick <hash> # 대상 브랜치에 체크아웃한 상태에서 reset --soft, cherry-pick, revert 사이에는 옮기거나 무력화하지 못할 커밋이 없습니다 — 요령은 플래그에 손을 뻗기 전에 “옮기기”와 “되돌리기” 중 무엇인지 고르는 것입니다.
어떤 되돌리기를 써야 할까? 빠른 판단 가이드
- 아직 push 안 함, 고쳐서 다시 커밋하고 싶다 →
git reset --soft HEAD~1 - 아직 push 안 함, 선택적으로 다시 스테이지하고 싶다 →
git reset HEAD~1(mixed) - 변경 사항까지 완전히 지우고 싶다 →
git reset --hard HEAD~1(후회하면 reflog가 알고 있습니다) - 이미 공유 브랜치에 push함 →
git revert HEAD - 커밋이 다른 브랜치에 속한다 → 되돌리지 말고
cherry-pick
마지막 운영 팁 하나: 나쁜 커밋이 서버까지 도달했다면 — 배포 훅이 잘못 됐다거나, force-push 뒤 CI 러너가 이상 동작한다거나 — 다음에 볼 곳은 Git이 아니라 그 기계의 로그입니다. systemd 기반 박스라면 journalctl -u <service> -n 100이 무엇이 언제 실행됐는지 정확히 보여 줍니다. 복사해 쓰는 패턴은 journalctl cheat sheet에 정리되어 있습니다. 그리고 diff 리뷰에 로컬 LLM 도구를 쓰는 워크플로라면, 주요 두 선택지를 Ollama vs LM Studio에서 비교해 뒀습니다.
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?journalctl 치트시트: Linux 로그 확인, 필터링, 실시간 추적
글이 마음에 드셨나요? 제 본업이 바로 이런 일입니다. 저를 고용하세요