🔥 git reset

939자
8분

git reset 명령어는 현재 브랜치가 가리키고 있는 커밋을 변경할 때 사용하는 강력한 도구예요. 이 명령어를 사용하면 이전 커밋으로 되돌아갈 수 있어요. 하지만 주의해서 사용해야 해요. 왜냐하면 git reset은 커밋 히스토리를 변경하기 때문이죠.

git reset에는 세 가지 모드가 있어요:

  1. -soft: 이 모드는 브랜치가 가리키는 커밋만 변경해요. 스테이징 영역과 워킹 디렉토리는 그대로 유지돼요.
  2. -mixed: 이 모드는 브랜치가 가리키는 커밋을 변경하고, 스테이징 영역도 그에 맞게 업데이트해요. 하지만 워킹 디렉토리는 그대로 유지돼요. 이 모드가 기본 모드예요.
  3. -hard: 이 모드는 브랜치가 가리키는 커밋, 스테이징 영역, 워킹 디렉토리까지 모두 지정한 커밋으로 되돌려요. 이 모드를 사용할 때는 매우 주의해야 해요. 왜냐하면 워킹 디렉토리의 변경 사항이 모두 사라지기 때문이죠.

예를 들어, 현재 브랜치를 이전 커밋으로 되돌리고 싶다면 다음과 같이 할 수 있어요:

# 현재 브랜치를 이전 커밋으로 되돌리기 (--mixed 모드가 기본)
git reset HEAD~1
 
# 현재 브랜치를 특정 커밋으로 되돌리기 (--soft 모드 사용)
git reset --soft 103d6c8
 
# 현재 브랜치를 특정 커밋으로 되돌리고, 워킹 디렉토리까지 모두 되돌리기 (--hard 모드 사용)
git reset --hard 103d6c8
HEAD is now at 103d6c8 Initial commit
shell

git reset은 매우 유용한 명령어지만, 신중하게 사용해야 해요. 특히 --hard 모드는 워킹 디렉토리의 변경 사항을 모두 삭제하기 때문에 주의가 필요해요. 만약 실수로 git reset --hard를 사용했다면, git reflog를 사용하여 이전 상태로 되돌릴 수 있어요.

$ git reflog
shell

git reflog는 히스토리 히스토리로 볼 수 있는데요. 이 명령어를 사용하면 git reset --hard로 삭제된 커밋을 포함한 모든 커밋을 볼 수 있어요. 그리고 원하는 커밋으로 브랜치를 이동시킬 수도 있죠. 현재 브랜치는 feature/signup 입니다.

$ git reflog
103d6c8 (HEAD -> feature/signup, origin/develop) HEAD@{0}: reset: moving to 103d6c84cc6d52f11993fdadd69c8d914e46917e
070ebac HEAD@{1}: reset: moving to 070ebaca5cd8eda7a17b5759dbe2b06c986e876c
2fa353f HEAD@{2}: reset: moving to HEAD~1
1ba1d9b HEAD@{3}: commit: Update README.md
2fa353f HEAD@{4}: commit: Delete README.md
caf90de (main, feature/login) HEAD@{5}: checkout: moving from main to feature/signup
caf90de (main, feature/login) HEAD@{6}: checkout: moving from feature/signup to main
caf90de (main, feature/login) HEAD@{7}: checkout: moving from feature/login to feature/signup
caf90de (main, feature/login) HEAD@{8}: checkout: moving from 160ea51d9b3e1d3fdfcb0e22ba94f223646b9724 to feature/login
160ea51 HEAD@{9}: checkout: moving from main to 160ea51d9b3e1d3fdfcb0e22ba94f223646b9724
caf90de (main, feature/login) HEAD@{10}: checkout: moving from 160ea51d9b3e1d3fdfcb0e22ba94f223646b9724 to main
160ea51 HEAD@{11}: checkout: moving from caf90dee1b4c6b3b00c0149039d34864346e90e1 to 160ea51d9b3e1d3fdfcb0e22ba94f223646b9724
caf90de (main, feature/login) HEAD@{12}: checkout: moving from 713d30794120139e6f55aba91c0e2830ada99ff0 to caf90dee1b4c6b3b00c0149039d34864346e90e1
713d307 HEAD@{13}: checkout: moving from 070ebaca5cd8eda7a17b5759dbe2b06c986e876c to 713d30794120139e6f55aba91c0e2830ada99ff0
070ebac HEAD@{14}: checkout: moving from main to 070ebaca5cd8eda7a17b5759dbe2b06c986e876c
caf90de (main, feature/login) HEAD@{15}: checkout: moving from feature to main
4b450e2 HEAD@{16}: commit: Implement new feature
caf90de (main, feature/login) HEAD@{17}: checkout: moving from main to feature
caf90de (main, feature/login) HEAD@{18}: commit: Update README
070ebac HEAD@{19}: commit: Initial commit
713d307 HEAD@{20}: commit: Update hello.txt
160ea51 HEAD@{21}: commit: Add message
9c5b645 HEAD@{22}: commit (amend): Add hello.txt
4045db5 HEAD@{23}: commit: Add hello.txt
fe5b5e0 (origin/main, origin/HEAD) HEAD@{24}: reset: moving to HEAD
fe5b5e0 (origin/main, origin/HEAD) HEAD@{25}: commit: update README.md
103d6c8 (HEAD -> feature/signup, origin/develop) HEAD@{26}: clone: from github.com:codingmax-tube/git-commands-cookbook.git
shell

현재 디렉토리에서는 아래와 같은 파일이 있습니다.

