Section 10: 변경사항 취소하기 및 시간 여행

김남훈·2024년 1월 20일

Git & Github

목록 보기
8/18

Critical

Checking Out Commits

git checkout d8194d6(축약된 해쉬 사용 가능(7자리)) 처럼 git checkout을 브랜치를 체크아웃하기 위해 사용하는 게 아니라, 특정 커밋 해시를 체크아웃하기 위해 사용할 수 있다. 그러나 이와 같은 명령을 실행하면 다음과 같은 에러가 나올 수 있다.

이 에러 메시지는 나중에 설명하기로 한다.


위의 그림은 git log를 해서 커밋 상황을 보여준다. 그 후에 git checkout 486f994를 통해서 커밋을 이동하면 아래의 그림과 같이 커밋이 줄어들어있는 것을 볼 수 있다.

왜냐하면 git checkout 486f994를 통해 travel back in time을 했기 때문이다. 그러나 마스터 브랜치에서 한 다른 작업은 모두 그대로 있다.

Detached HEAD란 무엇일까?
-> HEAD는 브랜치 레퍼런스를 가르키고 브랜치 레퍼런스는 브랜치의 가장 최근 커밋을 가리킨다. 아래와 같은 그림을 보면 이해할 수 있다.

즉 헤드는 커밋을 참조하지 않고, 브랜치 레퍼런스를 참조하고 있는 것이다. 이전 커밋을 체크아웃하면, 실제로 일어나는 일은 헤드가 커밋을 참조하도록 바꾸는 것이다. 따라서 아래의 그림과 같다. 말 그대로 HEAD가 브랜치 레퍼런스로부터 detached 된 상태라는 것이다.

커밋 해시를 이용하여 특정 커밋을 체크아웃할 수 있다는 것을 보았는데, 다른 방법도 존재한다. 깃은 HEAD를 기준으로 인덱싱하여 특정 커밋으로 HEAD를 움직일 수 있다.
HEAD~nHEAD n개 전 커밋을 나타낸다.

"Escaping" Detached HEAD

여기서는 Detached HEAD 상태일 때 할 수 있는 것에 대해서 알아보겠다.
1.단순히 예전 commit을 검토하고 파일을 보는 것.
2.이 Detached HEAD 상태에서 Escaping 하는 것 -> git checkout <branch-name> 또는 git switch -을 하는 것이다. git switch -를 하게 되면 가장 최근에 있던 브랜치 레퍼런스를 가리키게 된다.
3.특정 커밋으로 HEAD를 옮긴 뒤 거기서 새로운 브랜치를 만든다.

Important

Discarding Changes With Checkout

체크아웃 명령의 또 다른 용도는 변경 사항을 취소하는 것이다. 즉 이전에 커밋했던 때로 돌아가는 것이다. 만약에 아래의 그림처럼 파일에 이상한 내용을 쓰고 저장(커밋 x)을 했다면, 수동으로 지울 수도 있지만 git checkout HEAD dog.txt 명령어를 사용할 수도 있다. 이 명령어는 dog.txt 파일을 HEAD가 가리키는 방향으로 checkout하라는 것이다. 또는 git checkout -- <file> 명령어를 사용할 수도 있다.

Git Restore

  1. git restore <file-name> -> 마지막 커밋 이후의 변경사항을 삭제한다. git checkout -- <file-name>과 같은 명령어이다.

  2. git restore --source HEAD~1 app.js -> 마지막 커밋(HEAD가 가리키고 있는 곳)의 하나 전 커밋 상태로 app.js를 되돌린다.

  3. git restore --staged <file-name> -> staged 된 파일을 staged 되기 전으로 되돌린다.

Git Reset

1.git reset <commit-hash>(plain reset) -> 저장소를 특정 커밋으로 재설정한다. 이렇게 plain-reset을 하면 커밋만 제거되고 변경 사항은 여전히 워킹 디렉토리에 있다. 아래의 그림을 보면 이해가 잘 된다.

만약에 badstuff 라는 브랜치에 commit을 했어야 하는데, master 브랜치에 commit을 한 경우, 이와 같은 명령어를 쓰면 효율적이다.

2.git reset --hard <commit> -> 깃은 이 커밋 해시까지의 커밋을 삭제하고 워킹 디렉토리의 변경사항을 그 커밋 상태로 만든다.

git reset은 브랜치 기준으로 작동한다.

Git Revert

git revert <commit>git reset과 유사하지만, 매우 중요한 차이가 있다. 둘다 커밋을 취소하는 것이지만, 그 방식이 다르다. reset은 커밋을 완전히 제거하고 브랜치 포인터를 뒤로 이동시킨다. 그러나 revert는 대신에 새로운 커밋을 만들고 그 새로운 커밋에서 이전 커밋의 변경 사항을 취소한다.

이 때 병합 충돌과 마찬가지로 revert를 하는 경우에 충돌이 날 수 있다.다음과 같은 시나리오를 생각해보자.
1. 브랜치에서 커밋 A 이후에 다른 작업이 추가되었다.
2. git revert A 명령을 사용하여 커밋 A를 취소하려고 한다.
3. git은 커밋 A 이후에 다른 변경 사항(커밋 A에 기반한 것)이 있었기 때문에, 다른 변경 사항에 대해 어떻게 처리할지 묻는다.

왜 Reset 대신 Revert를 사용할까?
->아직 다루지 않았지만, 협업 때문이다. 다른 사람들과 팀을 이루어 작업한다면, 모두 다른 컴퓨터를 사용한다. 다른 사람들이 커밋의 복사본을 가지고 있는 경우, 즉 다른 모든 사람들이 Bad 커밋을 가지고 있는데, 내가 reset으로 그 커밋을 제거한다면, 다른 사람들이 가지고 있는 기록(history)을 취소하는 것이기 때문에 심각한 문제가 될 수 있다. 따라서 보통은 revert를 사용한다. 만약에 내가 혼자 실수를 하고 다른 사람과 공유를 하지 않은 상태이면 reset을 사용하는 것이 좋지만 , 다른 사람과 공유를 한 상태에서는 revert를 사용하는 것이 좋다.

Nice To Have

profile
안녕하세요

0개의 댓글