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

이 기사에서는 Git에서 커밋을 사용하는 다양한 방법에 대해 알아봅니다.

개발자로서 이전 커밋 중 하나로 롤백하고 싶었지만 어떻게 해야 할지 모르는 상황을 여러 번 겪었을 것입니다. 그리고 reset, revert, rebase와 같은 Git 명령을 알고 있더라도 이들 간의 차이점을 인식하지 못합니다. 이제 시작하여 git reset, revert 및 rebase가 무엇인지 이해해 봅시다.

힘내 재설정

Git reset은 복잡한 명령이며 변경 사항을 실행 취소하는 데 사용됩니다.

git reset을 롤백 기능으로 생각할 수 있습니다. git reset을 사용하면 다양한 커밋 사이를 이동할 수 있습니다. git reset 명령 실행에는 –soft, –mixed 및 –hard의 세 가지 모드가 있습니다. 기본적으로 git reset 명령은 혼합 모드를 사용합니다. git reset 워크플로에서는 git의 세 가지 내부 관리 메커니즘인 HEAD, 스테이징 영역(색인) 및 작업 디렉터리가 등장합니다.

작업 디렉토리는 현재 작업 중인 파일이 있는 위치입니다. git status 명령을 사용하여 작업 디렉토리에 있는 모든 파일/폴더를 확인할 수 있습니다.

스테이징 영역(인덱스)은 git이 파일의 모든 변경 사항을 추적하고 저장하는 곳입니다. 저장된 변경 사항은 .git 디렉토리에 반영됩니다. git add “filename”을 사용하여 스테이징 영역에 파일을 추가합니다. 이전과 마찬가지로 git status를 실행하면 스테이징 영역에 어떤 파일이 있는지 확인할 수 있습니다.

Git의 현재 분기를 HEAD라고 합니다. 현재 체크아웃 분기에서 발생한 마지막 커밋을 가리킵니다. 모든 참조에 대한 포인터로 취급됩니다. 다른 지점으로 체크아웃하면 HEAD도 새 지점으로 이동합니다.

하드, 소프트 및 혼합 모드에서 git reset이 어떻게 작동하는지 설명하겠습니다. 하드 모드는 지정된 커밋으로 이동하는 데 사용되며 작업 디렉토리는 해당 커밋의 파일로 채워지고 스테이징 영역은 재설정됩니다. 소프트 리셋에서는 포인터만 지정된 커밋으로 변경됩니다. 모든 커밋의 파일은 재설정 전에 작업 디렉터리와 스테이징 영역에 남아 있습니다. 혼합 모드(기본값)에서는 포인터와 스테이징 영역이 모두 재설정됩니다.

힘내 리셋 하드

git hard reset의 목적은 HEAD를 지정된 커밋으로 옮기는 것입니다. 지정된 커밋 이후에 발생한 모든 커밋을 제거합니다. 이 명령은 커밋 기록을 변경하고 지정된 커밋을 가리킵니다.

이 예에서는 세 개의 새 파일을 추가하고 커밋한 다음 하드 리셋을 수행합니다.

아래 명령에서 볼 수 있듯이 지금은 커밋할 것이 없습니다.

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.

(use "git push" to publish your local commits)

nothing to commit, working tree clean

이제 3개의 파일을 만들고 내용을 추가하겠습니다.

$ vi file1.txt
$ vi file2.txt
$ vi file3.txt

이러한 파일을 기존 리포지토리에 추가합니다.

$ git add file*

상태 명령을 다시 실행하면 방금 만든 새 파일이 반영됩니다.

$ 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에 3개의 커밋 로그가 있습니다.

$ 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

ls-files를 실행하면 새 파일이 추가된 것을 볼 수 있습니다.

$ git ls-files
demo
dummyfile
newfile
file1.txt
file2.txt
file3.txt

git에서 로그 명령을 실행하면 4개의 커밋이 있고 HEAD는 최신 커밋을 가리킵니다.

$ 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
HEAD is now at d69950b added 3 files

