Post

Git amend, restore, reset, revert

개요

클론 코딩을 하거나 토이 프로젝트를 진행하면서, git history가 엉망진창이 되는 경우가 종종 있었다. 엉켜버린 실타래를 풀다가 지쳐서 결국 프로젝트를 삭제하고, 다시 클론하거나 처음부터 다시 시작했던 경험이 여러 번 있었다. 🤦‍♂️

커밋 내역이 마음에 들지 않거나, 특정 커밋에 불필요한 변경 사항이 포함된 경우, 커밋을 너무 세분화하거나 오랫동안 커밋을 하지 않다가 한꺼번에 커밋을 하게 되는 경우 등 여러 상황에서 이런 문제들이 발생했다. 이런 상황에서 git 히스토리를 깔끔하게 관리할 수 있는 명령어들인 ammend , restore , reset , revert 명령어에 대해서 알아보자!

git commit –amend

  • 최근 커밋 메시지를 수정하거나 업데이트할 때 사용한다.

주요 사용 사례

  1. 커밋 메시지 수정
# 마지막 커밋 메시지 수정
git commit --amend -m "수정할 메시지"
  1. 누락된 파일 추가 및 커밋 내용 수정 (업데이트)
1
2
3
4
5
6
7
# 커밋 후에 추가하려는 파일을 누락했음을 알게 되었을 경우.
git add [filename]
git commit --amend

# 커밋 내용 수정 -> 이미 커밋된 파일을 수정한 후 변경사항 반영
git add [filename]
git commit --amend

주의 사항

  • 브랜치와 원격 저장소 : 수정된 커밋을 원격 저장소에 푸시하려면, 강제 푸시가 필요하다. (git push --force) 이 경우 협업 중인 사람들의 로컬 저장소에 문제가 발생할 수 있으므로 신중하게 사용해야 한다.

git restore

  • 커밋되지 않은 로컬 변경 사항. 즉, 작업 디렉토리와 인덱스(staging area)에서의 파일 변경을 복구하는 데 사용된다.

주요 사용 사례

  1. 작업 디렉토리의 변경사항 복구
1
2
# 작업 디렉토리에서 변경된 파일을 마지막 커밋 내용으로 복구
git restore [filename]
  1. 스테이징된 변경사항 취소
1
2
3
# 인덱스에 있는 파일을 다시 unstage
# 파일을 인덱스에서 제거하고, 해당 파일을 작업 디렉토리의 현재 상태로 유지.
git restore --staged|-S [filename]
  1. 스테이징과 작업 디렉토리 모두에서 변경사항 취소
1
2
3
4
5
# 작업 디렉토리와 인덱스 모두에서 변경사항을 취소하고, 마지막 커밋 상태로 복원한다.
git restore --source|-s [HashCode] [filename]
git restore --source|-s [HEAD~1] [filename]
# 프로젝트의 모든 파일을 특정 커밋 상태로 복원
git restore --source|-s [HashCode]

주의 사항

  • git restore 은 워킹 디렉토리와 인덱스의 상태를 변경하므로, 저장되지 않은 변경 사항이 있는 경우 작업이 손실될 수 있다. (예를 들어, 수정한 a 파일을 저장하지 않고 있다가 a 파일을 restore 하게 되면 수정한 내역이 모두 손실된다.)

git reset

  • 인덱스 또는 워킹 디렉토리의 상태를 되돌릴 때 사용하는 명령어이다.
  • 세 가지 주요 모드로 작동하며, 각각의 모드에 따라 커밋 기록, 스테이징된 파일, 작업 디렉토리에 미치는 영향이 다르다.
  • 주로 최근 커밋을 취소하거나 스테이징된 파일을 언스테이지할 때 사용된다.

주요 사용 사례

  1. –soft : HEAD를 지정한 커밋으로 이동시키며, 인덱스와 워킹 디렉토리의 변경 사항은 유지된다. 즉, 해당 커밋 이후의 모든 변경사항을 인덱스 상태로 유지한다.
1
2
3
4
# 최근 커밋을 취소하고, 그 변경 사항을 다시 스테이징하고 싶을 때
git reset --soft [HashCode]
# 마지막 커밋 취소하고 변경사항 스테이징
git reset --soft HEAD~1
  1. –mixed(기본값) : HEAD를 지정한 커밋으로 이동시키며, 인덱스를 초기화하지만, 워킹 디렉토리의 변경 사항은 유지한다. 즉, 변경사항은 unstage 되지만, 파일은 수정된 상태로 남는다.
