Git 재설정 대 되돌리기 대 Rebase

게시 됨: 2021-05-20

이 기사에서는 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*

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에 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에서 log 명령을 실행하면 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 상태를 수행하면 변경 사항이 커밋을 위해 준비되지 않았다는 메시지가 표시됩니다.

 $ 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

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

힘내

여기서는 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

열심히 1

로그를 확인해보면 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 log가 나타납니다. '소프트 리셋'이 최근 커밋이고 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를 사용하여 이전 커밋 중 하나로 전환하고 싶습니다.

이를 위해 아래 명령을 실행합니다. SHA의 시작 문자를 6자 이상 전달해야 하며 완전한 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 명령이 커밋이라고 해도 과언이 아닙니다.

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

이제 재설정과 달리 필요한 변경을 수행하기 위해 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 로그에는 모든 커밋 기록이 있습니다. 기록에서 커밋을 제거하려면 되돌리기가 좋은 선택이 아니지만 기록에서 커밋 변경 사항을 유지하려면 재설정 대신 되돌리기가 적절한 명령입니다.

힘내 리베이스

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

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

자식 리베이스

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

여기에서 rebase가 도움이 될 것입니다. 이번에는 git merge 대신 rebase를 수행하여 두 가지 기능 브랜치 커밋을 마스터 브랜치로 옮기고자 합니다. 리베이스는 기능 브랜치에서 내 모든 커밋을 가져와 마스터 브랜치 커밋 위로 이동합니다. 따라서 장면 뒤에서 git은 마스터 브랜치에서 기능 브랜치 커밋을 복제하고 있습니다.

자식 리베이스 1

이 접근 방식은 모든 커밋이 한 행에 있는 깨끗한 직선 그래프를 제공합니다.

자식 되돌리기 2

커밋이 어디로 갔는지 쉽게 추적할 수 있습니다. 많은 개발자가 있는 팀에 속해 있으면 모든 커밋이 여전히 연속적으로 진행되는 것을 상상할 수 있습니다. 그래서 많은 사람들이 동시에 같은 프로젝트를 진행하더라도 따라하기 정말 쉽습니다.

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

이것이 내 마스터 브랜치가 현재 어떻게 보이는지입니다. 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)에서 생성됩니다.

 (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) $ 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 명령이 전부였습니다. 이 단계별 가이드가 도움이 되었기를 바랍니다. 이제 기사에서 언급한 명령을 사용하여 필요에 따라 커밋을 처리하는 방법을 알게 되었습니다.