git checkout
은 branch를 이동하기 위해서 많이 사용되었는데, 이 밖에도 너무 많은 기능들이 git checkout
에 있기 때문에 git checkout
의 기능을 분리하려는 시도들이 많이 생겼다.
git switch
와 git restore
가 바로 git checkout
의 다양한 기능 중 일부를 분리한 명령어들이다.
먼저 git init
부터 시작해보도록 하자.
git init
echo "hello world!" > ./novel.txt
git add ./novel.txt
git commit -m "add novel.txt"
다음으로 novel.txt
를 수정해보도록 하자.
hello world!
good bye world!
그리고 commit을 해주도록 하자.
git add ./novel.txt
git commit -m "update novel.txt"
한번 더 novel.txt
를 수정해주고 commit을 시켜주도록 하자.
hello world!
good bye world!
New world is upto you!
git add ./novel.txt
git commit -m "Append line into novel.txt"
이제 git log를 통해서 repository의 상태를 보도록 하자.
git log
commit 3a20f305ae38434cd7650b4cf585aaa5dfef8b15 (HEAD -> master)
Author: calo <calo@calo.com>
Date: Fri Aug 2 17:02:08 2024 +0900
Append line into novel.txt
commit 4699d0ea403d5185eb30d783c1f87487fe5a9068
Author: calo <calo@calo.com>
Date: Fri Aug 2 16:54:28 2024 +0900
update novel.txt
commit 4272f53fa8f53846dc07e6cc1287b1947031ea45
Author: calo <calo@calo.com>
Date: Fri Aug 2 16:54:10 2024 +0900
add novel.txt
3개의 commit이 생겼다. 이제 git checkout <commit>
을 실행시켜보도록 하자. 우리의 경우 두 번째 commit인 4699d0ea403d5185eb30d783c1f87487fe5a9068
을 checkout시키도록 하자.
git checkout 4699d0ea403d5185eb30d783c1f87487fe5a9068
HEAD is now at 4699d0e update novel.txt
HEAD
가 변경되었다는 말이 나온다.
git log
로 확인해보도록 하자.
commit 4699d0ea403d5185eb30d783c1f87487fe5a9068 (HEAD)
Author: calo <calo@calo.com>
Date: Fri Aug 2 16:54:28 2024 +0900
update novel.txt
commit 4272f53fa8f53846dc07e6cc1287b1947031ea45
Author: calo <calo@calo.com>
Date: Fri Aug 2 16:54:10 2024 +0900
add novel.txt
이전에 있었던 첫번째 commit은 없어졌고, 두번째 commit이 첫번째 commit이 되었다. 그리고 HEAD
가 두번째 commiot으로 옮겨갔다. 이렇게 checkout
을 특정 commit에 사용하면 HEAD
가 detached되는 상태가 발생한다.
cat ./novel.txt
hello world!
good bye world!
novel
역시도 해당 commit의 내용을 반영하는 것으로 볼 수 있다.
실행 과정을 그려보면 다음과 같다.
master(HEAD)
|
--commit1-- --commit2-- --commit3--
|4272f5...| --> |4699d0...| --> |3a20f3...|
----------- ----------- -----------
다음과 같이 checkout
전에는 master
branch의 HEAD
가 master branch의 reference로 사용되고 있었다. 즉, HEAD
가 master
branch의 가장 최근 commit이라는 것이다.
그런데, git checkout 4699d0...
이후에는 다음과 같이 동작한다.
HEAD master
| |
--commit1-- --commit2-- --commit3--
|4272f5...| --> |4699d0...| --> |3a20f3...|
----------- ----------- -----------
이제 HEAD
는 master branch의 reference가 아니다. 즉, 분리된 것이다. 따라서 이를 'detached HEAD'라고 하는 것이다.
git status
로 현재 상황을 보면 다음과 같다.
git status
HEAD detached at 4699d0e
...
다음과 같이 HEAD detached
라는 글자가 나오게 된다.
그럼 왜 'detached HEAD'를 사용하는 것일까?? 그리고 이것으로 무엇을 할 수 있는 것일까??
먼저 'detached HEAD'에서 다시 원래대로 복귀하는 방법에 대해서 알아보자. 상당히 간단한데, 원래있었던 branch로 checkout
하면 된다. 우리의 경우는 master
branch였으므로 다음과 같다.
git checkout master
재대로 반영되었는 지 확인하기 위해서 git log
를 살펴보자.
git log
commit 3a20f305ae38434cd7650b4cf585aaa5dfef8b15 (HEAD -> master)
Author: calo <calo@calo.com>
Date: Fri Aug 2 17:02:08 2024 +0900
Append line into novel.txt
commit 4699d0ea403d5185eb30d783c1f87487fe5a9068
Author: calo <calo@calo.com>
Date: Fri Aug 2 16:54:28 2024 +0900
update novel.txt
commit 4272f53fa8f53846dc07e6cc1287b1947031ea45
Author: calo <calo@calo.com>
Date: Fri Aug 2 16:54:10 2024 +0900
add novel.txt
다시 돌아온 것을 볼 수 있다. 이렇게 checkout을 사용하여 특정 commit으로 뒤돌아가거나, 다시 branch의 최신 commit으로 되돌아 오거나를 반복할 수 있는 것이다.
그럼 왜 'detached HEAD'를 사용하는 것일까?? 생각해보면 간단하다. checkout
을 사용하면 HEAD
만 이전 commit으로 옮기고 현재의 branch의 commit들을 손상없이 뒤로 갈 수 있다. 그렇기 때문에 이전 commit 정보를 보는 것도 매우 쉬울 뿐더라, 현재 branch에 영향도 없다.
더불어, 특정 commit 이후부터 새로운 branch를 만들어 다른 code를 넣고 싶다고 하자. 가령 위의 경우 두번째 commit 이후로 새로운 branch를 만들어 다른 code들을 반영하고 싶을 수 있다. 이 경우에도 checkout
은 매우 유용하다.
우리의 경우 두 번째 commit으로 돌아가서 novel.txt
에 새로운 문자를 넣도록 하자.
git log --oneline
3a20f30 (HEAD -> master) Append line into novel.txt
4699d0e update novel.txt
4272f53 add novel.txt
두번째 commit의 해시가 4699d0e
이다. checkout해보도록 하자.
git checkout 4699d0e
git status
HEAD detached at 4699d0e
...
이제 새 branch를 만들도록 하자.
git switch -c redo
git log --oneline
4699d0e (HEAD -> redo) update novel.txt
4272f53 add novel.txt
redo
branch의 HEAD
가 master
branch의 두 번째 commit임을 확인할 수 있다. 이제 novel.txt
를 수정해주도록 하자.
hello world!
good bye world!
redo!
수정이 완료되었다면 commit해주도록 하자.
git add ./novel.txt
git commit -m "Append 'redo' in novel.txt"
git log
commit 2b15ba7135bf45b96a104a089123411c9b62fe8d (HEAD -> redo)
Author: calo <calo@calo.com>
Date: Fri Aug 2 17:57:23 2024 +0900
Append 'redo' in novel.txt
commit 4699d0ea403d5185eb30d783c1f87487fe5a9068
Author: calo <calo@calo.com>
Date: Fri Aug 2 16:54:28 2024 +0900
update novel.txt
commit 4272f53fa8f53846dc07e6cc1287b1947031ea45
Author: calo <calo@calo.com>
Date: Fri Aug 2 16:54:10 2024 +0900
add novel.txt
이제 재밌게도 master
의 이전 commit을 바탕으로 새로운 branch를 만들어 새로운 commit을 추가하게 된 것이다. 이것이 detached HEAD
를 사용하는 이유이다.
다시 master
branch로 돌아오면 novel.txt
가 원상 복구되는 것을 볼 수 있다.
git switch master
cat ./novel.txt
hello world!
good bye world!
New world is upto you!
원상태로 복구된 것을 볼 수 있다.
HEAD
를 기준으로 commit을 참조할 수 있다. HEAD~1
는 HEAD
에서 1개 전을 의미한다.
HEAD~2 HEAD~1 master(HEAD)
| | |
--commit1-- --commit2-- --commit3--
|4272f5...| --> |4699d0...| --> |3a20f3...|
----------- ----------- -----------
다음과 같이 쓰일 수 있다는 것이다.
git log --oneline
3a20f30 (HEAD -> master) Append line into novel.txt
4699d0e update novel.txt
4272f53 add novel.txt
현재 3a20f30
가 HEAD
인 것을 알 수 있다. checkout을 통해서 첫번째 commit인 4272f53
으로 넘어가도록 하자.
git checkout HEAD~2
HEAD is now at 4272f53 add novel.txt
git log --oneline
4272f53 (HEAD) add novel.txt
첫번째 commit으로 성공적으로 가게 된다.
다시 돌아가고 싶다면 git switch master
와 같이 branch로 돌아가는 것인데, 바로 이전으로 돌아가고 싶다면 git switch -
도 가능하다.
git switch -
Previous HEAD position was 4272f53 add novel.txt
Switched to branch 'master'
git log --oneline
3a20f30 (HEAD -> master) Append line into novel.txt
4699d0e update novel.txt
4272f53 add novel.txt
이전에 checkout
을 호출할 때 있었던 commit으로 돌아간 것을 볼 수 있다.
git checkout HEAD <file>
을 사용하면, local에 있는 변동사항을 적용하지 않고 HEAD
로 돌아가는 명령어이다. 여기에 <file>
을 함께 입력하면 <file>
에 대한 local 변동 사항이 사라지고 HEAD
에 있는 file로 덮어쓰게 된다.
즉, checkout
을 통해서 local 변동사항을 삭제하는 것이다.
cat.txt
라는 file을 만들어보도록 하자.
echo -n 'cat' >> cat.txt
이제 cat.txt
를 commit시켜보도록 하자.
git add ./cat.txt
git commit -m "Add cat.txt"
이제 novel.txt
와 cat.txt
에 다음의 code를 넣어보도록 하자.
hello world!
good bye world!
New world is upto you!
Dear my friends
cat
eqmkwemkl
meqwmelwmelkqmw
,elqwmel;wqm
novel.txt
와 cat.txt
두 파일이 수정되었는 데, cat.txt
만 이전 HEAD
가 가리키는 commit으로 되돌리고 싶다. 이때 사용하는 것이 바로 git checkout HEAD <file>
이다.
git checkout HEAD ./cat.txt
cat.txt
를 확인하면 다음과 같다.
cat
반면에 novel.txt
를 확인하면 다음과 같다.
hello world!
good bye world!
New world is upto you!
Dear my friends
novel.txt
의 local 변동사항은 이전 commit으로 덮어쓰여지지 않은 것을 볼 수 있다.
여기서 만약 git checkout HEAD
를 실행하면 HEAD
가 가리키는 최신 commit으로 되돌아가기 때문에, 모든 local 변동 사항이 삭제되게된다.
git checkout -- <file>
도 같은 기능을 한다. 즉 HEAD를 기준으로 local 변동 사항을 폐기할 수 있다는 것이다. 더 헷갈리는 것은 restore 명령어도 동일한 동작을 한다는 것이다.