1
2
3
4
# 특정 커밋 이후의 모든 변경 사항을 언스테이지하고, 작업 디렉토리에 남겨두고 싶을 때
git reset --mixed [HashCode]
# 스테이징된 파일을 언스테이지하고 워킹 디렉토리에 변경 사항을 남겨두고 싶을 때
git reset HEAD [filename]
  1. –hard : HEAD를 지정한 커밋으로 이동시켜, 인덱스와 워킹 디렉토리의 모든 변경 사항을 취소한다. 즉, 지정한 커밋 상태로 완전히 되돌아가며, 취소된 변경 사항은 복구할 수 없다.
1
2
3
4
5
# 특정 커밋 이후의 모든 변경 사항을 완전히 삭제하고,
# 해당 커밋의 상태로 워킹 디렉토리와 인덱스를 되돌리고 싶을 때
git reset --hard [HashCode]
# 현재 작업 중인 모든 변경 사항을 완전히 취소하고, 특정 커밋 상태로 돌아가고 싶을 때
git reset --hard HEAD

주의 사항

  • hard 옵션으로 reset 후, push 할 때
    • 로컬 저장소의 커밋 히스토리가 원격 저장소의 커밋 히스토리보다 뒤에 있어(non-fast-forward)인 경우, 푸시를 시도하면 오류가 발생한다.
    • 이런 상황에서는 push -f 옵션을 사용하여 강제로 덮어써야 한다. 하지만 이는 원격 저장소의 커밋 히스토리를 재작성하여 다른 사람의 작업 내용을 덮어쓸 수 있으므로 매우 신중히 사용해야 한다.
  • reset을 원격저장소에 사용할 때
    • 협업 중인 브랜치에서는 reset을 사용할 때 주의해야 한다. 특히 --hard 옵션을 사용하는 경우, 해당 브랜치에서 삭제될 커밋에 다른 사람이 작성한 커밋이 포함될 수 있다.
    • 또한, 삭제될 커밋들이 이미 다른 사람의 로컬 저장소로 전파된 경우, 그것들은 여전히 해당 커밋을 가지고 있을 것이다. 이는 협업 시 혼란을 초래할 수 있다.
    • 따라서, reset은 혼자 사용하는 브랜치에서만 사용!하거나, 다른 사람들이 해당 브랜치를 pull 해가지 않았다는 것이 확인된 경우에만 사용하는 것이 안전하다.

git revert

  • reset과 다르게 특정 커밋의 변경 사항을 되돌리는 새로운 커밋을 생성하는 명령어이다.
  • 기존 커밋 히스토리를 유지하면서 변경 사항을 취소하기 때문에 협업 환경에서 안전하게 사용할 수 있다.
  • 커밋 자체를 삭제하는 것이 아니라, 지정된 커밋을 “역으로” 적용하여 그 커밋의 영향을 취소하는 방식으로 동작한다.
  • 되돌리는 과정에서 변경 사항이 현재 코드와 충돌할 수 있다. 이 경우, git은 충돌을 해결할 수 있도록 사용자에게 알리며, 사용자는 수동으로 충돌을 해결한 후 git revert --continue 명령을 사용해 되돌리기 가정을 완료할 수 있다.
  • 원격 저장소에 푸시된 커밋을 되돌릴 대 적합하다. 커밋을 삭제하지 않고 새로운 커밋을 생성하기 때문에, 협업 중인 다른 개발자들의 작업에 영향을 최소화할 수 있다.
  • history 관리 차원에서 reset 보다는 revert 를 쓰도록 하자! 😊

주요 사용 사례

  1. 커밋 되돌리기
1
2
3
# 특정 커밋을 선택하여 그 커밋의 변경 사항을 취소하는 새로운 커밋을 만든다.
# [HashCode]의 변경사항을 되돌리는 새로운 커밋을 생성한다.
git revert [HashCode]
  1. 여러 커밋 되돌리기
1
2
3
4
5
# 연속된 여러 커밋을 되돌리고 싶을 때, 시작 커밋과 끝 커밋을 지정한다.
# 지정된 범위의 커밋을 각각 개별적으로 되돌리는 것이다.
git revert [시작커밋]..[끝 커밋]
# 마지막 세 개의 커밋을 역순으로 하나씩 되돌리는 새로운 커밋을 생성한다.
git revert HEAD~3..HEAD

주의 사항

  • 병합 커밋 되돌리기 : 병합 커밋을 되돌릴 때는 주의가 필요하다. 병합 커밋을 되돌릴 경우, 어떤 부모 커밋을 기준으로 되돌릴지를 지정해야 하며, 상황에 따라 -m 옵션이 필요할 수 있다.
  • 충돌 발생 가능성 : 되돌리는 과정에서 코드가 현재 워킹 디렉토리와 충돌할 수 있으므로, 충돌 해결 능력이 필요하다.
This post is licensed under CC BY 4.0 by the author.