힘내 재설정 대 되돌리기 대 리베이스
Git 커밋 활용법: 재설정, 되돌리기, 리베이스
개발자라면 과거 커밋으로 되돌아가야 하는 상황을 겪어봤을 것입니다. Git의 강력한 기능인 reset, revert, rebase는 이러한 작업을 가능하게 하지만, 각각의 차이점을 명확히 이해하는 것이 중요합니다. 이 글에서는 Git 커밋을 다루는 세 가지 주요 명령어인 git reset, git revert, git rebase를 자세히 살펴보고, 그 동작 방식과 사용 사례를 알아봅니다.
Git 재설정 (Reset)
git reset은 변경 사항을 취소하는 데 사용되는 강력한 명령입니다. 이 명령은 특정 커밋으로 돌아가는 기능을 제공하며, --soft, --mixed, --hard 세 가지 모드를 지원합니다. 기본적으로 git reset은 --mixed 모드로 작동합니다. 재설정 과정에서는 Git의 내부 관리 메커니즘인 HEAD, 스테이징 영역(인덱스), 작업 디렉토리가 상호작용합니다.
작업 디렉토리는 현재 작업 중인 파일들이 위치한 곳이며, git status 명령을 통해 현재 상태를 확인할 수 있습니다. 스테이징 영역(인덱스)은 Git이 파일 변경 사항을 추적하고 저장하는 곳으로, 변경 사항은 .git 디렉토리에 반영됩니다. 파일은 git add "filename" 명령으로 스테이징 영역에 추가할 수 있으며, git status를 통해 스테이징 영역에 있는 파일을 확인할 수 있습니다.
HEAD는 현재 브랜치를 가리키는 포인터이며, 마지막 커밋을 참조합니다. 다른 브랜치로 체크아웃하면 HEAD도 새로운 브랜치를 가리키도록 변경됩니다.
Git Reset 모드
각 모드별 git reset의 작동 방식은 다음과 같습니다.
- hard 모드: 지정된 커밋으로 HEAD를 이동시키고, 작업 디렉토리를 해당 커밋의 상태로 되돌립니다. 스테이징 영역도 재설정됩니다. 이는 되돌아가려는 커밋 이후의 모든 변경 사항을 삭제합니다.
- soft 모드: HEAD 포인터만 지정된 커밋으로 이동시킵니다. 작업 디렉토리와 스테이징 영역은 변경되지 않고, 이전 커밋의 파일이 그대로 유지됩니다.
- mixed 모드 (기본값): HEAD 포인터와 스테이징 영역을 지정된 커밋으로 재설정합니다. 작업 디렉토리는 그대로 유지됩니다.
git reset --hard 실습 예제
git reset --hard 명령은 HEAD를 특정 커밋으로 이동시키고 해당 커밋 이후의 모든 커밋을 제거합니다. 다음 예제를 통해 실제 작동 방식을 살펴봅니다.
먼저, 커밋할 것이 없는 깨끗한 상태에서 세 개의 새 파일을 생성하고 내용을 추가합니다.
$ vi file1.txt $ vi file2.txt $ vi file3.txt
생성한 파일을 스테이징 영역에 추가합니다.
$ git add file*
git status를 통해 새 파일들이 추가되었음을 확인합니다.
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: file1.txt new file: file2.txt new file: file3.txt
커밋하기 전, 현재 커밋 로그를 확인합니다.
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
파일을 커밋합니다.
$ git commit -m 'added 3 files' [master d69950b] added 3 files 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt
git ls-files 명령으로 새 파일이 저장소에 추가되었는지 확인합니다.
$ git ls-files demo dummyfile newfile file1.txt file2.txt file3.txt
git log --oneline 명령으로 새 커밋이 추가되었음을 확인합니다.
$ git log --oneline d69950b (HEAD -> master) added 3 files 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
file1.txt 파일을 수동으로 삭제 후 git status 명령을 실행하여 변경 사항이 있음을 확인합니다.
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: file1.txt no changes added to commit (use "git add" and/or "git commit -a")
이제 git reset --hard 명령을 실행하여 지정된 커밋으로 되돌립니다.
$ git reset --hard HEAD is now at d69950b added 3 files
git status를 실행하면 커밋할 항목이 없고, 삭제했던 file1.txt가 다시 추가된 것을 확인할 수 있습니다. 하드 리셋은 커밋되지 않은 변경 사항을 모두 삭제하고, 지정된 커밋 상태로 되돌립니다.
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean
git log 명령을 통해 커밋 로그를 확인하면, d69950b 커밋이 HEAD를 가리키고 있음을 확인할 수 있습니다.
$ git log commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 19:53:31 2020 +0530 added 3 files commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
이번에는 HEAD^를 사용하여 이전 커밋으로 하드 리셋을 실행합니다.
$ git reset --hard HEAD^ HEAD is now at 0db602e one more commit
이제 HEAD가 이전 커밋을 가리키고 있으며, 이전 커밋 이후의 파일 변경사항이 사라졌습니다.
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test

