git reset 명령은 변경 사항을 실행 취소하는 데 사용하는 복잡한 다용도 도구입니다. 이 명령에는 세 가지 기본 호출 형식이 있습니다. 이러한 형식은 명령줄 인수 --soft, --mixed, --hard에 해당하며, 세 인수는 각각 Git의 3가지 내부 상태 관리 메커니즘인 커밋 트리(HEAD), 스테이징 색인 및 작업 디렉터리에 해당합니다.

Git Reset 및 Git 3개의 트리

git reset 사용법을 제대로 파악하려면 먼저 Git의 내부 상태 관리 시스템을 이해해야 합니다. 이러한 메커니즘을 Git의 "3개의 트리"라고 부르기도 합니다. 트리는 엄밀히 말하면 전통적인 트리 데이터 구조가 아니기 때문에 잘못된 명칭일 수도 있습니다. 3개의 트리는 Git이 편집 타임라인을 추적하는 데 사용하는 노드 및 포인터 기반 데이터 구조입니다. 이 메커니즘을 보여주는 가장 좋은 방법은 리포지토리에서 변경 집합을 만들고 3개의 트리를 통해 따라가는 것입니다.

시작하기 위해 아래 명령으로 새 리포지토리를 만들어 보겠습니다.

$ mkdir git_reset_test
$ cd git_reset_test/
$ git init .
Initialized empty Git repository in /git_reset_test/.git/
$ touch reset_lifecycle_file
$ git add reset_lifecycle_file
$ git commit -m"initial commit"
[main (root-commit) d386d86] initial commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 reset_lifecycle_file

위의 예시 코드는 하나의 빈 파일인 reset_lifecycle_file을 사용하여 새로운 Git 리포지토리를 만듭니다. 이때 예시 리포지토리는 reset_lifecycle ycle_file을 추가하여 단일 커밋(d386d86)을 가집니다.

작업 디렉터리

가장 먼저 살펴볼 트리는 "작업 디렉터리"입니다. 이 트리는 로컬 파일 시스템과 동기화되며 파일 및 디렉터리의 콘텐츠에 대한 즉각적인 변경 사항을 나타냅니다.


$ echo 'hello git reset' > reset_lifecycle_file
$ git status 
On branch main
Changes not staged for commit: 
(use "git add ..." to update what will be committed) 
(use "git checkout -- ..." to discard changes in working directory) 
modified: reset_lifecycle_file

데모 리포지토리에서 일부 콘텐츠를 수정하고 reset_lifecycle_file에 추가합니다. git status를 호출하면 Git이 파일의 변경 사항을 인식하고 있음을 알 수 있습니다. 이러한 변경 사항은 현재 첫 번째 트리인 "작업 디렉터리"의 일부입니다. git status는 작업 디렉터리에 대한 변경 사항을 표시하는 데 사용할 수 있습니다. 'modified' 접두사와 함께 빨간색으로 표시됩니다.

스테이징 색인

다음은 '스테이징 색인' 트리입니다. 이 트리는 git add로 승격된 작업 디렉터리 변경 사항을 추적하여 다음 커밋에 저장합니다. 이 트리는 복잡한 내부 캐싱 메커니즘입니다. Git은 보통 스테이징 색인의 구현 세부 정보를 사용자에게 숨기려고 합니다.

스테이징 색인의 상태를 정확하게 확인하려면 조금 덜 알려진 Git 명령인 git ls-files를 사용해야 합니다. git ls-files 명령은 기본적으로 스테이징 색인 트리의 상태를 검사하기 위한 디버그 유틸리티입니다.

git ls-files -s
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   reset_lifecycle_file

여기서는 -s 또는 --stage 옵션을 사용하여 git ls-files를 실행했습니다. -s 옵션이 없으면 git ls-files 출력은 단순히 현재 색인에 포함된 파일 이름과 경로의 목록일 뿐입니다. -s 옵션은 스테이징 색인에 있는 파일에 대한 추가 메타데이터를 표시합니다. 이 메타데이터는 스테이징된 콘텐츠의 모드 비트, 개체 이름 및 스테이지 번호입니다. 여기서 주목해야 할 부분은 두 번째 값인 개체 이름(d7d77c1b04b5edd5acfc85de0b592449e5303770)입니다. 이것은 표준 Git 개체 SHA-1 해시로, 파일 콘텐츠의 해시입니다. 커밋 기록은 커밋과 참조에 대한 포인터를 식별하기 위한 자체적인 개체 SHA를 저장하며, 스테이징 색인은 색인에 있는 파일 버전을 추적하는 자체적인 개체 SHA가 있습니다.

