이력 되돌리기 ( RESET)
지금까지 우리는 커밋을 통해서 파일의 버전들을 관리하는 방법들을 GIT을 이용해 실습을 진행하였다. 이렇게 만들어진 커밋들은 순차적으로 트리형식으로 저장되어 각 커밋은 이전 커밋으로 돌아갈 수 있는 "스냅샷"을 가지고 있다.
이제는 이렇게 관리되어진 commit들을 통해 이전의 commit으로 되돌리는 RESET에 대해서 학습하자
협업 프로젝트나 개인 프로젝트에서 어떤 수정본을 작업하다가 보면 수정을 하는 방향의 접근이 잘못되어 이전의 버전으로 commit을 되돌리고 새롭게 작업을 시작할 일이 자주 생긴다. 이 때 우리는 commit msg들을 통해서 commit id나 file name을 통해서 제일 최근의 commit이나 특정한 commit으로 복귀할 수 있다. GIT은 이런경우를 위해 RESET 명령어를 지원한다.
먼저 나의 저장소에 f1.txt파일에 대해서 ver1 ~ ver3까지의 commit들을 만들어 두었다. 이를 git log를 통해서 확인하면 나의 master branch에 버전이 3개 존재하는 것을 확인 할 수 있다.
현재 f1.txt파일은 ver3으로 a b c 가 존재하고 ver2는 a b , ver3은 a 라는 텍스트 내용을 갖고 있다.
log로 출력된 내용에서 ver1 id를 복사한 뒤에 git reset 뒤에 붙여넣기하자. 그러면 commit id를 통해서 git은 내부적으로 HEAD 파일이 가리키는 commit을 ver3 commit ID 에서 ver1 commit ID로 단순히 변경하게 된다.
즉 이전버젼으로 돌아간다 하더라도 ver 2,3는 지워지지 않고 게속해서 저장하고 있다는 점이다. 물론 git 자체적으로 쓸모가 없다고 한다면 GC를 통해서 제거된다.
이렇게 변경된 버전을 확인하기 위해 cat을 통해 f1.txt파일을 살펴보면 a가 표시되고 git log를 통해 log 기록을 살펴보면 ver1에 대한 내용만 존재하는 것을 알 수 있다.
RESET을 취소하기
- git reflog
우리가 RESET한 ver1의 내용이 아닌 ver2로 복구해야 했는데 이미 ver1으로 RESET한 상황이라면 어떻게 다시 수정할 수 있을까
먼저 우리가 commit을 하고 commit을 reset하는 모든 행위는 결국 GIT의 HEAD파일이 어떤 commit id를 가리키는지 수정하는 일련의 행위이다. 이렇게 HEAD가 수정된 이력을 보는 방법이 있는데 이는 git reflog이다.
reflog도 log와 같이 STACK의 형식으로 저장된다. 맨 밑의 내용(HEAD@{9})은 ver1로 맨 처음 commit한 내용이다.
그 뒤로 ver2 , ver3가 오고 그 위로는 HEAD파일이 reset을 통해서 수정된 내용을 확인할 수 있다. HEAD 6-3까지는 내 실수로 무시해도 좋다. 여기서 볼 부분은 HEAD[2]를 보면 reset명령어를 통해 ver1의 commit ID값을 입력해서 ver1으로 HEAD파일이 변경되었고 그 위에 내용은 뒤에서 다시 알아보도록하자
위에서 보듯이 HEAD의 수정내용이 reflog를 통해 저장되어 있다. 물론 HEAD파일을 다시 이러한 수정 상황 즉 HEAD의 [n] 상태로 되돌릴 수 있다는 점이다. 실습을 위해 셋팅한 상황 ver1 ~ ver3까지 마련한뒤 ver3의 상태였던 HEAD@[7] 상태로 복귀해보자
cat f1.txt를 통해서 ver3의 상황으로 다시 되돌아 온 것을 확인 할 수 있다. git log를 통해서도 각 ver이 존재하는 것을 확인할 수 있다.
- ORIG_HEAD
게속해서 말하지만 commit들의 수정사항을 단지 git의 파일 중 HEAD라는 파일의 내용이 어떤 commit ID를 가리키는지 단순히 방향만 바꿔주는 역할이다.
이 떄 reset이나 다른 위험한 작업 등 즉, commit을 어떤 특정한 방향으로 수정하는 작업을 진행하면 git은 자체적으로 ORIG_HEAD라는 파일을 만들고 그 파일에 위험한 작업을 진행하기 전 현재 HEAD가 가리키는 commit을 ORIG_HEAD로 복사한다.
이러한 ORIG_HEAD를 이용해서 우리는 reset를 한 작업에서 한 단계 이전 상황으로 , 즉 되돌리기를 수행할 수 있다.
아래 예제 실습을 살펴보자
reset을 통해서 ver1의 내용으로 되돌려서 f1.txt파일을 확인해보면 a만 저장하고 있는 f1.txt를 확인할 수 있다. ver1으로 성공적으로 현재 commit 위치를 바꾼 것이다. 이때 reset을 실행하기 전, 즉 ver3의 상태로 돌아가는 방법은 위의 방법과 같이 HEAD의 수정사항 중에 번호를 확인하거나 commit ID를 알아내어 돌아갈 수 있지만 우리는 ORIG_HEAD의 존재를 알고있다.
ORIG_HEAD는 위험한 작업 이전의 commit 내용을 저장하고 있으므로 , git reset --hard ORIG_HEAD를 통해서 ver3으로 복귀해서 f1.txt내용을 확인하면 버전3의 택스트 내용을 성공적으로 담고있는 것을 확인할 수 있다.
--HARD --MIXED --SOFT
GIT은 파일들의 상태를 처리하기 위해서 크게 3가지 분류 공간을 사용한다. 이전 포스팅에서 언급한 내용은 Working directory / staging area / repository 이다.
하지만 이는 하나로 통일되기 보다는 각 분류 공간이 다양하게 언급되어 진다. 이는 아래의 표에서 살펴보도록하자
reset명령어에서 우리는 --hard 플래그를 통해서 실습을 진행해왔다. 이 때 reset의 플래그에서는 총 3가지 hard, mixed, soft가 존재한다. 이는 reset을 진행할 때 어느 공간을 포함해서 되돌릴 것인지 뜻하는 것이다.
hard는 전체적인 상황을 되돌리면서 다소 위험하지만 확실하게 버전을 되돌리는 방식이고 mixed는 working directory를 제외한 공간 , soft는 repository의 내용만 수정한다.
즉 우리가 빈 git 디렉터리에서 f1.txt 를 만들고 'repository' 라고 만들고 add f1.txt [ staging area의 f1.txt 파일 수정 ] 를 한 뒤,
commit -am f1.txt "ver1" [ repository의 f1.txt 파일 수정 ]을 진행하면 각 공간에 f1.txt는 "repository"라는 택스트 내용을 담겨서 저장되어 있다.
이 때 f1.txt를 'staging area'라고 수정하고 add f1.txt를 하면 공간에서 f1.txt파일에 담겨있는 택스트 내용은 무엇일까 이는 아래와 같다.
woring directory | staging area | repository |
"staging area" | "staging area" | "repository" |
아직 commit 이전으로 add만 진행한 상황이기 때문에 각 공간에서 가리키는 f1.txt파일은 위와 같은 내용을 담고있게 된다.
이번엔 add , commit 둘 다 하지않고 단순히 f1.txt파일의 내용을 "working dir"로 교체하자 그러면 각 공간에서 f1.txt는 아래와 같다.
working directory | staging area | repository |
"working dir" | "staging area" | "repository" |
HEAD => ver1 가리키고 있다.
이러한 상황일 때 각 플래그에서 reset을 진행하면 어떻게 될까? 먼저 ver1 이전에는 어떠한 내용도 담고있지 않기 때문에 f1.txt가 존재하지 않는 상황이다. 따라서 만약에 버전을 돌리면 각 공간에 "init"이라는 초기 생성자가 들어가게 된다.
- git reset --hard f1.txt
모든 공간에서의 reset이 진행된다. 띠리사 각 공간에서 init 들로 f1.txt가 존재하게 된다 이는 f1.txt가 존재하지 않는 상황으로 돌아감을 의미한다.
- git reset --mixed f1.txt [ reset의 default 값 ]
working directory를 제외한 공간에서 reset이 진행된다.
working directory에서는 f1.txt가 "working directory"리는 텍스트 내용을 담고있는 채로 존재하지만 add. commit 상태인 staging과 repository에는 f1.txt가 존재하지 않는 것으로 표시된다.
- git reset --soft f1.txt
repository만 reset이 진행된다 즉 head가 commit을 가리키는 연결고리만 수정하는 것이다.
working directory에서는 f1.txt가 "working directory"리는 텍스트 내용이 있고 staging aread의 f1.txt는 "staging area"를 담고있다. repository에는 f1.txt가 존재하지 않는 것으로 표시된다. 즉 commit 이력이 없는 것으로 판단된다. 이때 commit을 진행하게 되면 당연히 staging area가 담고있는 "staging area' f1.txt가 commit되어 저장되게 된다.
+
git checkout [commit id] 를 하게되면 그 commit의 상태로 돌아가게 된다. check out은 branch를 가르켜 분기로 돌아가는게 일반적이지만 이렇게 사용하게 되면 head가 특정 commit을 가리키기 때문에 특정 시점에서의 파일내용들이 어떻게 존재하는지 확인할 수 있다.
git diff : repository가 아닌 Working Directory의 내용과 Staging area의 내용을 비교하여 출력한다
'Infra > GIT&GIT HUB' 카테고리의 다른 글
Git (9) : 병합 충돌 & n way-merge (0) | 2021.07.20 |
---|---|
Git (8) : stash (임시저장) (0) | 2021.07.18 |
Git (7) : branch & merge (0) | 2021.07.17 |
Git (6) : branch (0) | 2021.07.12 |
Git (5) : Reset , Revert 기초 (0) | 2021.07.09 |