git log 명령을 통해 로그를 다시 확인합니다. d69950b 커밋이 삭제되고 HEAD가 0db602e 커밋을 가리키고 있습니다.
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 Test
ls-files 명령을 실행하면 file1.txt, file2.txt 및 files3.txt가 더 이상 저장소에 없는 것을 확인할 수 있습니다. 해당 파일들은 하드 리셋으로 인해 제거되었습니다.
$ git ls-files demo dummyfile newfile
git reset --soft 실습 예제
git reset --soft 명령의 작동 방식을 알아봅니다. 세 개의 파일을 추가하고 커밋한 후, 커밋 로그는 다음과 같습니다. 'soft reset' 커밋이 최신 커밋이며 HEAD가 이를 가리킵니다.
$ git log --oneline aa40085 (HEAD -> master) soft reset 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
커밋에 대한 자세한 내용은 다음 명령으로 확인할 수 있습니다.
$ git log commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 21:01:36 2020 +0530 soft reset commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
이제 soft reset을 사용하여 이전 커밋으로 전환합니다.
$ git reset --soft 0db602e085a4
git log를 실행하면 HEAD가 지정된 커밋으로 재설정된 것을 확인할 수 있습니다.
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
소프트 리셋의 핵심 차이점은 파일을 추가한 커밋의 파일들이 여전히 작업 디렉터리에 있다는 것입니다. 하드 리셋과 달리 파일이 삭제되지 않으므로 파일 손실 위험 없이 이전 상태로 돌아갈 수 있습니다.
$ git ls-files demo dummyfile file1.txt file2.txt file3.txt newfile
Git 되돌리기 (Revert)
git revert 명령은 특정 커밋의 변경 사항을 되돌리는 데 사용됩니다. 이 명령은 reset과 달리 되돌리는 변경 사항을 새로운 커밋으로 기록합니다. 즉, git revert 명령은 자체적으로 커밋입니다. git revert는 데이터를 삭제하지 않으므로 안전하게 이전 상태로 돌아갈 수 있습니다.
되돌리기 예제를 위해 세 개의 파일을 다시 추가하고 커밋했다고 가정합니다.
$ git commit -m 'add 3 files again' [master 812335d] add 3 files again 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt
로그에 새 커밋이 추가된 것을 확인할 수 있습니다.
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
이제 과거 커밋 중 하나인 "59c86c9 new commit"으로 되돌리려고 합니다.
$ git revert 59c86c9
명령을 실행하면 편집기가 열리고 되돌리려는 커밋 정보가 나타납니다. 이 내용을 기반으로 새로운 커밋 메시지를 생성하고 저장 후 닫습니다.
Revert "new commit" This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Your branch is ahead of 'origin/master' by 4 commits. # (use "git push" to publish your local commits) # # Changes to be committed: # modified: dummyfile
저장하고 닫으면 다음 출력이 표시됩니다.
$ git revert 59c86c9 [master af72b7a] Revert "new commit" 1 file changed, 1 insertion(+), 1 deletion(-)
리셋과 달리 리버트는 새로운 커밋을 생성하여 변경 사항을 되돌립니다. git log 명령을 다시 실행하여 되돌리기 작업으로 생성된 새 커밋을 확인합니다.
$ git log --oneline af72b7a (HEAD -> master) Revert "new commit" 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test