상태를 다시 확인하면 커밋할 항목이 없으며 삭제한 파일이 저장소에 다시 들어왔습니다. 롤백이 일어난 이유는 파일 삭제 후 커밋을 안해서 하드리셋 후 이전 상태로 돌아갔거든요.

$ 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의 로그를 확인하면 다음과 같이 표시됩니다.

$ 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

하드 리셋의 목적은 지정된 커밋을 가리키고 작업 디렉터리와 스테이징 영역을 업데이트하는 것입니다. 한 가지 예를 더 보여드리겠습니다. 현재 내 커밋의 시각화는 다음과 같습니다.

  Gmail에서 이메일 추적을 중지하는 방법

여기서는 HEAD^로 명령을 실행할 것입니다. 즉, 이전 커밋으로 재설정하고 싶습니다(하나의 커밋 다시).

$ git reset --hard HEAD^
HEAD is now at 0db602e one more commit

이제 헤드 포인터가 d69950b에서 0db602e로 변경된 것을 볼 수 있습니다.

$ git log --oneline
0db602e (HEAD -> master) one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

로그를 확인하면 d69950b의 커밋이 사라지고 헤드가 이제 0db602e SHA를 가리킵니다.

$ 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

힘내 소프트 재설정

마찬가지로 이제 소프트 리셋의 예를 보여 드리겠습니다. 위에서 언급한 대로 3개의 파일을 다시 추가하고 커밋했습니다. 아래와 같이 git 로그가 나타납니다. ‘소프트 리셋’이 내 최신 커밋이고 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

이제 소프트 리셋을 사용하여 SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14를 사용하여 이전 커밋 중 하나로 전환하고 싶습니다.

이를 위해 아래 명령을 실행합니다. 6개 이상의 SHA 시작 문자를 전달해야 하며 완전한 SHA는 필요하지 않습니다.

$ 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

그러나 여기서 차이점은 3개의 파일을 추가한 커밋(aa400858aab3927e79116941c715749780a59fc9)의 파일이 여전히 내 작업 디렉토리에 있다는 것입니다. 삭제되지 않았습니다. 그렇기 때문에 하드 리셋이 아닌 소프트 리셋을 사용해야 합니다. 소프트 모드에서는 파일이 손실될 위험이 없습니다.

$ git ls-files
demo
dummyfile
file1.txt
file2.txt
file3.txt
newfile

힘내 되돌리기

Git에서 revert 명령은 되돌리기 작업, 즉 일부 변경 사항을 되돌리기 위해 사용됩니다. reset 명령과 유사하지만 여기서 유일한 차이점은 특정 커밋으로 돌아가기 위해 새로운 커밋을 수행한다는 것입니다. 요컨대 git revert 명령이 커밋이라고 말하는 것이 타당합니다.

  BackupBuddy로 데이터베이스를 원활하게 마이그레이션

Git 되돌리기 명령은 되돌리기 작업을 수행하는 동안 데이터를 삭제하지 않습니다.

되돌리기 예제를 위해 파일 3개를 추가하고 git commit 작업을 수행한다고 가정해 보겠습니다.

$ 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 새 커밋”으로 되돌리고 싶습니다. 아래 명령을 실행합니다.

$ 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(-)

이제 필요한 변경을 수행하기 위해 재설정과 달리 revert는 새로운 커밋을 한 번 더 수행했습니다. 로그를 다시 확인하면 되돌리기 작업으로 인해 새로운 커밋을 찾을 수 있습니다.

$ 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 로그에는 모든 커밋 기록이 있습니다. 기록에서 커밋을 제거하려면 revert가 좋은 선택이 아니지만 기록에서 커밋 변경 사항을 유지하려면 revert가 reset 대신 적합한 명령입니다.

힘내 리베이스

Git에서 rebase는 한 분기의 커밋을 다른 분기로 이동하거나 결합하는 방법입니다. 개발자로서 저는 실제 시나리오에서 마스터 브랜치에 내 기능을 생성하지 않을 것입니다. 나는 내 자신의 브랜치(‘기능 브랜치’)에서 작업하고 기능이 추가된 내 기능 브랜치에 커밋이 몇 개 있을 때 마스터 브랜치로 옮기고 싶습니다.