다음으로 수정된 reset_lifecycle_file을 스테이징 색인으로 승격시켜 보겠습니다.


$ git add reset_lifecycle_file 

$ git status 

On branch main Changes to be committed: 

(use "git reset HEAD ..." to unstage) 

modified: reset_lifecycle_file

여기에서 스테이징 색인에 파일을 추가하는 git add reset_lifecycle_file을 호출했습니다. 이제 git status를 호출하면 "커밋할 변경 사항" 아래에 녹색으로 reset_lifecycle_file이 표시됩니다. 중요한 것은 git status가 스테이징 색인의 진정한 표현이 아니하는 사실입니다. git status 명령 출력은 커밋 기록과 스테이징 색인 간의 변경 사항을 표시합니다. 이 시점에서 스테이징 색인 콘텐츠를 살펴보겠습니다.

 $ git ls-files -s 100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file

reset_lifecycle_file에 대한 개체 SHA가 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391에서 d7d77c1b04b5edd5acfc85de0b592449e5303770으로 업데이트되었음을 알 수 있습니다.

커밋 기록

마지막 트리는 커밋 기록입니다. git commit 명령은 커밋 기록에 있는 영구적인 스냅샷에 변경 사항을 추가합니다. 이 스냅샷은 커밋 시점의 스테이징 색인 상태도 포함합니다.

$ git commit -am"update content of reset_lifecycle_file"
[main dc67808] update content of reset_lifecycle_file
1 file changed, 1 insertion(+)
$ git status
On branch main
nothing to commit, working tree clean

여기에서는 "update content of resetlifecyclefile" 메시지가 포함된 새 커밋을 만들었습니다. 변경 집합이 커밋 기록에 추가됐습니다. 이 시점에서 git status를 호출하면 트리에 보류 중인 변경 사항이 없음을 알 수 있습니다. git log를 실행하면 커밋 기록이 표시됩니다. 이제 3개의 트리를 통해 이 변경 집합을 따랐으므로 git reset을 활용할 수 있습니다.

작동 방식

표면적인 수준에서 볼 때 git resetgit checkout과 동작이 비슷합니다. git checkoutHEAD 참조 포인터에서만 작동하는 경우, git resetHEAD 참조 포인터 및 현재 브랜치 참조 포인터를 이동시킵니다. 다음 예시를 통해 이러한 동작을 더 자세히 설명해 보겠습니다.

"main 노드"가 마지막인 노드 4개

이 예시는 main 브랜치의 커밋 시퀀스를 보여줍니다. HEAD 참조와 main 브랜치 참조는 현재 커밋 d를 가리킵니다. 이제 git checkout bgit reset b를 실행하여 비교해 보겠습니다.

git checkout b

main이 마지막 노드를 가리키고 head가 두 번째 노드를 가리키는 노드 4개

git checkout의 경우 main 참조는 여전히 d를 가리키고 있습니다. HEAD 참조가 이동했으며 이제 커밋 b를 가리킵니다. 리포지토리는 이제 '분리된 HEAD' 상태입니다.

git reset b

2개 노드로 이루어진 세트 2개, head 포함, main이 첫 번째 세트의 두 번째를 가리킴

반면 git resetHEAD 및 브랜치 참조를 모두 지정된 커밋으로 이동시킵니다.

참조 포인터를 업데이트하는 것 외에도 git reset은 3개의 트리 상태를 수정합니다. 참조 포인터 수정은 항상 발생하며 세 번째 트리인 커밋 트리에 대한 업데이트입니다. 명령줄 인수 --soft, --mixed--hard는 스테이징 색인 및 작업 디렉터리 트리를 수정하는 방법을 지시합니다.

