[GitHub] 실수 없이 되돌리기: Git reset 옵션 완전 정복
안녕하세요!
이번에는 Git의 Reset 명령어에대해 알아보도록하겠습니다!
들어가며..
Git을 쓰다 보면 reset, revert, checkout, restore처럼 비슷한 명령어들이 많아 헷갈릴 때가 있습니다.
특히 reset은 Git의 히스토리를 직접 조작하는 명령어이기 때문에, 잘못 사용하면 복구하기 어려운 상황이 생길 수 있습니다.
이 글에서는 먼저 Git reset 옵션의 작동 원리를 개념적으로 설명한 뒤, 각각의 옵션(--soft, --mixed, --hard)을 예시와 함께 살펴보고, 마지막에는 실제 상황에서 어떤 옵션을 선택해야 할지 정리해보겠습니다.
각 박스는 커밋을 나타내며, hello.txt 파일의 변경 이력을 V1 → V2 → V3 순서로 보여줍니다.
커밋 해시(a1b2c3d 등)와 각 버전의 내용, 그리고 현재 HEAD와 브랜치(main)가 가리키는 위치에 주목해주세요
이 구조를 참고해 Git 명령어로 `HEAD`가 어떻게 이동하고, 어떤 커밋이 유지되거나 사라지는지를 함께 알아봅시다!
이미지에서 화살표 방향이 오른쪽에서 왼쪽으로 진행되어 헷갈릴 수 있는데, 이 흐름은 시간 순이 아니라 Git 내부 포인터가 이동하는 구조적 흐름을 보여주는 것입니다!
그리고 이번 글을 이해하기위해서는 Git이 관리하는 세 개의 트리에대한 사전지식이 있어야 하는데, 잘 모르는분들은 아래 게시글을 참고해주세요!
[GitHub]Git 핵심 개념 한번에 이해하기: 3대 영역 + HEAD와 Branch
Reset 이란?
reset은 브랜치의 커밋 포인터(HEAD)를 과거로 되돌리는 명령어입니다.
또한 옵션에 따라 Index(Staging Area)나 Working Directory까지 같이 되돌릴지 정할 수 있어요
기본적인 상태 이미지는 위와 같습니다.
d4e5f6g 커밋으로 되돌린다고 가정하고, 옵션에 따른 HEAD/ Index(Staging Area)/ Working Directory의 변화를 살펴보겠습니다
잠깐! Index를 Staging Area라고도 하는데요! 여기 이후부터는 편의상 Index라고만 표기하겠습니다 ㅎㅎ
reset의 3가지 옵션 비교
reset 명령어는 옵션이 총 세 가지가 있는데요
옵션에 따라 조금씩 달라서 각각의 특징을 알아두는 게 좋은데, 저희 자세히 공부해보기 전에 각각 어떤 특징을 가지고 있는지 표로 간단히 보고 시작해봅시다!
옵션 | HEAD 이동 | Index(Staging Area) | Working Directory | 사용 예시 / 설명 |
--soft | ✅ 이동 | ❌ 유지 | ❌ 유지 | 커밋만 되돌리고, 변경 내용은 그대로 스테이징해두고 싶을 때 |
--mixed (기본값) | ✅ 이동 | ✅ 초기화 | ❌ 유지 | 스테이징된 내용까지 되돌리고, 파일은 그대로 둘 때 |
--hard | ✅ 이동 | ✅ 초기화 | ✅ 초기화 | 변경사항을 모두 없애고 커밋 상태로 완전히 되돌리고 싶을 때 |
조금 감이 잡히시나요?!!
자! 그럼 --soft 옵션부터 시작해봅시다!
--soft
HEAD만 지정한 커밋으로 이동하고, Index와 Working Directory는 그대로 유지됩니다.
즉, 커밋 기록은 되돌려지지만, 그 안에 포함되었던 변경 내용은 여전히 스테이징 상태에 남아 있습니다.
💡 스테이징 상태란?
Git의 Index에 파일 내용이 임시로 올라간 상태
git reset --soft <커밋해시>
주요 특징
- 커밋 기록만 되돌림
- 변경 내용은 그대로 Index에 남아 있음
- 실수한 커밋을 정리하거나 메시지만 바꾸고 싶을 때 유용
이번에는 HEAD/ Index/ Working Directory가 각각 어떤 데이터를 가지고 있는지 살펴봅시다!
앞서 언급한 것처럼, --soft 옵션은 HEAD만 지정한 커밋으로 이동시키는 역할을 합니다.
이미지를 보면, HEAD는 이전 커밋(V2)로 이동했지만, Index와 Working Directory에는 여전히 최신 커밋(V3)의 내용이 남아 있는 것을 확인할 수 있습니다.
Index에 변경 사항이 유지된다는 건, 작업 결과물을 그대로 둔 채 커밋만 되돌릴 수 있다는 뜻이겠죠?
즉, 안전하게 커밋만 취소하고 다시 작성할 수 있는 정리 도구로 활용할 수 있어요.
예를들어 커밋 메시지를 잘못 적었거나, 직전 커밋에 내용을 추가하고 싶은 경우 --soft를 사용하면 변경 사항을 다시 준비할 필요 없이 곧바로 git commit만 다시 하면 됩니다!
실수한 커밋 메시지를 수정하거나, 커밋을 합치는 등의 작업을 할 때 유용하겠죠?
잠깐! 그럼 여기서 git commit을 하게된다면 V3로 되돌려질까요?!!
아닙니다! Index에 남아있던 V3 내용이 V4라는 아예 새로운 커밋으로 만들어집니다.
HEAD는 V2였기 때문에, V4는 V3와 파일 내용은 같지만, 커밋 시점이나 메시지, 해시 값이 달라서 Git 입장에서는 전혀 다른 커밋으로 인식됩니다.
이전에 있었던 V3 커밋은 위 이미지처럼 어떤 브랜치나 HEAD에서도 참조되지 않기 때문에, 기본 커밋 히스토리에서는 더 이상 보이지 않게 됩니다. (잠시 동안 내부에 남아 있을 수 있지만, 일정 시간이 지나면 자동으로 정리됨)
예제
이번엔 실제 상황을 가정해서 --soft 옵션을 어떻게 활용하는지 직접 살펴볼까요?!
1. 커밋 메세지 수정
만약, 저희가 버그 수정을하고 커밋 메세지에 "refactor"를 적어야하는데 "fix"로 잘 못 적은 상황이라고 가정해봅시다.
# 문제 상황
echo "버그 수정" >> login.swift
git add login.swift
git commit -m "fix: 버그"
이때 --soft 옵션으로 간단히 되돌릴 수 있는데요!
이 옵션을 사용하면 가장 최근 커밋만 되돌리고, 변경된 파일들은 여전히 Index에 남아있는 상태에요.
# 가장 최근 커밋 되돌리기
git reset --soft HEAD~1
# 커밋 수정
git commit -m "refactor: 로그인 로직 개선"
즉, --soft옵션을 사용하면 git add를 다시 하지 않아도 Index에 변경 사항이 남아있기때문에 변경 내용은 그대로 유지되고, 메시지만 바꿔서 다시 커밋이 가능합니다.
2. 여러 개의 커밋을 하나로 합치고 싶을 때 (수동 squash)
만약, 아래 세개 커밋을 한 개로 합치고 싶은 상황이라고 가정해봅시다!
# 문제 상황
git log --oneline
> a1b2c3 fix: 로그인 버튼 동작 오류 수정
> d4e5f6 style: 로그인 화면 margin 조정
> 38eb94 refactor: 로그인 뷰모델 로직 분리
저는 최근 커밋중에 세개의 커밋을 합치고 싶기때문에 --soft reset HEAD~3로 세번째 커밋으로 되돌립니다.
여기도 당연히 Index에 변경 사항이 남아있기때문에 바로 commit만 해주면 최근 세개의 커밋 변경 사항이 하나로 합쳐지겠죠?
# 커밋 합치기
git reset --soft HEAD~3
# 커밋 수정
git commit -m "refactor(login): 로그인 뷰모델 및 UI 구조 개선"
이 방법을 사용하면 rebase -i 명령어보다 더 간단하게 수동 squash를 할 수 있습니다!
간단히 커밋 로그를 정리하고 싶을 때 아주 유용하겠죠?!
잠만.. squash가 뭔데 갑자기 나와?
squash란 문장 뜻 그대로 눌러서(squash) 하나로 뭉친다는 뜻입니다!
즉, 여러 개의 커밋을 하나로 합쳐서, 커밋 히스토리를 더 깔끔하게 만드는 Git 작업을 말합니다!
git reset --soft는 간편하게 여러 커밋을 하나로 합칠 수 있지만,
조금 더 정교하게 커밋 메시지를 다듬고, 순서나 병합 방식을 조절하고 싶다면 git rebase -i 명령어를 사용하는 방법이 있습니다.
git rebase -i HEAD~3
해당 명령어를 치면 커밋 메시지를 하나로 합칠지, 어떤 메시지를 남길지 편집할 수 있는 창이 뜨고,
메시지를 정리한 후 저장하면 세 개의 커밋이 하나로 합쳐지게 됩니다.
자세한 내용은 다음 글에서 적겠습니다!
3. 새로운 브랜치에서 작업하려고 할 때
만약, feature/login-refactor 브랜치에서 작업을해야하는데 main브랜치에서 커밋해버린 상황이라고 가정해봅시다!
# 작업한 커밋 되돌리기
git reset --soft HEAD~1
# 브랜치 변경
git switch -c feature/login-refactor
# feature/login-refactor 브랜치에서 커밋
git commit -m "refactor: 로그인 로직 개선"
이렇게 실수한 브랜치의 작업 내용은 유지하고, 커밋만 적절한 브랜치로 옮길 수 있습니다!
--soft 옵션의 중요한 포인트는 변경 사항이 Index에 유지된다는 점이겠죠?!!
자 이제 --soft는 어느정도 이했으니 --mixed 옵션 알아볼까요?
--mixed
HEAD를 이동하고, Index를 현재 HEAD 기준으로 초기화합니다.
Working Directory는 그대로 유지됩니다.
어?! --soft 옵션은 Index를 유지했는데 --mixed 옵션은 초기화 하는군요!!!
git reset --mixed <커밋해시>
# 또는 아무 옵션도 주지 않으면 기본적으로 적용되는 옵션
git reset <커밋해시>
주요 특징
- 커밋과 Index 초기화
- 변경된 파일은 Working Directory에 그대로 남음
- 실수로 커밋했지만 작업 내용은 남기고 싶을 때 유용
자, --mixed 옵션 실행 후 HEAD/ Index/ Working Dirextory는 어떻게 변경될까요?!
위에서 설명했던 것처럼 커밋을 되돌리는 동시에, staging 상태인 Index도 초기화합니다.
다만 Working Directory는 그대로 유지되기 때문에, 파일의 변경 내용 자체는 그대로 남아 있습니다.
이 방식은 단순히 커밋만 수정하는 것이 아니라, 커밋에 포함된 파일을 일부 수정하거나 변경 대상을 다시 정해서 커밋하고 싶을 때 적합합니다.
예를 들어 실수로 staging하지 말아야 할 파일까지 add한 채 커밋했다면, --mixed를 사용해 커밋과 staging 상태를 지운 뒤 다시 필요한 파일만 선택적으로 add하여 새로 커밋할 수 있습니다.
다만 --mixed는 Index가 비워지기 때문에, 어떤 파일을 이전에 staging했는지 기억하지 못하면 다시 커밋할 때 빠뜨릴 수 있다는 점에 주의가 필요합니다.
즉, 작업 파일의 상태를 명확히 알고 있을 때 사용하도록 주의해야합니다.
예제
이번에도 실제 상황을 가정해서 --mixed 옵션이 어떤 상황에서 유용한지 살펴볼까요?
1. 커밋을 취소하고 변경 파일만 Working Directory에 남기고 싶을 때
만약, 실수로 완전히 준비되지 않은 상태에서 커밋을 해버린 상황이라고 가정해봅시다.
# 문제 상황
echo "임시 수정" >> login.swift
git add login.swift
git commit -m "wip: 로그인 임시 수정"
아직 메시지도 애매하고, 파일이 제대로 정리도 안 되었는데 커밋을 해버린 상황이죠?!
이럴 땐 --mixed 옵션으로 커밋도 되돌리고, Index만 초기화한 채 변경 파일은 Working Directory에 그대로 남겨둘 수 있습니다.
# 커밋 되돌리기
git reset --mixed HEAD~1
즉, 변경된 파일은 Working Directory에 살아있지만 git status를 하면 "Unstaged" 상태겠죠?!
이 상태에서 Working Directory파일을 마저 수정하고 원하는 메세지로 commit해주면됩니다!
2. 어떤 파일을 add 했는지 다시 보고 정리하고 싶을 때
만약, 커밋에 포함되면 안 되는 test.swift파일을 커밋해버린 상황이라고 가정해봅시다
# 문제 상황
git add login.swift test.swift AppDelegate.swift
git commit -m "fix: 여러 파일 수정"
--mixed HEAS~1 명령어를 사용하여 이전 커밋으로 되돌리고 다시 원하는 파일만 추가해봅시다!
# 커밋 되돌리기
git reset --mixed HEAD~1
# 원하는 파일 추가
git add login.swift AppDelegate.swift
git commit -m "fix: 필수 수정 사항만 반영"
세 파일이 모두 Unstaged 상태로 되돌아와서, 다시 원하는 파일만 골라서 add 해줍니다!
3. 새로운 브랜치로 이동하고 변경 파일을 다시 선택하고 싶을 때
만약, 아직 커밋하지 않은 채 한참 작업을 하다가,
input_validator.swift 파일은 main 브랜치가 아닌 feature/input-validation 브랜치에서 처리했어야 한다는 걸 뒤늦게 깨달은 상황이라고 가정해봅시다.
# 문제 상황
git add .
git commit -m "feat: 입력 검증 추가"
이럴 때 --mixed 옵션을 사용하면 커밋을 되돌리고, index만 초기화한 상태로 브랜치를 바꿔줍니다.
# 스테이징만 초기화
git reset --mixed HEAD~1
# 브랜치 변경
git switch -c feature/input-validation
# 원하는 파일 추가
git add input_validator.swift
# feature/input-validation브랜치에 파일 추가
git commit -m "feat: 입력 검증 기능 추가"
상황이 --soft 옵션과 비슷하지만 중요한 차이점이 하나 있습니다!
--soft 옵션에서는 git add를 다시 하지 않고 바로 커밋할 수 있었지만, --mixed 옵션에서는 우리가 다시 파일을 git add를 해주고 있죠?
그 이유는, --mixed 옵션이 Index를 비워버리기 때문입니다!
그러니까 커밋하려면 반드시 git add로 다시 파일을 추가해줘야합니다.
이렇게 하면 작업 내용은 그대로 유지하면서, 브랜치도 새로 만들고 커밋도 다시 깔끔하게 정리할 수 있습니다!
--mixed 옵션의 중요한 포인트는 변경 사항이 Index에 유지된다는 점이겠죠?!!
자 이제 --hard 명령어로 가봅시다!!!
--hard
HEAD, Index, Working Directory까지 모두 HEAD 기준으로 되돌리는 가장 강력한 옵션입니다.
즉, 커밋도 삭제되고, staging 상태도 사라지며, 파일의 실제 변경 내용도 완전히 제거됩니다.
결과적으로 되돌리기 전 상태로 완전히 돌아가기 때문에, 실수로 사용하면 복구가 매우 어렵거나 불가능할 수 있습니다.
원격 저장소에 push하지 않은 커밋이나, 저장하지 않은 변경 내용까지 전부 날아가므로 매우 주의해서 사용해야 합니다.
--hard 옵션은 HEAD기준으로 모두 덮어씌워지는군요..?!
git reset --hard <커밋해시>
주요 특징
- 작업 내용까지 전부 삭제됨
- Git이 관리하던 내역도 날아갈 수 있음
- 아주 신중하게 사용해야 함
자, 그럼 --hard 명령어를 실행했을때 HEAD/ Index/ Working Dirextory는 어떤 상태일까요?!
이 명령은 이전 커밋으로 HEAD를 이동시킬 뿐만 아니라, Index, Working Directory(작업 파일) 까지 모두 이전 커밋 상태로 덮어씌워버리기때문에 commit, add, 작업 파일 수정까지 전부 사라집니다.
즉, 아래와 같은 상태가 됩니다.
- HEAD → 이전 커밋으로 이동
- Index → 이전 커밋 기준 상태로 덮어쓰기
- Working Directory → 파일 내용까지 이전 상태로 되돌림 (편집했던 내용 전부 날아감)
결과적으로 commit, add, 파일 수정까지 전부 삭제됩니다.
이 방식은 단순히 커밋과 staging 상태를 되돌리는 것을 넘어, 작업 중인 파일 변경 내용까지 전부 되돌려야 할 때 적합합니다.
즉, 현재 작업 중인 내용 자체를 모두 버리고, 이전 커밋 상태로 완전히 초기화하고 싶을 때 사용하는 옵션입니다.
예를 들어, 여러 파일을 수정하고 커밋도 여러 번 했지만, 작업이 잘못되었다는 걸 깨닫고 아예 처음 상태로 되돌리고 싶을 때,
--hard 옵션을 사용하면 HEAD, Index, Working Directory 모두 이전 커밋 상태로 되돌릴 수 있습니다.
이때 변경한 파일이나 커밋 내역은 복구할 수 없으므로, 정말 버려도 괜찮은 작업일 때만 사용하는 것이 안전합니다.
예제
이번에도 실제 상황을 가정해서 --hard 옵션이 어떤 상황에서 유용한지 알아봅시다
1. 작업 내용까지 모두 초기화하고 싶을 때
만약, 코드를 수정했는데 작업 내용이 마음에 들지 않아서 완전히 처음 상태로 되돌리고 싶은 상황이라고 가정해봅시다.
# 문제 상황
echo "실패한 로직" >> login.swift
git add login.swift
git commit -m "feat: 실험적인 로그인 로직 도입"
이제 커밋 로그를 보면, 아래처럼 38eb94 해시 커밋이 추가된 것을 확인할 수 있습니다.
# 커밋 내역 확인
git log --oneline
> 38eb94 feat: 실험적인 로그인 로직 도입
> d4e5f6 fix: 로그인 입력 검증
# 삭제하고 싶은 커밋 되돌리기
git reset --hard HEAD~1
이제 방금 커밋한 38eb94의 작업 내용을 완전히 삭제해봅시다!
실행 결과는 위 이미지랑 같습니다
- HEAD → d4e5f6으로 이동
- Index → 이전 상태로 초기화
- Working Directory → "실패한 로직" 내용 삭제됨
즉, 이렇게 --hard 명령어는 단순히 HEAD만 이전 커밋으로 되돌리는 게 아니라,
Index와 Working Directory(작업 폴더)까지 모두 이전 상태로 덮어씌웁니다!
⚠️ 주의할 점
--hard는 Git이 관리하는 변경 내역까지 완전히 제거하므로,
로컬에서 수정 중이던 파일도 복구 불가능하게 날아갈 수 있습니다.
정말로 작업 내용을 버려도 괜찮은지 반드시 확인한 후, 신중하게 사용하세요!
그런데!! 정말 아예 --hard 옵션을 못 되돌릴까요!?!!!!
아닙니다!!
--hard 옵션 되돌리기
보통 --hard는 커밋, staging 상태, 작업 중인 파일 변경까지 모두 삭제하므로 되돌릴 수 없다고 알려져 있습니다.
하지만 reset 직후라면 되돌릴 수 있는 가능성도 있습니다.
Git은 최근의 HEAD 위치를 내부적으로 reflog에 저장하기 때문인데요!
한번 되돌려 봅시다!
# 최근 HEAD 히스토리 확인
git reflog
# 결과
a1b2c3 HEAD@{0}: reset: moving to HEAD~1
d4e5f6 HEAD@{1}: commit: 로그인 버튼 오류 수정
우선 reflog 명령어를 실행해서 HEAD 히스토리를 확인합니다.
그럼 커밋 기록이 나오는데, 여기서 원하는 커밋 해시를 복사해서 복구해주면됩니다!
# 되돌리고 싶은 커밋 해시 복구
git reset --hard d4e5f6
이렇게 하면 --hard 이전 커밋 상태로 되돌릴 수 있어요.
그런데, 여기서 알아야할 점은 commit이 꼭 되어있어야한다는 점이겠죠?!
commit이 안 되어있으면 되돌릴 수 없어요.. 주의해주세요!
마무리하며..
마지막으로상황에 따라 어떤 옵션을 사용하는 게 적절한지 간단히 정리해보면 다음과 같습니다
- 커밋 메시지만 수정하고 싶을 때
git reset --soft HEAD~1
→ 커밋만 되돌리고, 스테이징 상태는 유지됨. 메시지 재작성에 적합. - 커밋은 취소하되, 어떤 파일을 다시 올릴지 고르고 싶을 때
git reset --mixed HEAD~1
→ 커밋과 스테이징만 초기화. 워킹 디렉토리는 그대로 유지되어 선택적으로 git add 가능. - 커밋과 파일까지 모두 잘못한 경우, 전부 되돌리고 싶을 때
git reset --hard HEAD~1
→ 커밋, 스테이징, 파일 상태 모두 초기화. 복구 어려우므로 주의 필요. - 여러 커밋을 한꺼번에 되돌리고 브랜치를 초기화하고 싶을 때
git reset --hard <커밋 해시>
→ 해당 커밋으로 HEAD를 이동시키고, 그 이후 작업은 모두 삭제됨. - 과거 커밋을 참고만 하고 현재 작업은 유지하고 싶을 때
git reset --soft <커밋 해시>
→ 커밋만 되돌리고, 파일과 스테이징 상태는 그대로. 커밋 히스토리만 정리할 때 유용.
이렇게 git reset은 강력하지만, 옵션에 따라 되돌릴 수 없는 결과를 만들 수 있습니다.
되도록 --soft, --mixed, --hard의 차이를 확실히 이해하고 사용하는 습관을 들이면 실수를 줄일 수 있겠죠?!
다음 글에서는 checkout 명령어의 동작 방식과 reset과의 차이점을 비교해보면서, 두 명령어를 언제, 어떻게 써야 하는지 정리해보겠습니다!