매일 업데이트
2022-11-20 06:55 15 min

힘내 재설정 대 되돌리기 대 리베이스

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 명령을 이해하고 필요에 따라 커밋을 조작하는 방법을 배웠기를 바랍니다. 각 명령어의 차이점을 이해하고 상황에 맞게 활용함으로써, 효율적인 협업과 커밋 관리가 가능할 것입니다.

저자
Korea

기술 트렌드와 실용적인 팁을 전하는 लेखक입니다.