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 방식으로 적용하고 새 커밋을 만든다.
- 대상 커밋
C와 부모C^의 차이를 구한다. - 그 차이를 반대로 현재
HEAD위에 적용한다. - 충돌이 없으면 자동으로 커밋하고, 있으면 사용자가 해결하도록 멈춘다.
그래서 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 --abort5. 시간 복잡도 또는 성능 특성
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로 구분해 쓴다.