Skip to content

되돌리기와 복구

로컬에서 커밋을 정리하고, 실수한 히스토리를 되돌리고, reflog로 복구하는 방법을 한 흐름으로 정리합니다.

HEAD와 세 가지 참조 방식

HEAD는 지금 작업 중인 위치를 가리키는 포인터입니다.
Git에서 과거 상태를 가리키는 방법은 크게 세 가지로 나눠서 보면 이해가 쉽습니다.

1. 상대 참조

표현의미
HEAD현재 위치
HEAD~첫 번째 부모로 1단계 이전
HEAD~3첫 번째 부모 기준 3단계 이전
HEAD^첫 번째 부모
HEAD^2merge commit일 때 두 번째 부모
bash
git switch --detach HEAD~
git switch --detach HEAD~3

~^의 차이

단순 직선 히스토리에서는 둘이 비슷하게 보이지만, merge commit에서는 다릅니다. ~는 첫 번째 부모를 따라가고, ^2는 두 번째 부모를 가리킬 수 있습니다.

2. reflog 참조

표현의미
HEAD@{0}가장 최근 HEAD 위치
HEAD@{3}3번 전 HEAD 위치
main@{1}main 브랜치가 이전에 가리키던 위치
HEAD@{yesterday}어제 시점의 HEAD
HEAD@{"1 week ago"}1주 전 HEAD
bash
git show HEAD@{2}
git show HEAD@{yesterday}
git log main@{1}

3. 직전 브랜치 참조

표현의미
@{-1}방금 전 체크아웃했던 브랜치
@{-2}그 이전 브랜치
bash
git switch @{-1}

브랜치 전환 기본은 06. 브랜치 전략에서 먼저 보고, 여기서는 복구할 때 이 참조를 어떻게 쓰는가에 집중하면 됩니다.

git commit --amend

가장 최근 커밋에 파일을 추가하거나 커밋 메시지를 수정할 때 사용합니다.

파일 추가를 깜빡했을 때

bash
git add 추가할파일.txt
git commit --amend

커밋 메시지만 수정할 때

bash
git commit --amend

기본형은 에디터를 열어 현재 메시지를 확인한 뒤 수정하는 방식이 더 안전합니다.
git commit --amend -m "..."도 가능하지만, 본문이나 트레일러를 덮어쓸 수 있어 무조건 기본형으로 쓰는 편이 낫습니다.

--amend는 로컬 정리용이다

이미 push한 커밋을 amend하면 원격 이력과 달라집니다. 공유 브랜치에서는 revert를 먼저 검토하고, 개인 PR 브랜치라면 나중에 git push --force-with-lease까지 함께 이해하고 써야 합니다.

git reset - 포인터와 작업 상태 되돌리기

git resetHEAD, 스테이징 영역, 워킹 디렉토리를 어느 정도까지 되돌릴지 결정하는 명령어입니다.

명령어HEAD 이동스테이징워킹 디렉토리
git reset --soft <target>이동유지유지
git reset --mixed <target>이동초기화유지
git reset --hard <target>이동초기화초기화

자주 쓰는 예시

bash
# 최근 커밋만 해제하고 내용은 남기기
git reset --soft HEAD~

# 스테이징만 취소하고 내용은 워킹 디렉토리에 남기기
git reset --mixed HEAD~

# 스테이징 취소만 하고 현재 내용은 그대로 두기
git restore --staged event.txt

# 특정 커밋 상태로 완전히 되돌리기
git reset --hard <commit>

--hard는 가장 마지막 선택지다

git reset --hard는 현재 워킹 디렉토리 변경까지 버립니다. 복구 문서를 읽을 때도 기본 복구 순서는 find -> 새 브랜치 생성 -> 검증 -> 정말 필요하면 reset입니다.

git revert - 공유 브랜치에서 안전하게 취소하기

git revert는 특정 커밋을 지우는 대신, 반대 작업을 하는 새 커밋을 생성합니다.

구분git resetgit revert
동작이력을 이동하거나 삭제되돌리는 새 커밋 생성
기존 커밋사라질 수 있음유지됨
원격 공유 브랜치위험안전
협업 친화성낮음높음
bash
git revert HEAD
git revert abc1234

merge commit을 되돌릴 때는 부모를 지정하는 -m 옵션이 필요합니다. 이 단계는 초급 범위를 넘기므로, 실무에서는 먼저 팀 컨벤션을 확인하는 편이 안전합니다.

git reflog - 로컬 히스토리 안전망

git log현재 브랜치에 남아 있는 커밋 이력을 보여줍니다.
git reflogHEAD나 브랜치 포인터가 어디를 가리켔는지까지 기록합니다.

핵심 특징

항목의미
범위로컬 저장소 전용
기본 대상HEAD
추가 특징HEAD reflog는 브랜치 전환도 기록
주요 명령git reflog, git reflog show <ref>
별칭 관계git reflog show는 사실상 git log -g 계열 출력

자주 쓰는 명령

bash
# 현재 HEAD reflog
git reflog

# 특정 브랜치 reflog
git reflog show main

# 시간 정보까지 보기 좋게 출력
git reflog show main --date=iso --no-decorate

# 특정 시점 상태 확인
git show HEAD@{yesterday}

보관 기간

reflog는 영구 보관이 아닙니다.

설정기본값의미
gc.reflogExpire90일도달 가능한 reflog 항목
gc.reflogExpireUnreachable30일도달 불가(dangling) 항목

