DaleSchool

되돌리기

입문25분

학습 목표

  • git restore로 파일 변경을 취소할 수 있다
  • git reset의 soft/mixed/hard 차이를 이해할 수 있다
  • git revert로 공개 브랜치를 안전하게 되돌릴 수 있다
  • git reflog로 실수로 삭제한 커밋을 복구할 수 있다

동작하는 코드

예제 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
  1. 파일을 수정한 후 git restore로 되돌려보세요.
  2. 스테이징 후 git restore --staged로 스테이징만 취소해보세요.
  3. 커밋 3개를 만들고 git reset --soft HEAD~1, git reset --mixed HEAD~1, git reset --hard HEAD~1을 각각 테스트하고 차이를 비교하세요.
  4. 커밋을 git reset --hard HEAD~1로 삭제한 후 git reflog로 찾아서 복구해보세요.
  5. 커밋 하나를 git revert로 취소하고 히스토리가 어떻게 되는지 git log --oneline으로 확인하세요.

Q1. 이미 push하여 팀원과 공유된 커밋을 안전하게 취소하는 방법은?

  • A) git reset --hard HEAD~1git push --force
  • B) git revert 커밋해시
  • C) git reset --soft HEAD~1git push
  • D) git branch -D 후 다시 만들기