동작하는 코드
예제 1: git restore로 파일 변경 취소
작업 중 실수로 파일을 망쳤을 때:
git init
echo "원본 내용" > file.txt
git add file.txt
git commit -m "feat: 파일 추가"
# 실수로 중요한 파일 수정
echo "잘못된 내용으로 덮어씀" > file.txt
cat file.txt
출력:
잘못된 내용으로 덮어씀
# 마지막 커밋 상태로 복원
git restore file.txt
cat file.txt
출력:
원본 내용
git restore는 작업 디렉토리의 변경을 취소합니다. 이 작업은 되돌릴 수 없습니다.
예제 2: 스테이징 취소
echo "수정된 내용" > file.txt
git add file.txt
git status
출력:
Changes to be committed:
modified: file.txt
# 스테이징만 취소 (작업 디렉토리는 유지)
git restore --staged file.txt
git status
출력:
Changes not staged for commit:
modified: file.txt
파일 내용은 유지되고 스테이징만 취소됩니다.
예제 3: git revert로 특정 커밋 취소
git init
echo "v1" > version.txt && git add version.txt && git commit -m "feat: v1"
echo "v2" >> version.txt && git add version.txt && git commit -m "feat: v2"
echo "v3" >> version.txt && git add version.txt && git commit -m "feat: v3"
git log --oneline
출력:
c3d4e5f feat: v3
b2c3d4e feat: v2
a1b2c3d feat: v1
v2 커밋의 변경 사항을 취소 (새 커밋으로):
git revert b2c3d4e
편집기가 열리면 저장. 출력:
[main d5e6f7g] Revert "feat: v2"
1 file changed, 1 deletion(-)
cat version.txt
출력:
v1
v3
v2는 취소됐지만 히스토리는 유지됩니다.
직접 수정하기
git reset: 커밋 취소 3가지 방법
git init
echo "a" > file.txt && git add file.txt && git commit -m "커밋 A"
echo "b" >> file.txt && git add file.txt && git commit -m "커밋 B"
echo "c" >> file.txt && git add file.txt && git commit -m "커밋 C"
git log --oneline
출력:
c3d4e5f 커밋 C
b2c3d4e 커밋 B
a1b2c3d 커밋 A
--soft: 커밋만 취소, 변경은 스테이징 유지
git reset --soft HEAD~1
git log --oneline
출력:
b2c3d4e 커밋 B
a1b2c3d 커밋 A
git status
출력:
Changes to be committed:
modified: file.txt ← 커밋 C의 변경이 스테이징에 남아있음
커밋 메시지만 바꾸고 싶을 때 유용합니다.
--mixed: 커밋 + 스테이징 취소, 파일은 유지 (기본값)
git reset HEAD~1 # --mixed 가 기본값
git log --oneline
git status
출력:
a1b2c3d 커밋 A
Changes not staged for commit:
modified: file.txt ← 작업 디렉토리에 남아있음
파일을 다시 스테이징해서 다른 방식으로 커밋할 때 사용합니다.
--hard: 커밋 + 스테이징 + 파일 변경 모두 취소
git reset --hard HEAD~1
git log --oneline
cat file.txt
출력:
a1b2c3d 커밋 A
a
커밋 B, C의 내용이 완전히 사라졌습니다. 이 작업은 신중하게 사용하세요.
git reset 비교
| 명령 | 커밋 | 스테이징 | 작업 디렉토리 |
| --------- | ---- | -------- | ------------- |
| --soft | 취소 | 유지 | 유지 |
| --mixed | 취소 | 취소 | 유지 |
| --hard | 취소 | 취소 | 취소 |
git reflog: 안전망
git reset --hard나 실수로 커밋을 잃었을 때 복구할 수 있습니다:
# 이미 잃어버린 커밋 목록
git reflog
출력:
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
c3d4e5f HEAD@{1}: commit: 커밋 C
b2c3d4e HEAD@{2}: commit: 커밋 B
a1b2c3d HEAD@{3}: commit: 커밋 A
잃어버린 커밋 복구:
git reset --hard c3d4e5f # 또는 HEAD@{1}
git log --oneline
출력:
c3d4e5f 커밋 C ← 복구됨!
b2c3d4e 커밋 B
a1b2c3d 커밋 A
reflog는 Git이 로컬에서 관리하는 "모든 HEAD 이동 기록"입니다. git reset --hard로 잃어버린 커밋도 복구할 수 있습니다.
"왜?" — 각 명령어의 사용 시점
| 상황 | 올바른 명령어 |
| ------------------------- | ----------------------------- |
| 파일 수정 취소 (커밋 전) | git restore 파일명 |
| 스테이징 취소 | git restore --staged 파일명 |
| 마지막 커밋 메시지 수정 | git commit --amend |
| 아직 push 안 한 커밋 취소 | git reset |
| 이미 push한 커밋 취소 | git revert |
| 실수로 잃은 커밋 복구 | git reflog |
reset vs revert의 핵심 차이
reset: A → B → C → (D 삭제됨)
↑
HEAD
revert: A → B → C → D → D' (D의 반대 커밋)
↑
HEAD
push된 코드는 반드시 revert를 사용하세요. reset으로 push된 커밋을 지우면 다른 팀원의 히스토리와 충돌합니다.
흔한 실수
실수 1: --hard를 무분별하게 사용
# 주의! 되돌릴 수 없음
git reset --hard HEAD~3 # 3개 커밋의 모든 변경이 사라짐
# 먼저 변경 내용 확인
git diff HEAD~3 HEAD # 사라질 내용 확인
git reset --hard HEAD~3 # 그 후에 결정
실수 2: push된 커밋에 reset 사용
git push origin main # 이미 push됨
git reset --hard HEAD~1 # 로컬에서 커밋 삭제
git push origin main # 에러!
# error: failed to push some refs
# 강제 push (팀원에게 문제)
git push --force # 절대 공유 브랜치에 하지 마세요
실수 3: revert 후 merge conflict
git revert b2c3d4e # 충돌 발생 가능
# CONFLICT: ...
# 충돌 해결 후
git add 파일명
git revert --continue
심화 학습
git restore vs git checkout (이전 방식)
Git 2.23 전에는 checkout으로 파일을 복원했습니다:
# 구버전 방식
git checkout -- file.txt # 작업 디렉토리 복원
git checkout HEAD -- file.txt # 특정 커밋에서 복원
# 새로운 방식 (더 명확)
git restore file.txt # 작업 디렉토리 복원
git restore --staged file.txt # 스테이징 취소
git restore --source=HEAD~2 file.txt # 2커밋 전으로 복원
특정 커밋의 파일만 복원
# 5커밋 전의 특정 파일로 복원
git restore --source=HEAD~5 important-file.txt
# 특정 커밋 해시에서 복원
git restore --source=a1b2c3d config.yaml
# 복원된 파일은 작업 디렉토리에, 확인 후 커밋
git diff config.yaml
git add config.yaml
git commit -m "revert: config.yaml을 이전 버전으로 복원"
git bisect: 버그 도입 커밋 찾기
언제부터 버그가 생겼는지 모를 때:
git bisect start
git bisect bad HEAD # 현재 상태: 버그 있음
git bisect good a1b2c3d # 이 커밋은 정상이었음
# Git이 중간 커밋을 체크아웃
# 테스트 후 결과 보고
git bisect good # 이 커밋은 정상
git bisect bad # 이 커밋은 버그 있음
# Git이 버그 도입 커밋을 찾아줌
# 완료 후
git bisect reset
- 파일을 수정한 후
git restore로 되돌려보세요. - 스테이징 후
git restore --staged로 스테이징만 취소해보세요. - 커밋 3개를 만들고
git reset --soft HEAD~1,git reset --mixed HEAD~1,git reset --hard HEAD~1을 각각 테스트하고 차이를 비교하세요. - 커밋을
git reset --hard HEAD~1로 삭제한 후git reflog로 찾아서 복구해보세요. - 커밋 하나를
git revert로 취소하고 히스토리가 어떻게 되는지git log --oneline으로 확인하세요.
Q1. 이미 push하여 팀원과 공유된 커밋을 안전하게 취소하는 방법은?
- A)
git reset --hard HEAD~1후git push --force - B)
git revert 커밋해시 - C)
git reset --soft HEAD~1후git push - D)
git branch -D후 다시 만들기