Git 재활 훈련 6일차 - git restore, reset, revert

0

Git

목록 보기
6/14

'git restore' local 변동 사항 삭제하기

새로 나온 명령어로 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 --staged' stage 변동 사항 취소하기

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 restoregit restore --staged가 조금 헷갈릴 수 있는데, 하나는 local 변동 사항을 제거하는 것이고, 하나는 stage에 있는 file을 stage 영역에서 제외하는 기능이라고 보면 된다.

이제 secret.txt를 제외한 song.txt에 대해서 commit을 진행해보도록 하자.

git add ./song.txt
git commit -m "add song.txt"

git reset

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 resetad1531e으로 설정해주면 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들의 내용이 그대로 있는 것을 볼 수 있다.

  • song.txt
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 revertgit 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가 다음과 같을 것이다.

  • 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를 사용하는 것이 좋다.

0개의 댓글