git reset 사용법

git reset 사용법

Git으로 작업하다 보면 방금 한 커밋을 취소하고 싶거나, 스테이징한 파일을 다시 내리고 싶을 때가 있죠. 이럴 때 가장 먼저 떠오르는 명령어가 바로 git reset입니다.

git reset은 현재 브랜치의 HEAD 위치를 옮기면서 커밋 히스토리와 스테이징 영역, 작업 디렉토리의 상태를 조절할 수 있는 강력한 명령어인데요. 그만큼 옵션에 따라 동작이 크게 달라지기 때문에 제대로 이해하고 써야 합니다.

이번 글에서는 git reset의 세 가지 모드를 중심으로 기본 개념부터 실전 활용법까지 함께 알아보겠습니다.

HEAD가 무엇인지 아직 익숙하지 않다면 Git에서 HEAD란 무엇인가?를 먼저 읽어보시면 좋겠습니다.

Git의 세 가지 영역

git reset의 동작을 이해하려면 먼저 Git이 파일을 관리하는 세 가지 영역을 알아야 합니다.

  • 작업 디렉토리(Working Directory) — 실제로 파일을 편집하는 공간입니다. 에디터에서 코드를 수정하면 이 영역이 변경되죠.
  • 스테이징 영역(Staging Area)git add로 다음 커밋에 포함할 변경사항을 모아두는 공간입니다. 인덱스(Index)라고도 부릅니다.
  • 커밋 히스토리(Repository)git commit으로 확정된 스냅샷들이 저장되는 공간입니다. HEAD가 가장 최근 커밋을 가리키고 있죠.

git reset은 이 세 가지 영역 중 어디까지 되돌릴지를 옵션으로 조절합니다.

—soft 모드

--soft 옵션을 사용하면 HEAD만 지정한 커밋으로 이동하고, 스테이징 영역과 작업 디렉토리는 그대로 유지됩니다.

$ git reset --soft <밋>

예를 들어 방금 한 커밋을 취소하되 변경사항은 스테이징 영역에 남겨두고 싶다면 이렇게 하면 됩니다.

$ git log --oneline -3
a1b2c3d (HEAD -> main) 세 번째 커밋
b2c3d4e 번째 커밋
c3d4e5f 번째 커밋

$ git reset --soft HEAD~1

$ git log --oneline -2
b2c3d4e (HEAD -> main) 두 번째 커밋
c3d4e5f 번째 커밋

커밋은 사라졌지만 변경사항이 스테이징 영역에 그대로 있기 때문에 git status를 실행하면 파일이 커밋 대기 상태로 남아있는 것을 확인할 수 있습니다.

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   src/app.js

커밋 메시지를 수정하고 싶거나 직전 커밋의 내용을 살짝 바꿔서 다시 커밋하고 싶을 때 유용하죠.

—mixed 모드 (기본값)

아무 옵션도 지정하지 않으면 git reset은 기본적으로 --mixed 모드로 동작합니다. HEAD를 지정한 커밋으로 이동하면서 스테이징 영역도 함께 되돌리지만, 작업 디렉토리는 건드리지 않습니다.

$ git reset <밋>
# 위 명령어는 아래와 동일합니다
$ git reset --mixed <밋>

마찬가지로 직전 커밋을 취소하는 예시를 볼까요?

$ git log --oneline -3
a1b2c3d (HEAD -> main) 세 번째 커밋
b2c3d4e 번째 커밋
c3d4e5f 번째 커밋

$ git reset HEAD~1

$ git log --oneline -2
b2c3d4e (HEAD -> main) 두 번째 커밋
c3d4e5f 번째 커밋

여기까지는 --soft와 같은데요. 차이점은 스테이징 영역도 초기화된다는 것입니다.

$ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   src/app.js

변경사항이 “Changes not staged for commit”으로 표시되는 것을 볼 수 있죠. 파일 내용은 그대로지만 스테이징에서 내려온 상태입니다. 커밋을 되돌린 뒤 어떤 파일을 커밋에 포함할지 다시 선별하고 싶을 때 적합합니다.

—hard 모드