$ ls -al
 
total 24
drwxr-xr-x   6 codingmax  staff   192  3 29 22:12 .
drwxr-xr-x  10 codingmax  staff   320  3 29 17:47 ..
drwxr-xr-x  14 codingmax  staff   448  3 29 22:12 .git
-rw-r--r--   1 codingmax  staff  2047  3 29 17:37 .gitignore
-rw-r--r--   1 codingmax  staff  1066  3 29 17:37 LICENSE
-rw-r--r--   1 codingmax  staff    23  3 29 22:12 README.md
shell

이 상태에서 1ba1d9b 로 되돌려 볼게요.

$ git reset --hard HEAD@{3}
HEAD is now at 1ba1d9b Update README.md
shell

디렉토리를 다시 보면 사라졌던 hello.txt 가 다시 존재하는 것을 알 수 있습니다.

% ls -al
 
total 32
drwxr-xr-x   7 codingmax  staff   224  3 29 22:21 .
drwxr-xr-x  10 codingmax  staff   320  3 29 17:47 ..
drwxr-xr-x  14 codingmax  staff   448  3 29 22:21 .git
-rw-r--r--   1 codingmax  staff  2047  3 29 17:37 .gitignore
-rw-r--r--   1 codingmax  staff  1066  3 29 17:37 LICENSE
-rw-r--r--   1 codingmax  staff    15  3 29 22:21 README.md
-rw-r--r--   1 codingmax  staff    13  3 29 22:21 hello.txt
shell

이 상태에서 다시 git reflog를 확인해 보면 아래와 같아요.

$ git reflog
 
1ba1d9b (HEAD -> feature/signup) HEAD@{0}: reset: moving to HEAD@{3}
103d6c8 (origin/develop) HEAD@{1}: reset: moving to 103d6c84cc6d52f11993fdadd69c8d914e46917e
070ebac HEAD@{2}: reset: moving to 070ebaca5cd8eda7a17b5759dbe2b06c986e876c
2fa353f HEAD@{3}: reset: moving to HEAD~1
1ba1d9b (HEAD -> feature/signup) HEAD@{4}: commit: Update README.md
2fa353f HEAD@{5}: commit: Delete README.md
caf90de (main, feature/login) HEAD@{6}: checkout: moving from main to feature/signup
caf90de (main, feature/login) HEAD@{7}: checkout: moving from feature/signup to main
caf90de (main, feature/login) HEAD@{8}: checkout: moving from feature/login to feature/signup
caf90de (main, feature/login) HEAD@{9}: checkout: moving from 160ea51d9b3e1d3fdfcb0e22ba94f223646b9724 to feature/login
160ea51 HEAD@{10}: checkout: moving from main to 160ea51d9b3e1d3fdfcb0e22ba94f223646b9724
caf90de (main, feature/login) HEAD@{11}: checkout: moving from 160ea51d9b3e1d3fdfcb0e22ba94f223646b9724 to main
160ea51 HEAD@{12}: checkout: moving from caf90dee1b4c6b3b00c0149039d34864346e90e1 to 160ea51d9b3e1d3fdfcb0e22ba94f223646b9724
caf90de (main, feature/login) HEAD@{13}: checkout: moving from 713d30794120139e6f55aba91c0e2830ada99ff0 to caf90dee1b4c6b3b00c0149039d34864346e90e1
713d307 HEAD@{14}: checkout: moving from 070ebaca5cd8eda7a17b5759dbe2b06c986e876c to 713d30794120139e6f55aba91c0e2830ada99ff0
070ebac HEAD@{15}: checkout: moving from main to 070ebaca5cd8eda7a17b5759dbe2b06c986e876c
caf90de (main, feature/login) HEAD@{16}: checkout: moving from feature to main
4b450e2 HEAD@{17}: commit: Implement new feature
caf90de (main, feature/login) HEAD@{18}: checkout: moving from main to feature
caf90de (main, feature/login) HEAD@{19}: commit: Update README
070ebac HEAD@{20}: commit: Initial commit
713d307 HEAD@{21}: commit: Update hello.txt
160ea51 HEAD@{22}: commit: Add message
9c5b645 HEAD@{23}: commit (amend): Add hello.txt
4045db5 HEAD@{24}: commit: Add hello.txt
fe5b5e0 (origin/main, origin/HEAD) HEAD@{25}: reset: moving to HEAD
fe5b5e0 (origin/main, origin/HEAD) HEAD@{26}: commit: update README.md
103d6c8 (origin/develop) HEAD@{27}: clone: from github.com:codingmax-tube/git-commands-cookbook.git
shell

위 예제에서 HEAD@{3}는 reflog에서 세 번째 이전 상태를 의미해요. 이렇게 하면 git reset --hard로 삭제된 커밋으로 브랜치를 이동시킬 수 있습니다.

lecture image

이 다이어그램은 git reset의 세 가지 모드가 어떻게 작동하는지 보여줘요. 그리고 git reflog를 사용하여 git reset --hard로 삭제된 커밋을 복구할 수 있다는 것도 보여주고 있죠.

git reset을 사용할 때는 항상 주의해야 해요. 특히 협업 프로젝트에서는 더욱 그래요. 다른 사람과 공유한 커밋을 git reset으로 되돌리면 충돌이 발생할 수 있거든요. 따라서 git reset은 로컬 브랜치에서만 사용하는 것이 좋아요. 그리고 git reflog를 사용하여 실수로 삭제한 커밋을 복구할 수 있다는 것도 기억해 두세요!