실수로 삭제한 커밋 복구에서는 사실상 30일 창이 더 중요합니다.

git reflog find는 정식 명령이 아니다

원래 자료처럼 git reflog find <검색어>를 쓰면 안 됩니다. 검색이 필요하면 아래처럼 처리합니다.

bash
git reflog | grep reset
git log -g --grep="commit"

git reflog expire --expire=now --all은 평소엔 거의 안 쓴다

존재는 알아둘 만하지만, 학습용 기준에서는 "오래된 로컬 reflog를 정리하는 고급 명령" 정도로만 보면 충분합니다.

reflog로 안전하게 복구하기

복구의 기본 순서는 다음 네 단계입니다.

1. 먼저 위치를 찾는다

bash
git reflog
git reflog show main --date=iso --no-decorate

2. 새 브랜치로 안전하게 고정한다

bash
git switch -c recovery HEAD@{3}

이렇게 하면 복구 후보 지점을 새 브랜치로 먼저 보존할 수 있습니다.
이 단계를 건너뛰고 곧바로 reset --hard를 치는 습관이 가장 위험합니다.

3. 무엇을 하고 싶은지에 따라 명령을 고른다

상황추천 명령
브랜치 전체를 그 시점으로 되돌리고 싶다git reset --hard <hash>
잃어버린 특정 커밋만 현재 브랜치에 다시 올리고 싶다git cherry-pick <hash>
현재 체크아웃하지 않은 다른 브랜치 포인터만 옮기고 싶다git branch -f <branch> <hash>

특정 커밋만 가져오기

bash
git cherry-pick <hash>

다른 브랜치 포인터 옮기기

bash
git switch <다른-브랜>
git branch -f main <hash>

git branch -f는 현재 체크아웃 중인 브랜치에 바로 쓸 수 없다

현재 머물고 있는 브랜치를 강제로 옮기려 하면 Git이 거부합니다. 현재 브랜치를 움직일 때는 보통 git reset --hard <hash>가 맞고, 다른 브랜치를 움직일 때 git branch -f를 씁니다.

마지막 fallback

reflog가 만료되었거나 단서를 놓쳤다면 git fsck --lost-found로 dangling commit을 찾는 방법도 있습니다. 다만 reflog보다 읽기 어렵고 마지막 수단에 가깝습니다.

Detached HEAD 상태

특정 커밋을 직접 가리키면 detached HEAD 상태가 됩니다.

bash
git switch --detach HEAD@{2}
git switch --detach <commit>

이 상태는 "그 시점 코드를 잠깐 확인하거나 실험"할 때는 괜찮습니다.
문제는 이 상태에서 새 커밋을 만들고 브랜치로 연결하지 않으면, 나중에 찾기 어려워질 수 있다는 점입니다.

빠져나오는 법

bash
# 그냥 나가기
git switch main

# 지금 상태를 새 브랜치로 보존하기
git switch -c spike

git rebase -i - 로컬 히스토리 정리

여러 로컬 커밋을 다듬고 순서를 조정하고 합치는 대표 도구입니다.

bash
git rebase -i HEAD~4

자주 보는 액션

액션의미
pick그대로 유지
reword메시지만 수정
edit내용 수정 후 계속 진행
squash이전 커밋과 합치고 메시지도 함께 정리
fixup이전 커밋과 합치되 메시지는 버림
drop해당 커밋 제거

가장 실무적인 흐름: fixup + autosquash

코드 리뷰 피드백 반영 후 커밋을 예쁘게 정리할 때 자주 씁니다.

bash
git commit --fixup <정리할-대상-커>
git rebase -i --autosquash HEAD~4

--autosquash를 쓰면 fixup 커밋이 자동으로 제자리로 이동하고 fixup 액션까지 맞춰집니다.

충돌이 나면

bash
git rebase --continue
git rebase --skip
git rebase --abort
명령의미
--continue충돌 해결 후 계속
--skip현재 커밋 건너뜀
--abort시작 전 상태로 복귀

알아두면 좋은 옵션

옵션의미
--update-refs함께 움직여야 할 로컬 브랜치 참조 갱신 보조
--keep-base현재 base를 유지하며 rebase
--rebase-mergesmerge 구조를 유지한 채 rebase

push한 이력을 다시 쓸 때

공유 브랜치에서는 rebase -i보다 revert가 안전합니다.
내가 혼자 쓰는 PR 브랜치라면 이후 push는 git push --force-with-lease를 기본으로 보고, 환경이 허용하면 --force-if-includes도 검토할 수 있습니다. 맨 --force는 피하는 편이 좋습니다.

언제 무엇을 사용하나?

핵심 정리

명령어용도이력핵심 주의점
git commit --amend직전 커밋 수정덮어쓰기기본은 로컬만
git reset --soft커밋 해제, 내용과 스테이징 유지이동정리용
git reset --mixed커밋 해제, 스테이징만 초기화이동기본 reset
git reset --hard특정 시점으로 완전 복귀이동워킹 디렉토리 삭제 위험
git revert되돌리는 새 커밋 생성보존공유 브랜치 기본 선택
git reflog포인터 이동 기록 확인보존로컬 전용, 만료됨
git cherry-pick특정 커밋만 다시 적용보존충돌 가능
git branch -f다른 브랜치 포인터 강제 이동이동현재 브랜치엔 직접 못 씀
git rebase -i로컬 히스토리 정리재작성push 후엔 매우 신중