새로 나온 명령어로 git checkout
의 기능 일부인, commit하지 않은 local변경 사항에 대해서 취소 기능을 제공해준다.
git restore <file-name>
git restore
를 사용해보기 위해서, 다음과 같이 설정해주도록 하자.
git init
echo "first song" >> ./song.txt
git add ./song.txt
git commit -m "add first commit in song.txt"
song.txt
파일을 만들고, song.txt
를 commit한 것이다.
다음으로 두번째 commit을 만들도록 하자.
echo "second song" >> ./song.txt
git add ./song.txt
git commit -m "add second commit in song.txt"
다음으로 세번째 commit을 만들도록 하자.
echo "third song" >> ./song.txt
git add ./song.txt
git commit -m "add third commit in song.txt"
이제 git log
로 commit 내용을 확인해보도록 하자.
git log
commit ad1531e566f77f38812731a6a5eac3b403cec880 (HEAD -> master)
Author: calo <calo@calo.com>
Date: Mon Aug 5 13:30:30 2024 +0900
add third commit in song.txt
commit 0176bfa4b7089553b299bb6e696e13569d58388b
Author: calo <calo@calo.com>
Date: Mon Aug 5 13:29:57 2024 +0900
add second commit in song.txt
commit 120d88979423f741c4ae57f6857bb7f3eb27a3db
Author: calo <calo@calo.com>
Date: Mon Aug 5 13:28:49 2024 +0900
add first commit in song.txt
이제 song.txt
파일을 수정해보도록 하자.
echo "nononononi" > ./song.txt
git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: song.txt
...
song.txt
가 변했다는 것을 알 수 있다. 그런데 이는 우리가 원했던 변화가 아니므로 song.txt
를 이전 commit으로 복구시키도록 하자.
git restore song.txt
다음으로 확인해보도록 하자.
cat ./song.txt
first song
second song
third song
local 변화가 사라지고 이전 commit 내용들이 다시 복구된 것을 확인할 수 있다.
사실 위 명령어는 HEAD
가 생략된 것과 마찬가지이다.
git restore --source HEAD ./song.txt
이렇게 된 것이다. 따라서, --source
부분에 HEAD
가 아니라 특정 commit을 넣어도 된다. 만약 HEAD
보다 바로 이전의 commit으로 song.txt
를 복구시킬 수도 있다.
먼저 song.txt
local file을 수정해주로 하자.
echo "nononononi" > ./song.txt
cat ./song.txt
nononononi
다음으로 HEAD
보다 하나 떨어진 HEAD~1
을 기준으로 다시 복구하도록 하자.
git restore --source HEAD~1 ./song.txt
cat ./song.txt
first song
second song
두번째 commit으로 돌아간 것을 볼 수 있다.
단 git restore
를 사용할 때, 한 가지 조심해야할 것은 복구가 불가능하다는 것이다.
다시 원상 복구하도록 하자.
git restore ./song.txt
git restore
를 통해서 staging 변경 사항도 취소할 수 있다.
git restore --staged <file-name>
테스트를 해보기 위해서 secret.txt
라는 file을 만들어보도록 하자.
echo "API_KEY=e12knmciejo123kopr=we" >> ./secret.txt
다음으로 song.txt
도 다음을 추가하도록 하자.
echo "fourth song" >> ./song.txt
실수로 git add .
를 해버렸다고 하자. git status
를 확인하면 다음과 같이 나올 것이다.
git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: secret.txt
modified: song.txt
...
secret.txt
은 staging되어서는 안되는데, staging area에 들어와버렸다.
secret.txt
를 staging에서 제외하기위해서 다음과 같이 git restore
를 사용하도록 하자.
git restore --staged ./secret.txt
이제 secret.txt
가 stage영역에서 제외된 것을 볼 수 있다. 다음으로 git status
로 stage영역을 확인해보도록 하자.
git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: song.txt
...
다시 secret.txt
를 확인하면 내용이 전혀 사라지지 않고 그대로 남아있는 것을 알 수 있다.
cat ./secret.txt
API_KEY=e12knmciejo123kopr=we
git restore
과 git restore --staged
가 조금 헷갈릴 수 있는데, 하나는 local 변동 사항을 제거하는 것이고, 하나는 stage
에 있는 file을 stage
영역에서 제외하는 기능이라고 보면 된다.
이제 secret.txt
를 제외한 song.txt
에 대해서 commit을 진행해보도록 하자.
git add ./song.txt
git commit -m "add song.txt"
git reset
은 commit된 내용, 즉, repository를 특정 commit으로 다시 재설정한다.
git reset <commit-hash>
단, git reset
은 --hard
옵션을 사용 여부에 따라 동작이 다른데, 먼저 --hard
옵션을 사용하지 않은 경우를 보도록 하자.
일반적으로 git reset
을 사용할 때는 잘못된 commit들이 있을 때이다. 따라서, 먼저 잘못된 commit들을 만들어보도록 하자.
echo "mistake commit1" >> ./song.txt
git add ./song.txt
git commit -m "add mistake commit1"
echo "mistake commit2" >> ./song.txt
git add ./song.txt
git commit -m "add mistake commit2"
cat ./song.txt
first song
second song
third song
fourth song
mistake commit1
mistake commit2
이제 git log
를 확인해보도록 하자.
git log --oneline
42408b5 (HEAD -> master) add mistake commit2
31dd9b4 add mistake commit1
deac94c add song.txt
ad1531e add third commit in song.txt
0176bfa add second commit in song.txt
120d889 add first commit in song.txt
이제 git reset
을 통해서 정상적인 commit이었던 ad1531e
로 돌아가보도록 하자. git reset
을 ad1531e
으로 설정해주면 ad1531e
이후의 commit들에 대해서 repository에 commit들이 삭제된다. 단, ad1531e
commit은 삭제되지 않는다.
git reset ad1531e
git log
를 확인해보도록 하자.
git log --oneline
ad1531e (HEAD -> master) add third commit in song.txt
0176bfa add second commit in song.txt
120d889 add first commit in song.txt
실수로 만든 commit들이 reset당한 것을 볼 수 있다. 그런데, 재미난 사실은 song.txt
에 실수로 만든 commit들의 내용이 그대로 있는 것을 볼 수 있다.
first song
second song
third song
fourth song
mistake commit1
mistake commit2
git reset
은 repository에 있는 commit을 reset할 뿐이지, workding directory에 있는 내용을 바꾸진 않는다. 따라서, mistake
commit들의 내용이 그대로 남아있는 것이다.
그래서 사용하는 것이 git reset --hard
이다. git reset
의 명령어와 같이 해당 commit hash 이후로의 commit들을 모두 삭제하지만, --hard
는 working directory의 내용도 삭제해버린다는 특징이 있다.
git reset --hard
를 사용하면, 특정 commit 다음의 commit들이 repository에서 사라지고, 내용들도 working directory에서 사라지는 지 확인해보도록 하자.
echo "miskemiskewewmksd" >> ./song.txt
git add ./song.txt
git commit -m "add mistake code"
git log
를 통해서 확인해보도록 하자.
git log --oneline
a80b856 (HEAD -> master) add mistake code
ad1531e add third commit in song.txt
0176bfa add second commit in song.txt
120d889 add first commit in song.txt
git reset --hard
을 사용하기 전에 song.txt
의 내용을 확인해보도록 하자.
cat ./song.txt
first song
second song
third song
fourth song
mistake commit1
mistake commit2
miskemiskewewmksd
이제 git reset --hard
을 사용하여 ad1531e
로 되돌아가보도록 하자. 단, 이번에는 commit hash가 아니라 HEAD
를 통해 사용해보도록 하자.
git reset --hard HEAD~1
이제 git log
를 확인해보자.
git log --oneline
ad1531e (HEAD -> master) add third commit in song.txt
0176bfa add second commit in song.txt
120d889 add first commit in song.txt
성공적으로 mistake commit을 repository에서 삭제하였다. 이제 working directory에서도 삭제되었는 지 확인해보도록 하자.
cat ./song.txt
first song
second song
third song
working directory에서도 반영된 것을 볼 수 있다. 이렇게 특정 commit으로 가서, 그 앞의 commit들을 삭제하고, working directory에도 해당 commit 내용을 되돌리고 싶다면 git reset --hard
를 사용하면 된다.
단, 주의할 것은 이러한 git reset
을 통한 commit 초기화는 각 branch마다 이뤄진다는 것이다. 즉, 위에서 master
branch와 동일한 commit flow를 가지는 또 다른 branch인 test
가 있다면 master
branch에서 git reset
한 내용은 master
branch에서만 유효하고, test
branch에서는 유효하지 않다.
git revert
는 git reset
과 유사한데, git reset
과 달리 commit을 repository에서 삭제하기 보다는, 새로운 commit을 만들어 변동사항을 반영하도록 한다. 즉, 이전 commit 사항에 대해서 수정하려는 새로운 commit을 만들어 이전 commit 사항 내용을 수정하도록 하는 것이다.
Master(HEAD)
--------- --------- ---------
|commit1| ---> |commit2| ---> |commit3|
--------- --------- ---------
다음과 같은 commit 구조에서 HEAD
에 있는 commit3에 실수가 있다고 하자. commit3
에 대해서 수정하고 싶은데, 이러한 경우 revert
를 사용하면 commit3
를 그대로 남기고, 이를 수정하는 새로운 commit을 추가한다.
Master(HEAD)
--------- --------- --------- ---------
|commit1| ---> |commit2| ---> |commit3| ---> |commit4|
--------- --------- --------- ---------
commit3에 대한 수정이
반영된 새로운 commit
왜 이전 commit을 살려두고, 수정 commit을 새로 만드는가?? 라고 생각한다면, 이는 commit에 대한 이력을 삭제하지 않고도 수정을 하기 위함이다. commit 이력이 갑자기 없어진다면, 같이 일하는 개발자들이 혼란이 겪을 수 밖에 없게된다. 이를 해결하기 위해서는 commit에 대한 이력을 남기고 새로운 commit으로 수정 사항을 반영하도록 하는 것이다.
이제 실습을 해보도록 하자. 현재의 repository 상황은 다음과 같다.
git log --oneline
ad1531e (HEAD -> master) add third commit in song.txt
0176bfa add second commit in song.txt
120d889 add first commit in song.txt
다음으로 mistake commit을 만들어보도록 하자.
echo "mistak1" >> ./song.txt
git add ./song.txt
git commit -m "add mistake1"
echo "mistak2" >> ./song.txt
git add ./song.txt
git commit -m "add mistake2"
cat ./song.txt
first song
second song
third song
mistak1
mistak2
git log
를 확인하여 commit 사항들을 확인해보도록 하자.
git log --oneline
3370ac6 (HEAD -> master) add mistake2
e62c075 add mistake1
ad1531e add third commit in song.txt
0176bfa add second commit in song.txt
120d889 add first commit in song.txt
따라서 revert
해보도록 하자.
git revert e62c075
이 때, 충돌이 나거나, 텍스트 편집기를 통해 commit 메시지를 수정하라고 나올 것이다. 지금은 충돌이 발생하여 song.txt
가 다음과 같을 것이다.
first song
second song
third song
<<<<<<< HEAD
mistak1
mistak2
=======
>>>>>>> parent of e62c075... add mistake1
mistak1
, mistak2
부분을 삭제하도록 하자. 다음으로 commit을 새로만들어 충돌된 부분을 병합하면 된다.
git add ./song.txt
git commit -m "revert mistak commit"
이제 git log --oneline
을 확인해보도록 하자.
git log --oneline
c1b85c0 (HEAD -> master) revert mistak commit
3370ac6 add mistake2
e62c075 add mistake1
ad1531e add third commit in song.txt
0176bfa add second commit in song.txt
120d889 add first commit in song.txt
revert mistak commit
로 새로운 commit이 추가된 것을 볼 수 있다.
그리고 song.txt
를 보면 mistake 내용들이 삭제된 것을 볼 수 있다.
cat ./song.txt
first song
second song
third song
그럼 왜 reset
을 사용하지 않고 revert
를 사용하는 것일까?? 이는 '협업' 때문이다.
만약, 내가 만든 commit을 다른 사람들이 갖고 있지 않다면, reset
을 사용하고, 다른 사람들이 갖고 있다면 revert
를 사용하는 것이 좋다.