🌈 기술스택/Git

실수를 되돌리는 방법들 (restore, reset, revert)

NoHack 2021. 10. 24. 08:26
728x90

마음 아프지만 실수는 누구나 하는 것 😢

Git으로 프로젝트를 관리하다 보면 종종 실수를 합니다. 오타를 낸 상태로 커밋 메시지를 저장하기도 하고, 때로는 문제가 해결되지 않은 코드를 그대로 커밋하는 경우도 있을 수 있습니다. 저도 정말 많이 겪은 실수들인데 이럴 때 되돌아가는 방법을 몰라서 항상 프로젝트를 다시 클론하거나, 완전히 지운 다음 처음부터 다시 시작하곤 했습니다. 작은 실수라도 문제이지만, 치명적인 영향을 주는 실수라면.. 생각만 해도 벌써부터 현기증이 나네요 ㅂㄷㅂㄷ

다행히 Git은 실수가 발생했을 때, 되돌릴 수 있도록 다양한 명령을 제공하고 있습니다.

최근 커밋 수정하기

변경 내역을 커밋할 때 메시지를 잘못 작성했으면 다음 명령을 사용하면 됩니다.

$ git commit --amend -m "변경할 메시지"

--amend 옵션은 직전의 커밋에 대해 메시지나 파일을 변경할 수 있도록 하는 옵션입니다. 만약 파일들을 수정하고 그 중 일부를 커밋했는데, 실수로 하나를 빠뜨렸다면 다음과 같이 동일하게 --amend 옵션을 사용하면 됩니다.

$ git add <누락된 파일>
$ git commit --amend

만약 더 이전의 커밋 정보를 변경하고 싶다면, 이때는 git rebase 명령을 사용해야 합니다.

파일 복구하기

파일의 내용을 수정했는데, 수정하기 전으로 복구하고 싶다면 다음 명령을 사용하면 됩니다.

$ git restore <modified 상태인 파일명>

restore은 복구를 위한 명령이며, git add 명령으로 Staging Area에 올라간 파일들을 다시 Working Directory로 되돌릴 수도 있습니다. 이 때는 --staged 옵션과 함께 사용하면 되는데, Git에서 staged와 cached는 Staging Area를 의미합니다.

$ git restore --staged <파일명>

위 명령들은 git status를 사용해 디렉토리의 상태를 확인할 때, Git에서 힌트로써 제공해 줍니다.

그리고 --source 옵션을 사용하면 특정 파일을 특정 커밋 시점의 상태로 복구할 수 있습니다.

$ git restore --source=<hash> <파일명>

이렇게 하면 특정 커밋 시점의 파일 상태로 복구가 되긴 하지만, modified 상태이기에 새롭게 커밋을 해야 합니다.

특정 커밋으로 되돌아가기

git reset 명령을 사용하면 특정 커밋으로 되돌아갈 수 있습니다. 대신 reset의 이름에 걸맞게 이후의 커밋들은 모두 초기화되어 히스토리에서 사라집니다. reset은 확실하게 되돌아갈 수 있는 방법이지만 모든 기록을 없애는 작업이기 때문에 사용할 때 항상 주의를 기울여야 합니다.

reset을 보여드리기 위해 예제를 준비했습니다.

위에서부터 로그를 보면 2번째 커밋에서 b.txt라는 파일이 삭제되었습니다. 그런데 저는 b.txt 파일이 삭제되기 전으로 되돌아가고 싶어졌습니다. 이런 경우 git reset <hash> 형태의 명령을 실행하면 되는데, b.txt 파일이 존재하던 시점은 Update b.txt 커밋 메시지가 적힌 직후의 시점이니 이 hash값 d015ab7을 복사해야 합니다.

별도의 옵션 없이 git reset을 사용하면 기본적으로 --mixed 옵션이 적용됩니다. mixed와 함께 soft, hard까지 총 3가지의 옵션이 있습니다. --hard는 돌아간 시점의 커밋 바로 직후의 상태로 완전히 초기화하는 옵션이고, --soft는 Staging Area까지 초기화하는 옵션입니다. 마지막으로 --mixed는 두 옵션의 중간 옵션이며 Working Directory까지만 초기화하는 옵션입니다. 우리는 b.txt 파일이 있던 시점으로 되돌아가기 위해 git reset d015ab7 명령을 실행했는데, 이렇게 되면 Working Directory 상태까지 복구되기 때문에 b.txt 파일은 deleted 상태로 나타날 것입니다.

최종적으로 b.txt 파일을 복구하기 위해서는 git store b.txt 명령을 실행해야 합니다. 이와 같이 만약 되돌아간 시점 이후의 작업 내역은 필요하지 않다면, --hard 옵션을 사용해서 깔끔하게 커밋 시점 직후로 초기화하면 됩니다.

직접 해보지 않으면 헷갈릴 수 있어서, 위 예시에서의 각 옵션을 추가로 정리했습니다.
--mixed: 다음 커밋을 위해 b.txt 파일을 삭제한 상태
--soft: 다음 커밋을 위해 삭제한 b.txt 파일 정보를 add한 상태
--hard: "Update b.txt" 커밋을 남긴 바로 직후의 상태

특정 커밋만 취소하기

reset을 사용하게 되면 되돌아간 커밋 이후의 히스토리는 전부 삭제됩니다. 하지만 특정 커밋만 제거하고 싶을 수도 있습니다. 위 예시의 로그에서는 b.txt 파일을 제거하기 이전으로 돌아가기 위해, d.txt 파일이 추가된 커밋도 초기화했습니다. 하지만 d.txt 파일도 살리면서, b.txt 파일을 제거한 커밋만 취소하는 방법은 없을까요? 이럴 때는 revert라는 명령을 사용하면 됩니다.

취소하고자 하는 커밋에 대해 git revert <hash> 명령을 실행하면, 에디터가 열리면서 다음 내용을 보여 줍니다.

Revert "Delete b.txt"

This reverts commit 9cbf7776e0d477a534ab437ee0bc0fd55dc65a9a.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main
# Changes to be committed:
#	new file:   b.txt
#

별다른 문제가 없다면 저장 후 파일을 닫아서 Revert를 마치면 됩니다.

References 📝

본문의 내용은 Git 공식 문서를 참고해서 작성했습니다 ❤️