"Git server에 push를 잘못했어요. commit을 되돌리고싶어요"
- 안타깝게도 이미 remote에 올라간 commit은 되돌릴 수 없다. 그러게 로컬에서 잘확인하고 push했어야지...
- 하지만 해결방법이 아예 없는 것은 아니다. commit이 잘못되었음을 빠르게 인지했을 경우, main-branch에 자신의 commit이후에 다른사람의 commit이 많지 않을 경우엔 다음 방법을 고려해 볼 수 있다.
Revert
- revert는 해당 commit을 되돌린다는 개념보다는 원복한다의 개념에 초점이 더 맞춰져있다. 말 그대로 추가된 라인은 제거되고 제거된 라인은 추가된다. 파일자체가 삭제되거나 rename된 경우에도 commit의 변경사항을 완전히 반대로 수행하는 것이 revert다
- 사용방법은 다음과 같다
$ git log -i
$ git revert <commit-hash>
- log명령을 통해 커밋의 해시(고유식별자)를 확인하고 revert시킨다.
Revert의 치명적 단점
- 위만 보면 revert자체로도 나의 commit을 되돌린다는 느낌은 있지만 여기엔 치명적인 단점이 존재한다. revert는 commit자체를 제거하는 것이 아니기 때문에 commit기록도 히스토리에 저장되고 revert는 별개의 commit으로 추가가되는 구조이다. 만약 다음과 같은 상황이라고 가정해보자.
- A라는 브랜치가 있고 나의 commit은 여기에 있다. 나는 A 커밋을 origin에 merge하였는데, 협업상 지금 merge가 되어서는 안되고 나중에 다른작업자의 작업물과 함께 올라가야한다.
- 이 때 내 merge된 commit을 revert하면 내가 A 브랜치에서 작업한 commit들의 종합에 대한 반대의 commit이 생성된다.
- 나는 이후 A브랜치를 origin에 다시 merge하려고했으나 아무런 변경이 발견되지않아서 충돌도, 변경도 아무것도 일어나지 않았다.
- 이 문제가 발생하는 이유는 이미 origin에는 A브랜치의 commit에 대한 해시가 있기 때문에 이미 포함된 commit으로 받아들이기 때문이다.
- 즉 revert는 어차피 다시작업을 해야하거나, 정말 특수한 경우가 아닌이상 사용을 지양하는것이 좋다.
Force Push
- push에 --force옵션을 주게되면 원격저장소의 내용을 현재 내 로컬브랜치의 내용으로 덮어쓰겠다는 뜻이다. 어감에서 보이듯이 절대로 막 사용해서는 안되는 아주 위험한 명령이며 팀원들과 전체적으로 조율이 된 경우에만 해당방법을 사용한다.
- 아래의 절차를 따른다.
- 작업자
$ git log -i
$ git reset [--hard] <commit-hash>
$ git push --force
- commit hash를 파악한다. (잘못된 커밋의 직전버전 커밋의 hash)
- 현재 브랜치를 해당 commit의 버전으로 reset한다. reset에는 soft, mixed, hard 리셋이 존재하는데, 일반적으로 force push를 하기 위해선 hard리셋을 한다.
- 현재 버전으로 원격저장소에 force push한다.
- 위 절차를 따랐으면 원격저장소에 이미 올라간 내역은 모두 사라지고 reset한 commit의 버전으로 재정렬된다.
- 협업자는 다음 절차를 따른다.
$ git status // 변경사항 확인
$ git merge --abort // 진행중인 merge가 있으면 abort시킨다
$ git reset --hard origin // origin으로 hard reset(HEAD도 초기화함)
$ git branch -d another_branch // 로컬 origin을 제거하기 위해 다른 브랜치로 이동한다.
$ git fetch origin // fetch
$ git checkout -b origin origin // 원격 origin으로 checkout
$ git remote prune origin // 유효하지않은 로컬브랜치 제거
결론
- 위 방법들은 웬만해서는 사용할 일이 없어야하는 방법이다. 모두 안전한 원격저장소 관리를 하기를 바란다.