메인 옵션

git reset의 기본 호출에는 --mixedHEAD라는 암시적 인수가 있습니다. 즉, git reset을 실행하는 것은 git reset --MIXED HEAD를 실행하는 것과 같습니다. 이 형식에서 HEAD는 지정된 커밋입니다. HEAD 대신 Git SHA-1 커밋 해시를 사용할 수 있습니다.

git reset 범위 다이어그램

--hard

가장 직접적이고 위험하며 자주 사용하는 옵션입니다. --hard를 전달하면 커밋 기록 참조 포인터가 지정된 커밋으로 업데이트됩니다. 그런 다음 스테이징 색인 및 작업 디렉터리가 지정된 커밋과 일치하도록 재설정됩니다. 스테이징 색인 및 작업 디렉터리에 대해 보류 중인 이전의 변경 사항은 커밋 트리 상태와 일치하도록 재설정됩니다. 즉, 스테이징 색인 및 작업 디렉터리에서 보류 중이었던 모든 작업이 손실된다는 것을 의미합니다.

이것을 설명하기 위해 앞서 만든 3개의 트리 예시 리포지토리로 계속해 보겠습니다. 먼저 리포지토리를 약간 수정해 보겠습니다. 예시 리포지토리에서 다음 명령을 실행합니다.

$ echo 'new file content' > new_file
$ git add new_file
$ echo 'changed content' >> reset_lifecycle_file
 

이러한 명령은 new_file이라는 새 파일을 만들어 리포지토리에 추가했습니다. 또한 reset_lifecycle_file 콘텐츠가 수정될 것입니다. 이러한 변경 사항을 적용했으니 이제 git status를 사용하여 리포지토리 상태를 살펴보겠습니다.

$ git status
On branch main
Changes to be committed:
   (use "git reset HEAD ..." to unstage)

new file: new_file

Changes not staged for commit:
   (use "git add ..." to update what will be committed)
   (use "git checkout -- ..." to discard changes in working directory)

modified: reset_lifecycle_file

현재 리포지토리에 보류 중인 변경 사항이 있음을 알 수 있습니다. 스테이징 색인 트리에 new_file 추가에 대한 보류 중인 변경 사항이 있으며, 작업 디렉터리에는 reset_lifecycle_file 수정에 대한 보류 중인 변경 사항이 있습니다.

계속 진행하기 전에 스테이징 색인의 상태도 살펴보겠습니다.

$ git ls-files -s
100644 8e66654a5477b1bf4765946147c49509a431f963 0 new_file
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file

new_file이 색인에 추가된 것을 볼 수 있습니다. reset_lifecycle_file을 업데이트했지만 스테이징 색인 SHA(d7d77c1b04b5edd5acfc85de0b592449e5303770)는 그대로 유지됩니다. git add를 사용하여 이러한 변경 사항을 스테이징 색인으로 승격하지 않았기 때문에 이는 예상된 동작입니다. 이러한 변경 사항은 작업 디렉터리에 있습니다.

이제 git rese --hard를 실행하고 리포지토리의 새 상태를 살펴보겠습니다.

$ git reset --hard
HEAD is now at dc67808 update content of reset_lifecycle_file
$ git status
On branch main
nothing to commit, working tree clean
$ git ls-files -s
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file

여기서는 --hard 옵션을 사용하여 "하드 재설정"을 실행했습니다. Git은 HEAD가 최근 커밋 dc67808을 가리키고 있다는 출력을 표시합니다. 다음으로 git status로 리포지토리 상태를 확인합니다. Git은 보류 중인 변경 사항이 없음을 표시합니다. 스테이징 색인의 상태도 살펴보고 new_file이 추가되기 전의 시점으로 재설정되었는지 확인합니다. reset_lifecycle_file에 대한 수정 사항과 new-file 추가는 삭제되었습니다. 이 데이터 손실은 되돌릴 수 없으므로 주의해야 합니다.

--mixed

이것은 기본 작동 모드로 참조 포인터가 업데이트됩니다. 스테이징 색인은 지정된 커밋 상태로 재설정됩니다. 스테이징 색인에서 실행 취소된 모든 변경 사항은 작업 디렉터리로 이동합니다. 계속해 보겠습니다.