Rebase는 병합과 매우 유사하기 때문에 때때로 이해하기 약간 혼란스러울 수 있습니다. 두 가지를 병합하고 리베이스하는 목표는 내 기능 브랜치에서 커밋을 가져와 마스터 브랜치 또는 다른 브랜치에 배치하는 것입니다. 다음과 같은 그래프가 있습니다.

다른 개발자와 팀을 이루어 작업하고 있다고 가정합니다. 이 경우 다른 기능 분기에서 작업하는 많은 다른 개발자가 있고 여러 변경 사항을 병합하는 경우 이것이 정말 복잡해질 수 있다고 상상할 수 있습니다. 추적하기가 혼란스러워집니다.

  iPhone에서 배터리 백분율을 표시하는 방법

따라서 여기에서 rebase가 도움이 될 것입니다. 이번에는 git merge를 수행하는 대신 리베이스를 수행하여 두 개의 기능 분기 커밋을 마스터 분기로 이동하려고 합니다. 리베이스는 기능 브랜치에서 모든 커밋을 가져 와서 마스터 브랜치 커밋 위로 이동합니다. 따라서 배후에서 git은 기능 분기 커밋을 마스터 분기에 복제합니다.

이 접근 방식은 모든 커밋이 연속된 깔끔한 직선 그래프를 제공합니다.

어떤 커밋이 어디로 갔는지 쉽게 추적할 수 있습니다. 많은 개발자가 있는 팀에 속해 있다고 상상할 수 있습니다. 모든 커밋이 여전히 행에 있습니다. 따라서 같은 프로젝트에 여러 사람이 동시에 작업하는 경우에도 쉽게 따라할 수 있습니다.

이것을 실제로 보여드리겠습니다.

이것이 현재 내 마스터 브랜치의 모습입니다. 4개의 커밋이 있습니다.

$ git log --oneline
812335d (HEAD -> master) add 3 files again
0db602e one more commit
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

아래 명령을 실행하여 feature라는 새 분기를 생성하고 전환할 것이며 이 분기는 2차 커밋, 즉 59c86c9에서 생성됩니다.

(master)
$ git checkout -b feature 59c86c9
Switched to a new branch 'feature'

기능 분기에서 로그를 확인하면 마스터(메인라인)에서 오는 커밋이 2개뿐입니다.

(feature)
$ git log --oneline
59c86c9 (HEAD -> feature) new commit
e2f44fc (origin/master, origin/HEAD) test

기능 1을 만들고 기능 분기에 커밋합니다.

(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

기능 분기에 하나 이상의 기능, 즉 기능 2를 만들고 커밋합니다.

(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)
$ git log --oneline
0f4db49 (HEAD -> feature) feature 2
c639e1b feature 1
59c86c9 new commit
e2f44fc (origin/master, origin/HEAD) test

이제 이 두 가지 새로운 기능을 마스터 브랜치에 추가하고 싶습니다. 이를 위해 rebase 명령을 사용합니다. 기능 분기에서 마스터 분기에 대해 리베이스합니다. 이것이 할 일은 최신 변경 사항에 대해 내 기능 분기를 다시 고정하는 것입니다.

(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)

마지막으로 내 기능 분기에 대해 마스터 분기를 리베이스합니다. 이렇게 하면 내 기능 브랜치에서 두 개의 새로운 커밋을 가져와 내 마스터 브랜치 위에서 재생합니다.

(master)
$ git rebase feature
Successfully rebased and updated refs/heads/master.

이제 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의 재설정, 되돌리기 및 리베이스 명령에 관한 것입니다.

결론

Git의 재설정, 되돌리기 및 리베이스 명령에 관한 것입니다. 이 단계별 가이드가 도움이 되었기를 바랍니다. 이제 기사에 언급된 명령을 사용하여 필요에 따라 커밋을 가지고 노는 방법을 알았습니다.