git log에는 모든 커밋 기록이 유지됩니다. 커밋을 기록에서 완전히 제거해야 하는 경우에는 revert가 적합하지 않지만, 커밋의 변경 사항을 기록에 남기고 싶다면 리셋 대신 revert를 사용하는 것이 좋습니다.
Git 리베이스 (Rebase)
git rebase는 한 브랜치의 커밋을 다른 브랜치로 이동하거나 결합하는 데 사용됩니다. 개발 환경에서 기능은 일반적으로 별도의 브랜치에서 개발되며, 기능 개발이 완료되면 이 브랜치의 커밋을 마스터 브랜치로 통합해야 합니다. 이때, rebase는 병합과 유사하게 동작하지만, 커밋 히스토리를 깔끔하게 유지하는 데 유용합니다.
리베이스와 병합의 목표는 동일합니다. 기능 브랜치 커밋을 가져와 마스터 브랜치나 다른 브랜치에 통합하는 것입니다. 예를 들어, 다음과 같은 그래프가 있다고 가정합니다.

여러 개발자가 협업하는 환경에서는 여러 기능 브랜치에서 작업이 이루어지고, 이 변경 사항들을 통합할 때 복잡해질 수 있습니다. 이 때, 리베이스를 사용하면 커밋 히스토리를 깔끔하게 유지할 수 있습니다. 리베이스는 기능 브랜치의 모든 커밋을 가져와 마스터 브랜치의 최신 커밋 위에 재배치합니다.

리베이스를 통해 모든 커밋이 순서대로 깔끔하게 정렬된 직선 그래프를 얻을 수 있습니다.

이 접근 방식은 커밋 추적을 단순화하고 협업을 용이하게 합니다. 여러 개발자가 동시에 작업하는 프로젝트에서도 각 커밋의 흐름을 쉽게 따라갈 수 있습니다.
실제 예를 통해 리베이스의 작동 방식을 자세히 살펴보겠습니다.
현재 마스터 브랜치에는 다음과 같은 4개의 커밋이 있습니다.
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
새로운 기능 개발을 위해, 두 번째 커밋(59c86c9)에서 시작하는 feature 브랜치를 생성하고 체크아웃합니다.
(master) $ git checkout -b feature 59c86c9 Switched to a new branch 'feature'
feature 브랜치의 로그를 확인하면, 마스터 브랜치에서 온 두 개의 커밋만 존재합니다.
(feature) $ git log --oneline 59c86c9 (HEAD -> feature) new commit e2f44fc (origin/master, origin/HEAD) test
feature 브랜치에서 feature1.txt 파일을 생성하고 커밋합니다.
(feature) $ vi feature1.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 1' [feature c639e1b] feature 1 1 file changed, 1 insertion(+) create mode 100644 feature1.txt
추가로 feature2.txt 파일을 생성하고 커밋합니다.
(feature) $ vi feature2.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 2' [feature 0f4db49] feature 2 1 file changed, 1 insertion(+) create mode 100644 feature2.txt
feature 브랜치의 로그를 확인하면, 추가된 두 개의 새로운 커밋을 확인할 수 있습니다.
(feature) $ git log --oneline 0f4db49 (HEAD -> feature) feature 2 c639e1b feature 1 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
이제 이 두 가지 새로운 기능을 마스터 브랜치에 추가하려고 합니다. 이를 위해, feature 브랜치에서 마스터 브랜치에 대해 리베이스를 수행합니다. 이는 feature 브랜치를 최신 마스터 브랜치 위에 재정렬하는 과정입니다.
(feature) $ git rebase master Successfully rebased and updated refs/heads/feature.
이제 마스터 브랜치로 체크아웃합니다.
(feature) $ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits)
마지막으로 마스터 브랜치를 feature 브랜치에 대해 리베이스합니다. 이는 기능 브랜치의 두 개 새로운 커밋을 가져와 마스터 브랜치에 재생하는 작업입니다.
(master) $ git rebase feature Successfully rebased and updated refs/heads/master.
이제 마스터 브랜치의 로그를 확인하면, 기능 브랜치의 두 커밋이 마스터 브랜치에 성공적으로 통합된 것을 확인할 수 있습니다.
(master) $ git log --oneline 766c996 (HEAD -> master, feature) feature 2 c036a11 feature 1 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
이것으로 Git의 reset, revert, rebase 명령어에 대한 설명을 마칩니다.
결론
이 글을 통해 Git의 reset, revert, rebase 명령을 이해하고 필요에 따라 커밋을 조작하는 방법을 배웠기를 바랍니다. 각 명령어의 차이점을 이해하고 상황에 맞게 활용함으로써, 효율적인 협업과 커밋 관리가 가능할 것입니다.