$ echo 'new file content' > new_file
$ git add new_file
$ echo 'append content' >> reset_lifecycle_file
$ git add reset_lifecycle_file
$ git status
On branch main
Changes to be committed:
    (use "git reset HEAD ..." to unstage)

new file: new_file
modified: reset_lifecycle_file


$ git ls-files -s
100644 8e66654a5477b1bf4765946147c49509a431f963 0 new_file
100644 7ab362db063f9e9426901092c00a3394b4bec53d 0 reset_lifecycle_file

위의 예시에서는 리포지토리를 일부 수정했습니다. 다시 말하자면, new_file을 추가하고 reset_lifecycle_file 콘텐츠를 수정했습니다. 그런 다음 git add를 사용하여 이러한 변경 사항을 스테이징 색인에 적용했습니다. 이 상태의 리포지토리에서 이제 재설정을 실행합니다.

$ git reset --mixed
$ git status
On branch main
Changes not staged for commit:
    (use "git add ..." to update what will be committed)
    (use "git checkout -- ..." to discard changes in working directory)

modified: reset_lifecycle_file

Untracked files:
    (use "git add ..." to include in what will be committed)

new_file


no changes added to commit (use "git add" and/or "git commit -a")
$ git ls-files -s
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file

여기에서는 "혼합 재설정"을 실행했습니다. 다시 말하자면, --mixed는 기본 모드이며 git reset을 실행하는 것과 동일한 효과가 있습니다. git statusgit ls-files 출력을 살펴보면 reset_lifecycle_file이 색인에서 유일한 파일인 상태로 스테이징 색인이 재설정되었음을 알 수 있습니다. reset_lifecycle_file에 대한 개체 SHA가 이전 버전으로 재설정되었습니다.

여기서 주목해야 할 중요한 점은 git statusreset_lifecycle_file에 수정 사항이 있었으며 추적되지 않은 파일인 new_file이 있음을 알려준다는 것입니다. 이것은 명시적인 --mixed 동작입니다. 스테이징 색인이 재설정되었고 보류 중인 변경 사항이 작업 디렉터리로 이동했습니다. 스테이징 색인이 재설정되고 작업 디렉터리도 재설정되어 업데이트가 손실된 --hard 재설정 사례와 비교해 보세요.

--soft

--soft 인수가 전달되면 참조 포인터가 업데이트되고 재설정이 중지됩니다. 스테이징 색인 및 작업 디렉터리는 그대로 유지됩니다. 이 동작은 명확하게 보여주기 어려울 수 있습니다. 데모 리포지토리로 계속하여 소프트 재설정 준비를 해보겠습니다.


$ git add reset_lifecycle_file 

$ git ls-files -s 

100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file 

$ git status 

On branch main

Changes to be committed: 

(use "git reset HEAD ..." to unstage) 

modified: reset_lifecycle_file 

Untracked files: 

(use "git add ..." to include in what will be committed) 

new_file

여기서는 다시 git add를 사용하여 수정된 reset_lifecycle_file을 스테이징 색인으로 승격시켰습니다. git ls-files 출력으로 색인이 업데이트되었음을 확인합니다. 이제 git status 출력에 "커밋할 변경 사항"이 녹색으로 표시됩니다. 이전 예시의 new_file은 작업 디렉터리에서 추적되지 않은 파일로 남아 있습니다. 다음 예시에서는 필요하지 않으므로 rm new_file을 빠르게 실행하여 파일을 삭제해 보겠습니다.

이 상태의 리포지토리에서 이제 소프트 재설정을 실행합니다.

$ git reset --soft
$ git status
On branch main
Changes to be committed:
    (use "git reset HEAD ..." to unstage)

modified: reset_lifecycle_file
$ git ls-files -s
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file

'소프트 재설정'을 실행했습니다. git statusgit ls-file로 리포지토리 상태를 살펴보면 아무것도 변경되지 않았음을 알 수 있습니다. 이것은 예상된 동작입니다. 소프트 재설정은 커밋 기록만 재설정합니다. 기본적으로 git resetHEAD를 대상 커밋으로 하여 호출됩니다. 커밋 기록이 이미 HEAD에 있었고 암시적으로 HEAD로 재설정했기 때문에 아무 일도 일어나지 않았습니다.

