본문 바로가기
TYLER SONGBlog
← Blog index
CS학습4 min read

Revert: 기존 기록을 지우지 않고 변경을 되돌리는 새 커밋 만들기

git revert는 특정 커밋의 변경을 반대로 적용하는 새 커밋을 만들어 변경을 취소하는 명령이다. 기존 히스토리를 수정하지 않으므로 이미 공유된 브랜치에서도 안전하게 쓸 수 있다. reset과의 차이, 머지 커밋 되돌리기, 충돌 처리 방법까지 함께 정리한다.

By 송민성

1. 개념

git revert <커밋>은 지정한 커밋이 만든 변경 내용을 반대로 적용한 새 커밋을 만드는 명령이다. 원래 커밋은 히스토리에 그대로 남고, 그 뒤에 "되돌리는 커밋"이 하나 추가된다.

예를 들어 커밋 C에서 함수 하나를 추가했다면, C를 revert한 커밋은 그 함수를 삭제하는 변경을 담는다.

text
A --- B --- C --- D --- R
                       (R = C의 변경을 취소하는 커밋)

2. 왜 사용하는가

  • 히스토리를 보존한다. 무엇이 언제 왜 취소되었는지 기록이 남는다.
  • 공유 브랜치에서 안전하다. 이미 push한 커밋을 reset으로 지우면 다른 사람의 저장소와 히스토리가 어긋나지만, revert는 커밋을 추가할 뿐이라 일반 push로 공유할 수 있다.
  • 특정 커밋만 골라 취소할 수 있다. 최근 커밋이 아니라 과거의 중간 커밋도 대상으로 삼을 수 있다.

3. 동작 원리

Git은 커밋을 스냅숏으로 저장하지만, revert는 대상 커밋과 그 부모 커밋의 차이(diff)를 계산한다. 그다음 이 diff를 뒤집어 현재 작업 트리에 3-way merge 방식으로 적용하고 새 커밋을 만든다.

  1. 대상 커밋 C와 부모 C^의 차이를 구한다.
  2. 그 차이를 반대로 현재 HEAD 위에 적용한다.
  3. 충돌이 없으면 자동으로 커밋하고, 있으면 사용자가 해결하도록 멈춘다.

그래서 C 이후에 같은 부분을 수정한 커밋이 있다면 충돌이 발생할 수 있다.

머지 커밋은 부모가 둘 이상이므로 어느 부모 쪽을 기준으로 되돌릴지 -m 옵션으로 지정해야 한다. -m 1은 첫 번째 부모(보통 머지를 받은 쪽 브랜치)를 기준으로 삼는다.

4. 코드 예제

빈 디렉터리에서 그대로 실행해 볼 수 있는 예제다.

shell
git init revert-demo && cd revert-demo echo "hello" > a.txt git add a.txt && git commit -m "add a.txt" echo "bug" >> a.txt git commit -am "introduce bug" echo "world" >> a.txt git commit -am "add world" git log --oneline # 세 개의 커밋 확인 # 중간 커밋("introduce bug")만 취소 git revert --no-edit HEAD~1 git log --oneline # Revert "introduce bug" 커밋이 맨 위에 추가됨 cat a.txt # hello # world

자주 쓰는 변형은 다음과 같다.

shell
# 커밋하지 않고 변경만 작업 트리에 반영 (여러 커밋을 묶어 하나로 커밋할 때) git revert --no-commit HEAD~2..HEAD git commit -m "Revert last two commits" # 머지 커밋 되돌리기 (첫 번째 부모 기준) git revert -m 1 <머지커밋해시> # 충돌 발생 시 git status # 충돌 파일 확인 # 파일 수정 후 git add <파일> git revert --continue # 또는 git revert --abort

5. 시간 복잡도 또는 성능 특성

revert는 자료구조 연산이 아니므로 큰 O 표기로 정리하기보다 비용이 어디서 생기는지를 보는 편이 정확하다.

  • 비용은 대체로 되돌릴 커밋이 건드린 파일의 변경 규모와, 그 이후 같은 파일에 쌓인 변경에 따라 달라진다.
  • 저장소 전체 히스토리를 다시 쓰지 않는다. 새 커밋 하나(또는 지정한 개수)만 추가한다.
  • 충돌이 생기면 사람이 해결하는 시간이 가장 큰 비용이 된다.

6. 실무 사용 사례

  • 배포 직후 장애 대응: 문제를 일으킨 커밋을 main에서 revert하고 즉시 배포해 서비스를 복구한다.
  • PR 머지 취소: GitHub 등에서 제공하는 "Revert" 버튼은 내부적으로 해당 머지 커밋을 되돌리는 새 PR을 만든다.
  • 릴리스 브랜치 정리: 릴리스에 포함하기 이른 기능 커밋만 골라 취소한다.

7. 주의할 점

  • revert의 revert: 머지 커밋을 revert한 뒤 같은 브랜치를 다시 머지하면, Git은 이미 반영된 것으로 보고 변경을 가져오지 않는다. 다시 넣으려면 먼저 그 revert 커밋을 revert해야 한다.
  • 머지 커밋은 `-m`이 필수다. 지정하지 않으면 오류가 난다.
  • 데이터 변경은 되돌리지 못한다. 코드만 되돌릴 뿐, DB 마이그레이션이나 이미 실행된 외부 작업의 결과는 별도로 처리해야 한다.
  • reset과 구분하자. 아직 push하지 않은 로컬 커밋은 reset으로 정리할 수 있지만, 공유된 커밋에는 revert를 쓰는 것이 원칙이다.
  • 작업 트리에 커밋하지 않은 변경이 있으면 revert가 거부될 수 있으므로 먼저 커밋하거나 stash로 정리한다.

8. 핵심 정리

  • git revert는 대상 커밋의 변경을 반대로 적용하는 새 커밋을 만든다.
  • 히스토리를 고쳐 쓰지 않으므로 push된 공유 브랜치에도 안전하다.
  • 이후 커밋과 겹치는 부분이 있으면 충돌이 날 수 있다.
  • 머지 커밋은 -m <부모번호>로 기준 부모를 지정해야 한다.
  • 로컬에서만 쓰는 커밋은 reset, 이미 공유된 커밋은 revert로 구분해 쓴다.