1 상황별 되돌리기 요약
먼저 전체 그림을 보고, 각각의 상세 내용을 확인하자.
| 상황 | 명령어 | 안전도 |
|---|---|---|
| 파일 수정을 취소하고 싶다 (아직 add 전) | git restore <파일> |
안전 |
| 스테이징을 취소하고 싶다 (add 취소) | git restore --staged <파일> |
안전 |
| 마지막 커밋 메시지를 수정하고 싶다 | git commit --amend |
주의 |
| 마지막 커밋을 취소하고 싶다 (변경사항 유지) | git reset --soft HEAD~1 |
주의 |
| 마지막 커밋을 완전히 없애고 싶다 | git reset --hard HEAD~1 |
위험 |
| 이미 push한 커밋을 되돌리고 싶다 | git revert <커밋해시> |
안전 |
2 파일 수정 취소: git restore
아직 git add 하지 않은 수정 사항을 되돌린다.
# 특정 파일의 수정 취소 (마지막 커밋 상태로 복원)
git restore src/app.py
# 모든 수정 취소
git restore .
# 구 버전 명령어 (동일한 동작)
git checkout -- src/app.py두 명령 모두 성공하면 아무 출력도 없다. 파일이 마지막 커밋 상태로 되돌아갔는지 git status 로 확인한다:
On branch main
nothing to commit, working tree clean
git restore로 취소한 변경사항은 복구할 수 없다. 커밋되지 않은 변경사항은 Git이 추적하지 않으므로 영구 삭제된다.
3 스테이징 취소: git restore –staged
git add로 스테이징한 파일을 다시 unstaged 상태로 되돌린다. 파일 내용은 그대로 유지된다.
성공하면 아무 출력도 없다. git status 로 확인하면 파일이 Changes not staged 섹션으로 이동한다:
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: src/app.py
4 커밋 메시지 수정: git commit –amend
마지막 커밋의 메시지를 수정하거나, 빠뜨린 파일을 추가한다.
4.1 메시지만 수정
[main 8f3a2b1] fix: 올바른 커밋 메시지로 변경
Date: Mon Jan 15 14:30:00 2024 +0900
1 file changed, 3 insertions(+)
커밋 해시가 변경됐다는 점에 주의한다. 8f3a2b1 은 이전과 다른 새 해시이다.
4.2 빠뜨린 파일 추가
[main 9c4b3d2] fix: 올바른 커밋 메시지로 변경
Date: Mon Jan 15 14:30:00 2024 +0900
2 files changed, 8 insertions(+)
--no-edit 옵션은 편집기를 열지 않고 기존 메시지를 그대로 유지한다. 파일이 추가됐으므로 2 files changed 로 바뀐다.
--amend는 마지막 커밋을 새로운 커밋으로 교체한다 (커밋 해시가 변경됨). 이미 push한 커밋에는 사용하지 않는 것이 좋다. push 후 amend하면 git push --force가 필요하다.
5 커밋 취소: git reset
5.1 reset의 3가지 모드
git reset은 HEAD 포인터를 이전 커밋으로 이동시킨다. 모드에 따라 Working Directory와 Staging Area의 처리가 달라진다.
커밋 이력: A --- B --- C (HEAD)
↑
이 커밋을 취소하고 싶다
5.1.1 –soft: 커밋만 취소 (변경사항은 staged 상태로 유지)
성공하면 출력 없음. git status 로 확인하면:
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/app.py
커밋이 취소되고 변경된 파일이 staged 상태로 남아 있다. 바로 다시 커밋할 수 있다.
- 용도: 커밋 메시지 수정, 커밋 분리
5.1.2 –mixed (기본값): 커밋 + 스테이징 취소
성공하면 출력 없음. git status 로 확인하면:
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: src/app.py
파일 내용은 유지되지만 staged 상태가 해제된다. git add 부터 다시 해야 한다.
- 용도: 커밋과 스테이징을 모두 다시 하고 싶을 때
5.1.3 –hard: 모든 것 취소 (변경사항 삭제)
HEAD is now at a123456 feat: 이전 커밋 메시지
HEAD is now at <hash> 메시지가 나타나고 파일 내용이 해당 커밋 시점으로 완전히 복원된다. 이후 커밋(C)에서 변경한 내용은 삭제된다.
- 용도: 변경사항을 완전히 버리고 이전 상태로 돌아가고 싶을 때
--hard는 되돌릴 수 없다 (reflog를 사용하면 가능하지만 복잡함). 신중하게 사용해야 한다.
5.2 reset 대상 지정 방법
5.3 3가지 모드 비교
| 모드 | 커밋 | Staging Area | Working Directory |
|---|---|---|---|
--soft |
취소 | 유지 (staged) | 유지 |
--mixed |
취소 | 취소 (unstaged) | 유지 |
--hard |
취소 | 취소 | 삭제 |
6 Push된 커밋 되돌리기: git revert
이미 원격에 push한 커밋은 reset 대신 revert를 사용한다. revert는 기존 커밋을 삭제하지 않고, 되돌리는 내용의 새로운 커밋을 생성한다.
편집기가 열려 revert 커밋 메시지를 입력하게 된다 (기본 메시지가 자동으로 채워진다):
Revert "feat: 결제 기능 추가"
This reverts commit abc1234.
저장 후 종료하면:
[main f7e2a9b] Revert "feat: 결제 기능 추가"
1 file changed, 12 deletions(-)
[main g8f3b0c] Revert "feat: 결제 기능 추가"
Date: Mon Jan 15 15:00:00 2024 +0900
1 file changed, 12 deletions(-)
--no-edit 은 편집기 없이 기본 revert 메시지를 사용한다.
Revert 결과:
A --- B --- C --- D (원본)
\
R (C를 되돌리는 revert 커밋)
6.1 Reset vs Revert 비교
| 항목 | reset | revert |
|---|---|---|
| 동작 | 커밋을 삭제 | 되돌리는 새 커밋 생성 |
| 이력 | 이력이 사라짐 | 이력이 보존됨 |
| push 후 사용 | 위험 (force push 필요) | 안전 |
| 협업 환경 | 비권장 | 권장 |
7 git reflog: 최후의 수단
git reset --hard로 날린 커밋도 복구할 수 있는 방법이 있다. Git은 HEAD의 이동 이력을 reflog에 기록한다.
def5678 HEAD@{0}: reset: moving to HEAD~1
abc1234 HEAD@{1}: commit: feat: Important feature
ghi9012 HEAD@{2}: commit: fix: Bug fix
HEAD@{0} 은 현재 HEAD의 위치, HEAD@{1} 은 바로 이전 위치이다. reset: moving to HEAD~1 은 방금 실행한 reset --hard 이전에 있었던 위치이다.
HEAD is now at abc1234 feat: Important feature
git reset --hard 로 날린 커밋을 복구했다.
reflog는 로컬에만 존재하며, 기본적으로 90일간 유지된다. 완전한 안전장치는 아니지만 실수했을 때 큰 도움이 된다.
8 실전 시나리오별 해결법
8.1 시나리오 1: 잘못된 파일을 commit했다
8.2 시나리오 2: commit 메시지에 오타가 있다
8.3 시나리오 3: 이미 push한 커밋에 버그가 있다
8.4 시나리오 4: 과거 커밋에서 특정 경로만 복구하고 싶다
git reset --hard나 git revert는 전체 커밋을 되돌린다. 특정 파일 또는 디렉토리만 과거 상태로 복구하고 싶을 때는 git checkout <커밋해시> -- <경로>를 사용한다.
이 명령은 해당 커밋 시점의 경로 스냅샷을 staged 상태로 working directory에 복원한다. 커밋 이력은 변경되지 않으며, 복원된 내용을 바로 커밋할 수 있다.
# 복구 후 상태 확인
git status
# → Changes to be committed: (modified/added 파일들이 staged 상태로 표시)
# 커밋
git add .
git commit -m "fix: 과거 커밋에서 _site/ 복구"적용 예시 — git add로 삭제된 파일을 원격에 반영한 경우:
quarto render가 _site/ 내 파일을 삭제한 뒤 이를 git commit && git push까지 반영했다면, git revert는 전체 커밋의 반대를 만들어 다른 파일까지 영향을 준다. 이때 git checkout <정상커밋> -- _site/로 해당 경로만 골라서 복구하는 것이 안전하다.
# _site/ 가 정상이었던 커밋 확인
git log --oneline -- _site/
# 해당 커밋 시점으로 _site/ 복구
git checkout 778cc33c -- _site/
# 커밋 및 푸시
git add _site/
git commit -m "fix: 삭제된 _site/ HTML 복구"
git pushgit checkout <커밋해시> -- <경로>는 커밋 이력을 변경하지 않는다. 전체 브랜치를 되돌리는 git reset이나 새 커밋을 만드는 git revert와 달리, 특정 경로의 내용만 과거 상태로 가져오는 외과적 복구 방법이다.
8.5 시나리오 5: 작업 중인 파일 변경을 모두 버리고 싶다
Removing temp_file.py
Removing debug/
Removing __pycache__/
삭제된 파일과 디렉토리 목록이 출력된다. -f(force)와 -d(디렉토리 포함) 옵션이 필요하다.
9 요약
되돌리기 판단 기준:
push 했는가?
├── Yes → git revert (안전)
└── No
├── commit을 취소하고 싶다
│ ├── 변경사항 유지 → git reset --soft HEAD~1
│ └── 변경사항 삭제 → git reset --hard HEAD~1
├── staging을 취소하고 싶다 → git restore --staged <파일>
└── 파일 수정을 취소하고 싶다 → git restore <파일>