--soft를 더 잘 이해하고 활용하려면 HEAD가 아닌 대상 커밋이 필요합니다. 스테이징 색인에 대기 중인 reset_lifecycle ycle_file이 있습니다. 새 커밋을 만들어 보겠습니다.

$ git commit -m"prepend content to reset_lifecycle_file"

이 시점에서는 리포지토리에 커밋이 3개 있어야 합니다. 이제 첫 번째 커밋으로 시간을 거슬러 올라가겠습니다. 이를 수행하려면 첫 번째 커밋의 ID가 필요합니다. 이 ID는 git log의 출력을 확인하여 찾을 수 있습니다.

$ git log
commit 62e793f6941c7e0d4ad9a1345a175fe8f45cb9df
Author: bitbucket 
Date: Fri Dec 1 15:03:07 2017 -0800
prepend content to reset_lifecycle_file

commit dc67808a6da9f0dec51ed16d3d8823f28e1a72a
Author: bitbucket 
Date: Fri Dec 1 10:21:57 2017 -0800

update content of reset_lifecycle_file

commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4

Author: bitbucket 
Date: Thu Nov 30 16:50:39 2017 -0800

initial commit

커밋 기록 ID는 시스템마다 고유합니다. 즉, 이 예시의 커밋 ID는 개인 컴퓨터에 표시되는 것과 다르게 표시됩니다. 이 예시에서 우리가 살펴볼 커밋 ID는 780411da3b47117270c0e3a8d5dcfd11d28d04a4입니다. 이 ID는" 최초 커밋"에 해당하는 ID입니다. ID를 찾고 나면 소프트 재설정의 대상으로 사용할 것입니다.

시간을 거슬러 올라가기 전에 먼저 리포지토리의 현재 상태를 확인해 보겠습니다.

$ git status && git ls-files -s
On branch main
nothing to commit, working tree clean
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file

여기에서 git status 및 git ls-files- s를 혼합한 명령을 실행합니다. 그러면 리포지토리에 보류 중인 변경 사항이 있고 스테이징 색인의 reset_lifecycle_file67cc52710639e5da6b515416fd779d0741e3762e 버전이라는 것을 알 수 있습니다. 이것을 염두에 두고 첫 번째 커밋까지 소프트 재설정을 실행하겠습니다.

$git reset --soft 780411da3b47117270c0e3a8d5dcfd11d28d04a4
$ git status && git ls-files -s
On branch main
Changes to be committed:
    (use "git reset HEAD ..." to unstage)

modified: reset_lifecycle_file
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file

The code above executes a "soft reset" and also invokes the git status and git ls-files combo command, which outputs the state of the repository. We can examine the repo state output and note some interesting observations. First, git status indicates there are modifications to reset_lifecycle_file and highlights them indicating they are changes staged for the next commit. Second, the git ls-files output indicates that the Staging Index has not changed and retains the SHA 67cc52710639e5da6b515416fd779d0741e3762e we had earlier.

이러한 재설정에서 발생한 사항을 더욱 명확하게 이해하기 위해 git log를 살펴보겠습니다.

 $ git log commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4 Author: bitbucket  Date: Thu Nov 30 16:50:39 2017 -0800 initial commit

이제 로그 출력은 커밋 기록에 단일 커밋이 있다는 것을 보여줍니다. 이것은 --soft가 수행한 작업을 명확하게 설명하는 데 도움이 됩니다. 모든 git reset 호출과 마찬가지로 재설정이 수행하는 첫 번째 작업은 커밋 트리를 재설정하는 것입니다. --hard--mixed를 사용한 이전 예시에서는 모두HEAD에 대한 것이며 커밋 트리를 이전으로 이동시키지 않았습니다. 소프트 재설정 동안 일어나는 일은 이것이 전부입니다.