--hard 옵션을 사용하면 HEAD, 스테이징 영역, 작업 디렉토리를 모두 지정한 커밋 상태로 되돌립니다.

$ git reset --hard <밋>

직전 커밋을 완전히 없었던 일로 만들고 싶다면 이렇게 합니다.

$ git reset --hard HEAD~1
HEAD is now at b2c3d4e 번째 커밋

이번에는 git status가 깨끗합니다.

$ git status
On branch main
nothing to commit, working tree clean

변경사항이 작업 디렉토리에서까지 완전히 사라졌기 때문입니다.

⚠️ 주의: --hard로 삭제된 변경사항은 커밋되지 않은 상태라면 복구하기가 매우 어렵습니다. 정말로 되돌릴 건지 한 번 더 확인하는 습관을 들이세요.

세 가지 모드 비교

세 모드의 차이를 표로 정리하면 다음과 같습니다.

모드HEAD 이동스테이징 초기화작업 디렉토리 초기화
--soft
--mixed (기본값)
--hard

아래쪽으로 갈수록 더 많은 것을 되돌린다고 기억하면 쉽습니다. 확신이 없을 때는 --soft부터 시작해서 필요에 따라 단계를 높이는 게 안전하겠죠.

스테이징 취소

커밋을 되돌리는 것 외에도 git reset은 스테이징을 취소하는 데 자주 쓰였습니다.

$ git add src/app.js
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   src/app.js

$ git reset HEAD src/app.js
Unstaged changes after reset:
M       src/app.js

git reset HEAD <파일>을 실행하면 해당 파일이 스테이징 영역에서 내려오면서 작업 디렉토리에는 변경사항이 그대로 남습니다. 모든 파일의 스테이징을 한꺼번에 취소하려면 파일명 없이 실행하면 됩니다.

$ git reset HEAD

다만 Git 2.23 이후에는 같은 작업을 git restore —staged로 할 수 있어서 스테이징 취소 목적이라면 git restore가 더 직관적입니다.

# 모던 방식 (Git 2.23+)
$ git restore --staged src/app.js

# 레거시 방식
$ git reset HEAD src/app.js

git status를 실행하면 Git이 스테이징 취소 방법으로 git restore --staged를 안내하고 있는 것을 볼 수 있을 거예요.

여러 커밋 한 번에 되돌리기

git resetHEAD~1뿐 아니라 HEAD~n을 전달하면 여러 커밋을 한 번에 되돌릴 수 있습니다.

$ git log --oneline -5
e5f6g7h (HEAD -> main) 다섯 번째 커밋
d4e5f6g 번째 커밋
c3d4e5f 번째 커밋
b2c3d4e 번째 커밋
a1b2c3d 번째 커밋

$ git reset --soft HEAD~3

$ git log --oneline -2
b2c3d4e (HEAD -> main) 두 번째 커밋
a1b2c3d 번째 커밋

최근 3개 커밋이 한꺼번에 취소되었고, --soft를 사용했으므로 변경사항은 스테이징 영역에 모여있습니다. 이 상태에서 git commit을 실행하면 3개 커밋을 하나로 합치는(squash) 효과를 낼 수 있죠.

$ git commit -m "세 번째~다섯 번째 작업을 하나로 합침"

물론 특정 커밋 해시를 직접 지정해도 동일하게 동작합니다.

$ git reset --soft b2c3d4e

실전 활용: 커밋 메시지 수정

방금 커밋했는데 메시지에 오타가 있거나 내용을 보강하고 싶을 때가 있죠. 가장 간편한 방법은 git commit --amend이지만, git reset --soft를 활용할 수도 있습니다.

# 직전 커밋 취소 (변경사항은 스테이징에 유지)
$ git reset --soft HEAD~1

# 새로운 메시지로 다시 커밋
$ git commit -m "수정된 커밋 메시지"

--amend와 달리 커밋 내용을 추가하거나 빼기도 편해서 커밋 자체를 재구성하고 싶을 때 쓸 만하죠.

실전 활용: 잘못된 브랜치에 커밋한 경우

