TL;DR: git revert는 오래된 커밋을 되돌리는 새 커밋을 추가합니다 — 히스토리는 보존되고, 다른 사람이 pull한 브랜치에서도 안전합니다. git reset은 브랜치 포인터를 뒤로 움직입니다 — 히스토리가 재작성되고, push하지 않은 커밋에만 안전합니다. 공유 브랜치에는 revert, 로컬 정리에는 reset. reset --hard는 커밋하지 않은 작업 트리 변경까지 삭제합니다 — 진짜 작업을 집어삼키는 바로 그 옵션입니다.
git revert와 git reset의 차이는 무엇인가?
둘 다 프로젝트를 과거 커밋이 없었던 것처럼 만듭니다. 방법이 다를 뿐입니다:
git revert <sha>는 커밋의 역 패치를 계산해 커밋합니다. 히스토리에 “저걸 되돌린다”는 커밋이 하나 늘어납니다. 나쁜 커밋은 로그에 남고, 그 뒤에 취소가 붙습니다. 나머지 모든 SHA는 그대로입니다.git reset <sha>는 현재 브랜치의 포인터를<sha>로 옮깁니다. 이후의 커밋들은 분리됩니다 — 가비지 컬렉션 전까지 오브젝트 데이터베이스에는 남아 있지만, 어떤 브랜치나git log에서도 도달할 수 없습니다.
하나는 아무것도 재작성하지 않고, 다른 하나는 없던 일로 만듭니다. 이 한 가지 성질이 각 상황이 요구하는 답을 결정합니다:
# 데모: 바로 실행해볼 수 있는 임시 저장소
git init /tmp/revert-vs-reset && cd /tmp/revert-vs-reset
echo one > file.txt && git add . && git commit -m "one"
echo two > file.txt && git add . && git commit -m "two"
# Revert: 히스토리는 두 커밋을 모두 남기고, "two"를 되돌리는 세 번째 커밋이 붙는다
git revert --no-edit HEAD
git log --oneline # 커밋 3개: revert, two, one
# Reset: 브랜치 포인터가 뒤로 가고, "two"는 로그에서 사라진다
git reset --hard HEAD~1
git log --oneline # 커밋 1개: one 둘 다 실행하고 git log를 들여다보세요 — 이 비대칭이 답의 전부입니다.
git reset —soft, —mixed, —hard는 실제로 뭘 다를까?
세 모드는 포인터가 움직인 뒤 수정 사항이 어디에 남는지를 제어합니다:
| 모드 | 브랜치 포인터 | 스테이징 영역 | 작업 트리 | 수정 사항 |
|---|---|---|---|---|
--soft | 뒤로 이동 | 스테이지 유지 | 그대로 | 전부 보존 |
--mixed (기본값) | 뒤로 이동 | 언스테이지 | 그대로 | 보존, 언스테이지 |
--hard | 뒤로 이동 | 리셋 | 리셋 | 삭제 |
표를 실제 상황으로 읽으면:
git reset --soft HEAD~1— “커밋이 너무 빨랐다.” 전부 스테이지 상태로 돌아가 재커밋 준비 완료, 추가 작업과 함께 묶어도 됩니다.git reset HEAD~1(mixed) — “잘못된 파일을 스테이지했다.” 수정은 작업 트리에 살아 있고, 스테이지된 것은 없습니다. 흩어진 파일까지 치우고 싶다면git remove untracked files와 함께 쓰세요.git reset --hard HEAD~1— “이 커밋과 커밋 안 된 수정은 모두 쓰레기다.” Git은 두 번 묻지 않습니다; stash하거나 커밋해 두지 않았다면 작업 트리 부분에는 되돌리기가 없습니다.
revert에는 모드가 없습니다. 아무것도 파괴하지 않기 때문입니다 — 언제나 보상 커밋을 추가할 뿐입니다.
git reset 대신 git revert를 써야 할 때는?
질문 하나면 충분합니다: 이 커밋을 다른 사람이 pull한 적이 있는가? 있다면 reset은 후보에서 퇴장입니다.
다른 사람이 기반을 삼은 브랜치를 reset하면 공유 히스토리가 재작성됩니다. 그들의 다음 pull은 갈라진 브랜치와 만나고, 그 “수정”은 보통 force-push로 귀결되어 당신의 실수를 모두의 문제로 만듭니다. revert는 평범한 커밋을 덧붙이므로 평범한 git pull이 모두에게 깨끗하게 머지됩니다 — 그래서 main과 GitHub PR에서는 revert가 기본 답이고, 머지된 PR의 히스토리 재작성이 선택지가 아니기에 “Revert” 버튼이 정확히 존재하는 것입니다.
reset은 공유되기 직전의 창을 위한 도구입니다: 30초 전에 만든 커밋, 스테이징 실수, 로컬 실험 브랜치. 작업의 유일한 사본이 내 머신에만 있다면 히스토리를 마음대로 다시 배열해도 됩니다 — pre-push 창의 존재 이유 그 자체입니다.
중간 사례가 있습니다: 자기 feature 브랜치에 push했고 아무도 그 위에 빌드하지 않는 경우입니다. 여기서 reset 뒤의 force-push는 사회적으로 용인되지만, 여전히 revert가 조율 비용이 씁니다. force-push는 혼자 소유한 브랜치를 위해 아껴두세요.
공유 브랜치에서는 git revert가 git reset보다 안전한가?
네, 관행이 아니라 구조적으로 그렇습니다:
- revert는 평범한 커밋을 만듭니다; CI가 돌고, diff는 리뷰 가능하고,
git log가 미래의 나에게 변경이 왜 사라졌는지 설명해 줍니다. - reset은 중간 상태를 조용히 버립니다. 나중에 리뷰하는 사람은 무엇이 언제 취소됐는지 볼 수 없습니다 — 로그가 그 커밋을 애초에 품은 적 없는 직선을 보여주기 때문입니다.
실전에서 다치는 모서리 둘:
- 머지 커밋을 revert하려면
git revert -m 1 <sha>가 필요합니다(첫 번째 부모의 줄을 유지).-m이 없으면 git이 거부하고 에러 읽기는 당신 몫입니다. - revert는 타임머신이 아닙니다: 커밋 하나의 diff를 되돌립니다. 이후 커밋이 같은 줄을 만졌다면 충돌을 해소해야 할 수 있습니다 — 되돌리기가 얽혀 있다는 git의 알림이며, 오작동이 아니라 유용한 정보입니다.
마지막 커밋 하나만을 되돌리는 경우 — 변경을 남길지 버릴지 — 에는 git undo last commit: keep the changes에 선택지가 정리돼 있습니다.
git restore는 어디에 들어맞나?
git restore(그리고 짝꿍 git switch)는 git 2.23에서 이름 과부하로 reset이 억지로 떠맡았던 일들을 인수하러 왔습니다:
| 작업 | 옛 방식 | 명확한 방식 |
|---|---|---|
| 파일의 작업 트리 수정 폐기 | git checkout -- file / git reset --hard | git restore file |
| 파일 언스테이지 | git reset HEAD file | git restore --staged file |
| 브랜치를 다른 커밋으로 이동 | git reset <sha> | git reset <sha> (대체재 없음 — 이 자리는 유지) |
정리하면 현대의 분업은 이렇습니다: restore는 파일을 고치고, reset은 브랜치를 옮기고, revert는 공개된 커밋을 되돌립니다. 자동완성 데이터에서 사람들이 “git revert vs reset vs restore”를 함께 검색하는 데 이유가 있습니다 — 하나의 멘탈 모델이 세 명령으로 쪼개져 있는 것입니다. 옛 git checkout 형태는 여전히 모든 곳에서 동작합니다; 새 형태가 해 주는 건 파일 하나를 언스테이지하려다 브랜째 통째로 분쇄기에 넣는 일을 막아준다는 것뿐입니다.
나쁜 커밋을 지우는 게 아니라 좋은 커밋을 앞으로 가져오는 게 목적이라면 그건 cherry-pick의 일입니다 — git cherry-pick: multiple commits, branches, conflicts를 보세요.
빠른 결정 요약
- 커밋이 공개됐다(push됐고, 남이 pull했다) →
git revert <sha>. - 로컬에만 있고 다시 만들고 싶다 →
git reset --soft또는--mixed후 재커밋. - 로컬에만 있고 커밋 안 된 엉망까지 통째로 치우고 싶다 →
git reset --hard, 단 작업 트리에 뭐가 더 들어 있는지 한 번 정직하게 보고. - 파일 하나만 망가졌다 →
git restore <file>로 끝, 브랜치는 건드리지 않는다.
이 목록에서 진짜 위험한 항목은 --hard뿐입니다 — 나머지는 전부 git reflog로 말을 걸어 되돌릴 수 있습니다. git에서 영구적인 손실은 좁고, 대개 명시적으로 타이핑해야 일어납니다; 만들 만한 습관은 --hard 앞에서 반 박자 멈추는 것이지, git의 날카로운 도구를 전부 피하는 게 아닙니다.
— mrsaynothing
Get the next one by email
One email per post. No spam, no algorithms.
what is this?글이 마음에 드셨나요? 제 본업이 바로 이런 일입니다. 저를 고용하세요