ProGit 정리 - Reset 제대로 알고 가기

3 분 소요

Git reset에 대해 이해하기 위해서는 아래에서 설명하는 세 개의 트리에 대한 이해가 필요하다.

세 개의 트리

Git을 서로 다른 세 ‘파일의 묶음’을 관리하는 컨텐츠 관리자로 생각하면 이해하기 쉽다.

트리 역할
HEAD 마지막 커밋 스냅샷, 다음 커밋의 부모 커밋
Index 다음에 커밋할 스냅샷
워킹 디렉토리 샌드박스

HEAD는 현재 브랜치를 가리키고 있는 포인터이다. 지금 HEAD가 가리키고 있는 커밋은 다음 커밋의 부모 커밋이 된다. Progit에는 HEAD는 *현재 브랜치 마지막 커밋의 스냅샷* 이다. 라고 나와 있다.

cat-file

Index

Index는 다음에 커밋할 스냅샷이다. 이런 개념을 Staging Area 라고 배운 바 있다. Staging Area는 사용자가 git commit 명령을 실행했을 때 Git이 처리할 것들이 있는 곳이다.

먼저 Index는 워킹 디렉토리에서 마지막으로 Checkout 한 브랜치의 파일 목록과 파일 내용으로 채워진다. 이후 파일 변경작업을 하고 변경한 내용으로 Index를 업데이트 할 수 있다. 이렇게 업데이트 하고 git commit 명령을 실행하면 Index는 새 커밋으로 변환된다.

ls-files 명령으로 현재 Index가 어떤 상태인지 확인 할 수 있다.

ls-files

Working Directory

위의 두 트리는 .git 디렉토리에 저장한다. 워킹 디렉토리는 실제로 파일로 존재한다. 워킹 디렉토리는 샌드박스로 생각하자. 커밋하기 전에는 Index(Staging Area)에 올려놓고 얼마든지 변경할 수 있다.

tree

Workflow

workflow

Progit에 아주 잘 나와있다. progit에 나와있는 설명 과정을 그대로 로컬에서 실행 해보면 아래와 같은 흐름이다.

일단 테스트 디렉토리에 새로운 파일을 만든다. 그리고 해당 디렉토리에서 git init한다.

#!bin/bash
$ mkdir test
$ echo testfile > test.txt
$ git init

여기까지 하면 아래와 같은 상태가 된다.

before_add

init

이후 git add test.txt로 Index에 파일을 추가 해준다. 그러면 아래와 같이 변한다.

after_add2

after_add

이 상태에서 git commit 명령어로 커밋해주면 HEAD 영역에도 동일한 파일이 추가 된다. 이 상태에서 git status를 쳐보면 Index와 HEAD가 동일한 상태이기 때문에 아무것도 없다고 나온다.

after_commit2

after_commit

git status 명령은 Index와 현재 HEAD의 상태를 비교해서 나타낸다.

브랜치를 바꾸거나 Clone 명령도 내부에서는 비슷한 절차를 밟는다. 브랜치를 Checkout 하면, HEAD가 새로운 브랜치를 가리키도록 바뀌고, 새로운 커밋의 스냅샷을 Index에 놓는다. 그리고 Index의 내용을 워킹 디렉토리로 복사한다.

Reset의 역할

여기서도 Progit에서 보여주는 예시를 로컬에서 그대로 해보면 아래와 같다.

일단 아까 만들었던 test.txt 파일을 변경하고 커밋하는 과정을 반복 해서 아래와 같은 상태를 만든다.

log

second_commit

이 상태에서 Reset의 동작을 순서대로 알아보자.

1단계. HEAD 이동

위와 같은 상태를 그림으로 나타내면 아래와 같은 상태이다.

before_reset

그리고 이 상태에서 git reset --soft HEAD~ 명령을 실행하면 아래와 같이 변한다.

after_reset_soft

reset_soft_log

reset --soft 명령은 딱 여기까지만 수행한다. HEAD의 상태만 변경 시킨다.

위에서 설명한 트리의 형태로 나타내면 아래와 같다.

soft_tree

그러면 HEAD와 Index와의 차이가 있기 때문에 git status명령을 보면 아래와 같이 modified에 파일이 나타난다.

reset_soft

2단계. Index 업데이트

git reset [--mixed] HEAD~의 명령을 치면, Index까지 업데이트가 일어난다.

그러니까 그림으로 봤을 때는 아래와 같다.

after_reset_soft

mixed_tree

reset명령의 default 옵션이 mixed 이기 때문에 git reset HEAD~명령으로 다음과 같이 할 수 있다.

after_reset_mixed

여기서 보면 빨간색으로 Index에도 변경 내용이 git add 되지 않은 상태인 것을 알 수 있다. 보면 git commit 명령과 git add 명령을 되돌리는 것이라고 생각하면 된다.

3단계. Working Directory 업데이트

git reset --hard HEAD~의 명령을 치면, 워킹 디렉토리에 있는 상태까지 업데이트가 된다.

그러면 트리의 상태는 아래처럼 변한다.

hard_tree

워킹 디렉토리의 상태가 변했기 때문에, 실제 우리가 보는 파일의 내용도 바뀌게 된다.

reset을 사용할 때 hard 옵션을 사용할 때는 정말 신중하게 사용해야 한다. 워킹 디렉토리의 내용을 업데이트 하기 때문에 실제로 우리가 보는 파일이 변경 되기 때문에 신중하게 사용해야 한다.

명령을 수행한지 얼마 되지 않았으면, git reflog로 HEAD의 이동 상태를 추적해서 다시 상태를 원상 복구 시킬 수도 있다.

reflog

after_reflog

지금 modified: test.txt 파일을 열어보면 second commit 까지의 상태만 반영 되어 있다. 그렇지만 working directory에는 third commit의 상태가 있기 때문에, working directory의 상태로 원상 복구 시키면 파일을 reset hard 전으로 되돌릴 수 있다.

위 상태에서 변경 사항을 취소하고 싶다면 git reset --hard명령으로 모든 변경사항을 버리고 HEAD로 되돌아갈 수 있다.

경로, 파일명을 주고 reset 하기

git reset test.txt 명령을 풀어서 쓰면, git reset --mixed HEAD test.txt 이다. 이 명령은 git add test.txt와 완전히 정반대의 역할을 수행한다.

git add test.txt 명령이 index 트리에 test.txt의 커밋을 올려 놓는 것이라면, git reset test.txt 명령은 index 트리에서 test.txt의 커밋을 뺀다. 한마디로 staging area에서 변경 내용을 뺀다.

특정 커밋을 명시하면 Git은 HEAD에서 파일을 가져오는 것이 아니라 그 커밋에서 파일을 가져온다. git reset 4483fb5 file.txt 명령과 같이 실행한다.

4483fb5은 initial commit 이기 때문에 file.txt의 index 상태는 아래와 같이 변하게 된다.

after_commit_reset

요약

Progit에 어떤 명령이 어떤 트리에 영향을 주는지 나와있는 표를 그대로 아래 옮겨놨다.

명령이 HEAD가 가리키는 브랜치를 움직인다면 HEAD 열에 REF 라고 적혀 있고 HEAD 자체가 움직인다면 HEAD 라고 적혀 있다. WD Safe는 워킹 디렉토리의 상태를 나타내기 때문에, No라고 적혀있다면 해당 명령을 실행하기 전에 한 번쯤 더 생각해보아야 한다.

summary

참고자료

태그:

카테고리:

업데이트:

댓글남기기