main 브랜치에서 작업해야 할 내용을 실수로 feature 브랜치에 커밋해버린 경우가 있죠. 이럴 때 git reset으로 깔끔하게 정리할 수 있습니다.

# feature 브랜치에 잘못 커밋된 상태
$ git log --oneline -2
a1b2c3d (HEAD -> feature) 잘못된 커밋
b2c3d4e 이전 커밋

# 커밋을 취소하면서 변경사항은 작업 디렉토리에 유지
$ git reset HEAD~1

# 올바른 브랜치로 전환
$ git switch main

# 변경사항을 올바른 브랜치에서 커밋
$ git add .
$ git commit -m "올바른 브랜치에 커밋"

브랜치 전환 시 커밋하지 않은 변경사항이 충돌하는 경우에는 git stash를 먼저 사용한 후 브랜치를 전환하면 됩니다.

git reset과 git revert 비교

git resetgit revert는 둘 다 커밋을 되돌리는 명령어이지만 방식이 다릅니다.

git reset은 커밋 히스토리에서 커밋 자체를 제거합니다. 마치 커밋이 처음부터 없었던 것처럼 히스토리가 깔끔해지죠.

# 직전 커밋 제거 (히스토리에서 사라짐)
$ git reset HEAD~1

반면 git revert는 되돌리는 내용을 담은 새로운 커밋을 생성합니다. 원래 커밋은 히스토리에 그대로 남아있고, 그 변경을 취소하는 커밋이 추가되는 방식이죠.

# 직전 커밋을 되돌리는 새 커밋 생성 (원본 커밋은 히스토리에 유지)
$ git revert HEAD
기준git resetgit revert
히스토리 변경커밋 제거 (히스토리 재작성)새 커밋 추가 (히스토리 보존)
협업 안전성이미 push한 커밋에 사용하면 위험push한 커밋에도 안전
주 사용처로컬에서 아직 push하지 않은 커밋 정리이미 공유된 커밋의 변경 취소

핵심은 아직 push하지 않은 커밋git reset으로 깔끔하게 정리하고, 이미 원격 저장소에 push한 커밋git revert로 되돌리는 것입니다. 이미 push한 커밋을 git reset으로 제거하면 다른 팀원의 히스토리와 충돌이 생겨서 큰 혼란이 생길 수 있으니 주의하세요.

git reflog로 실수 복구

git reset --hard를 실행했는데 되돌리고 싶어졌다면 어떻게 해야 할까요? 다행히 Git은 HEAD가 이동한 모든 기록을 reflog에 남겨두기 때문에 복구가 가능합니다.

$ git reflog
b2c3d4e (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
a1b2c3d HEAD@{1}: commit: 번째 커밋
b2c3d4e HEAD@{2}: commit: 번째 커밋

여기서 a1b2c3dreset 전의 커밋이라는 것을 확인할 수 있죠. 이 커밋으로 다시 돌아가면 됩니다.

$ git reset --hard a1b2c3d
HEAD is now at a1b2c3d 번째 커밋

reflog는 보통 90일간 보관되니까 실수를 발견한 직후라면 대부분 복구할 수 있습니다. 다만 커밋되지 않은 작업 디렉토리의 변경사항은 reflog로도 살릴 수 없으니 --hard를 사용할 때는 항상 조심해야 합니다.

마치며

git reset은 커밋을 되돌리는 데 가장 많이 쓰이는 명령어입니다. 핵심 모드를 정리하면 다음과 같습니다.

  • git reset --soft: HEAD만 이동, 변경사항은 스테이징에 유지
  • git reset --mixed (기본값): HEAD와 스테이징을 이동, 변경사항은 작업 디렉토리에 유지
  • git reset --hard: HEAD, 스테이징, 작업 디렉토리를 모두 초기화

평소에는 --soft--mixed를 사용하고, --hard는 정말 필요할 때만 쓰는 게 안전합니다. 그리고 이미 push한 커밋을 되돌릴 때는 git reset 대신 git revert를 사용하는 것을 잊지 마세요.

더 자세한 내용은 Git 공식 문서를 참고하세요.

This work is licensed under CC BY 4.0 CC BY

Discord