그러면 git status가 수정된 파일이 있다고 나타내는 이유가 헷갈릴 수도 있습니다. --soft는 스테이징 색인에 영향을 주지 않으므로 스테이징 색인에 대한 업데이트는 커밋 기록을 통해 과거로 거슬러 올라갑니다. 이것은 reset_lifecycle_file에 대한 SHA가 변경되지 않았음을 보여주는 git ls-files -s 출력으로 확인할 수 있습니다. 다시 말해, git status는 '3개의 트리'의 상태를 나타내지 않으며 그들 사이의 차이점을 보여줍니다. 이 경우 마치 이미 스테이징된 것처럼 스테이징 색인이 커밋 기록의 변경 사항보다 먼저 표시됩니다.

재설정 및 되돌리기

git revert가 변경 사항을 실행 취소하는 “안전한” 방법이라면 git reset은 위험한 방법이라고 생각할 수 있습니다. git reset의 경우 작업을 손실할 위험이 있습니다. git reset은 커밋을 절대 삭제하지 않지만 커밋이 '고립'될 수 있습니다. 즉, 참조에서 커밋에 액세스할 수 있는 직접적인 경로가 없음을 의미합니다. 이러한 고립된 커밋은 일반적으로 git reflog를 사용하여 찾고 복원할 수 있습니다. Git은 내부 가비지 컬렉터를 실행한 후 고립된 커밋을 영구적으로 삭제합니다. 기본적으로 Git은 30일마다 가비지 컬렉터를 실행하도록 구성되어 있습니다. 커밋 기록은 'Git 3개의 트리' 중 하나로, 나머지 2개인 스테이징 색인 및 작업 디렉터리는 커밋만큼 영구적이지 않습니다. 이 도구를 사용할 때는 주의해야 합니다. Git 명령 중에서 작업 내용을 잃을 가능성이 있는 유일한 Git 명령이기 때문입니다.

되돌리기는 공개 커밋을 안전하게 실행 취소하도록 설계된 반면, git reset은 스테이징 색인 및 작업 디렉터리에 대한 로컬 변경 사항을 실행 취소하도록 설계되었습니다. 목적이 분명히 다르므로 두 명령은 다르게 구현됩니다. 재설정은 변경 집합을 완전히 제거하는 반면 되돌리기는 원래 변경 집합을 유지하고 새 커밋을 사용하여 실행 취소를 적용합니다.

공개 기록 재설정하지 않기

이후의 스냅샷을 공개 리포지토리로 푸시한 경우에는 git reset 을 사용하면 안 됩니다. 커밋을 게시한 후에는 다른 개발자가 해당 커밋에 의존하고 있다고 가정해야 합니다.

다른 팀원이 계속 개발 중인 커밋을 제거하면 공동 작업에 심각한 문제를 야기할 수 있습니다. 다른 팀원이 리포지토리에 동기화하려고 하면 일련의 프로젝트 기록이 갑자기 사라진 것처럼 보입니다. 아래 시퀀스는 공개 커밋을 재설정하려고 할 때 발생하는 현상을 보여줍니다. origin/main 브랜치는 로컬 main 브랜치의 중앙 리포지토리 버전입니다.

origin/main이 마지막 노드를 가리키는 노드 세트 4개

재설정 후 새 커밋을 추가하자마자 Git은 로컬 기록이 origin/main에서 분기되었다고 생각할 것이며, 리포지토리를 동기화하는 데 필요한 병합 커밋은 팀을 혼란스럽고 답답하게 만들 수 있습니다.

요점은 게시된 변경 사항이 아닌 잘못된 로컬 실험에서 git reset 을 사용하고 있는지 확인하는 것입니다. 공개 커밋을 수정해야 하는 경우 git revert 명령이 이러한 목적을 위해 특별히 설계되었습니다.

예제

 git reset <file>

스테이징 영역에서 지정된 파일을 제거하지만 작업 디렉터리는 그대로 둡니다. 이렇게 하면 변경 사항을 덮어쓰지 않고 파일을 스테이징 취소합니다.

 git reset

스테이징 영역을 가장 최근 커밋과 일치하도록 재설정하지만 작업 디렉터리는 그대로 둡니다. 이렇게 하면 변경 사항을 덮어쓰지 않고도 모든 파일이 스테이징 취소되어 스테이징된 스냅샷을 처음부터 다시 구축할 수 있습니다.

 git reset --hard

스테이징 영역 및 작업 디렉터리를 가장 최근 커밋과 일치하도록 재설정합니다. --hard 플래그는 변경 사항을 스테이징 취소하는 것 외에도 Git에게 작업 디렉터리의 모든 변경 사항을 덮어쓰도록 지시합니다. 다른 말로 설명하자면 커밋되지 않은 모든 변경 사항이 삭제되므로 사용하기 전에 로컬에서 개발한 내용을 버리고 싶은지 확인해야 합니다.

 git reset  

현재 브랜치 팁을 뒤로 옮겨 commit하고 스테이징 영역을 그와 일치하도록 재설정하지만 작업 디렉터리는 그대로 둡니다. 이후의 모든 변경 사항은 작업 디렉터리에 있으므로 더 깔끔하고 원자적인 스냅샷을 사용하여 프로젝트 기록을 다시 커밋할 수 있습니다.

 git reset --hard  

현재 브랜치 팁을 뒤로 옮겨 하고 스테이징 영역과 작업 디렉터리를 모두 일치하도록 재설정합니다. 이렇게 하면 커밋되지 않은 변경 사항뿐만 아니라 이후의 모든 커밋도 삭제됩니다.

파일 스테이징 취소

스테이징된 스냅샷을 준비하는 동안 git reset 명령을 자주 접하게 됩니다. 다음 예시에서는 리포지토리에 이미 hello.pymain.py라는 파일 2개를 추가했다고 가정하겠습니다.

# Edit both hello.py and main.py

# Stage everything in the current directory
git add .

# Realize that the changes in hello.py and main.py
# should be committed in different snapshots

# Unstage main.py
git reset main.py

# Commit only hello.py
git commit -m "Make some changes to hello.py"

# Commit main.py in a separate snapshot
git add main.py
git commit -m "Edit main.py"

보시다시피 git reset은 다음 커밋과 관련이 없는 변경 사항을 스테이징 취소할 수 있도록 하여 커밋에 집중하도록 도와줍니다.

로컬 커밋 제거

다음 예시에서는 고급 사용 사례를 살펴봅니다. 한동안 새로운 실험을 진행했지만 몇 개의 스냅샷을 커밋한 후 완전히 버리기로 결정했을 때 어떤 일이 발생하는지 보여줍니다.

# Create a new file called `foo.py` and add some code to it

# Commit it to the project history
git add foo.py
git commit -m "Start developing a crazy feature"

# Edit `foo.py` again and change some other tracked files, too

# Commit another snapshot
git commit -a -m "Continue my crazy feature"

# Decide to scrap the feature and remove the associated commits
git reset --hard HEAD~2

git reset HEAD~2 명령은 현재 브랜치를 두 번의 커밋만큼 뒤로 이동하여 방금 만든 두 개의 스냅샷을 프로젝트 기록에서 효과적으로 제거합니다. 이러한 유형의 재설정은 게시되지 않은 커밋에만 사용해야 합니다. 이미 커밋을 공유 리포지토리로 푸시했다면 위의 작업을 수행하지 마세요.

요약

복습하자면 git reset은 Git 리포지토리 상태에 대한 로컬 변경 사항을 실행 취소하는 데 사용하는 강력한 명령입니다. git reset은 "Git 3개의 트리"에서 작동합니다. 이러한 트리는 커밋 기록(HEAD), 스테이징 색인 및 작업 디렉터리입니다. 3개의 트리에 해당하는 세 가지 명령줄 옵션이 있습니다. --soft, --mixed--hard 옵션을 git reset에 전달할 수 있습니다.

이 문서에서는 재설정 프로세스를 설명하는 데 도움이 되는 몇 가지 다른 Git 명령을 활용했습니다. git status, git log, git add, git checkout, git refloggit revert 명령에 대한 자세한 내용은 해당 명령의 개별 페이지를 참조하세요.

git reset을 배울 준비가 되었습니까?

이 대화형 자습서를 사용해 보세요.

지금 시작하기