Git 되돌리기 완전 가이드

reset, revert, restore, amend로 실수 복구하기

Git에서 실수를 되돌리는 다양한 방법을 상황별로 정리한다. 파일 수정 취소, 스테이징 취소, 커밋 취소, 커밋 메시지 수정, push된 커밋 되돌리기 등 실무에서 자주 겪는 상황별 해결법을 다룬다.

Engineering
Git
DevOps
저자

Kwangmin Kim

공개

2023년 05월 09일

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 restore --staged src/app.py

성공하면 아무 출력도 없다. 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
# 모든 파일의 스테이징 취소
git restore --staged .

# 구 버전 명령어 (동일한 동작)
git reset HEAD src/app.py

4 커밋 메시지 수정: git commit –amend

마지막 커밋의 메시지를 수정하거나, 빠뜨린 파일을 추가한다.

4.1 메시지만 수정

git commit --amend -m "fix: 올바른 커밋 메시지로 변경"
[main 8f3a2b1] fix: 올바른 커밋 메시지로 변경
 Date: Mon Jan 15 14:30:00 2024 +0900
 1 file changed, 3 insertions(+)

커밋 해시가 변경됐다는 점에 주의한다. 8f3a2b1 은 이전과 다른 새 해시이다.

4.2 빠뜨린 파일 추가

# 빠뜨린 파일을 스테이징
git add forgotten_file.py

# 마지막 커밋에 포함 (메시지는 유지)
git commit --amend --no-edit
[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 reset --soft HEAD~1

성공하면 출력 없음. 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 reset HEAD~1
# 또는
git reset --mixed HEAD~1

성공하면 출력 없음. 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: 모든 것 취소 (변경사항 삭제)

git reset --hard HEAD~1
HEAD is now at a123456 feat: 이전 커밋 메시지

HEAD is now at <hash> 메시지가 나타나고 파일 내용이 해당 커밋 시점으로 완전히 복원된다. 이후 커밋(C)에서 변경한 내용은 삭제된다.

  • 용도: 변경사항을 완전히 버리고 이전 상태로 돌아가고 싶을 때
경고

--hard되돌릴 수 없다 (reflog를 사용하면 가능하지만 복잡함). 신중하게 사용해야 한다.

5.2 reset 대상 지정 방법

# 바로 이전 커밋으로
git reset HEAD~1

# 2개 전 커밋으로
git reset HEAD~2

# 특정 커밋 해시로
git reset abc1234

# 특정 브랜치의 상태로
git reset origin/main

5.3 3가지 모드 비교

모드 커밋 Staging Area Working Directory
--soft 취소 유지 (staged) 유지
--mixed 취소 취소 (unstaged) 유지
--hard 취소 취소 삭제

6 Push된 커밋 되돌리기: git revert

이미 원격에 push한 커밋은 reset 대신 revert를 사용한다. revert는 기존 커밋을 삭제하지 않고, 되돌리는 내용의 새로운 커밋을 생성한다.

# 특정 커밋을 되돌리는 새 커밋 생성
git revert abc1234

편집기가 열려 revert 커밋 메시지를 입력하게 된다 (기본 메시지가 자동으로 채워진다):

Revert "feat: 결제 기능 추가"

This reverts commit abc1234.

저장 후 종료하면:

[main f7e2a9b] Revert "feat: 결제 기능 추가"
 1 file changed, 12 deletions(-)
# 커밋 메시지 편집 없이 바로 실행
git revert --no-edit abc1234
[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에 기록한다.

# reflog 확인
git 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 이전에 있었던 위치이다.

# 삭제된 커밋으로 복구
git reset --hard abc1234
HEAD is now at abc1234 feat: Important feature

git reset --hard 로 날린 커밋을 복구했다.

힌트

reflog는 로컬에만 존재하며, 기본적으로 90일간 유지된다. 완전한 안전장치는 아니지만 실수했을 때 큰 도움이 된다.

8 실전 시나리오별 해결법

8.1 시나리오 1: 잘못된 파일을 commit했다

# 커밋 취소 (파일은 staged 상태로 유지)
git reset --soft HEAD~1

# 잘못된 파일 제거
git restore --staged wrong_file.py

# 다시 커밋
git commit -m "feat: 올바른 변경사항만 커밋"

8.2 시나리오 2: commit 메시지에 오타가 있다

# push 전이라면
git commit --amend -m "fix: 올바른 메시지"

# push 후라면 (개인 브랜치에서만)
git commit --amend -m "fix: 올바른 메시지"
git push --force-with-lease

8.3 시나리오 3: 이미 push한 커밋에 버그가 있다

# revert로 안전하게 되돌리기
git revert HEAD
git push

8.4 시나리오 4: 과거 커밋에서 특정 경로만 복구하고 싶다

git reset --hardgit revert는 전체 커밋을 되돌린다. 특정 파일 또는 디렉토리만 과거 상태로 복구하고 싶을 때는 git checkout <커밋해시> -- <경로>를 사용한다.

# 특정 커밋 시점의 파일 하나 복구
git checkout abc1234 -- src/app.py

# 디렉토리 전체 복구
git checkout abc1234 -- _site/

이 명령은 해당 커밋 시점의 경로 스냅샷을 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 push
노트

git checkout <커밋해시> -- <경로>는 커밋 이력을 변경하지 않는다. 전체 브랜치를 되돌리는 git reset이나 새 커밋을 만드는 git revert와 달리, 특정 경로의 내용만 과거 상태로 가져오는 외과적 복구 방법이다.

8.5 시나리오 5: 작업 중인 파일 변경을 모두 버리고 싶다

# 추적 중인 파일의 변경사항 모두 취소
git restore .

# 추적되지 않는 새 파일도 모두 삭제
git clean -fd
Removing temp_file.py
Removing debug/
Removing __pycache__/

삭제된 파일과 디렉토리 목록이 출력된다. -f(force)와 -d(디렉토리 포함) 옵션이 필요하다.

경고

git clean -fd는 추적되지 않는 파일과 디렉토리를 영구 삭제한다. 먼저 git clean -nd로 삭제될 파일 목록을 확인한 후 실행하는 것이 안전하다.

git clean -nd
Would remove temp_file.py
Would remove debug/
Would remove __pycache__/

Would remove 로 어떤 파일이 삭제될지 미리 확인할 수 있다. 문제가 없으면 git clean -fd 를 실행한다.

9 요약

되돌리기 판단 기준:

push 했는가?
├── Yes → git revert (안전)
└── No
    ├── commit을 취소하고 싶다
    │   ├── 변경사항 유지 → git reset --soft HEAD~1
    │   └── 변경사항 삭제 → git reset --hard HEAD~1
    ├── staging을 취소하고 싶다 → git restore --staged <파일>
    └── 파일 수정을 취소하고 싶다 → git restore <파일>

Subscribe

Enjoy this blog? Get notified of new posts by email: