들어가기 전에
Git 작업시 부득이하게 force 푸시를 해야하는 상황이 있습니다.
force 푸시 옵션에는 두가지가 있습니다.
--force
--force-with-lease
--force
옵션은 강제로 원격 저장소의 커밋 히스토리를 덮어쓰는 작업입니다.
force 푸시의 위험성에 대해 알아보고, 조금 더 안전하게 푸시하는 방법인 --force-with-lease
옵션에 대해서 알아보겠습니다.
force 푸시의 위험성
설명하지 않아도 이미 경험상 알고 있는 분들이 많을 것 같습니다.
혼자서 작업하는 경우 --force
옵션을 사용해도 크게 문제가 없겠지만, 협업하는 경우 동료가 올린 작업 내역을 덮어쓰게 되는 문제가 있습니다.
사례 1.
마이크, 로지타 두 사람이 같은 브랜치에서 협업하는 상황을 가정해봅시다.
(마이크는 오랜세월 혼자서 작업하는데 익숙했고 force 푸시를 남발했습니다. 협업에 익숙하지 않습니다.)
- 로지타는 앱의 버그를 수정해서 커밋을 푸시합니다.
- 마이크도 앱의 버그를 수정해서 커밋을 푸시하지만 실패합니다.
- 마이크는 왜 푸시가 실패했는지 모르지만 (알아보지도 않고) force 푸시합니다. (로지타가 작업한 내역을 덮어쓰게 됩니다.)
이후 앱을 배포했는데 버그가 발생했습니다.
로지타는 왜 수정한 버그가 또 발생했는지 이해가 안갔고, 찾는데 3시간이 걸렸습니다.
사례 2.
사례 1 같은 문제를 방지하기 위해서 로지타는 마이크에게 force 푸시하기 전에 꼭 협의하고 하자고 말합니다.
- 로지타가 force 푸시 할 일이 생겼고 마이크한테 지금 force 푸시 할테니 작업 내역 올리지 말라고 말합니다. 마이크도 알겠다고 했습니다.
- 하지만 마이크는 로지타가 force 푸시 하기 전에 자신의 작업 내역을 푸시합니다. 😮
- 로지타가 force 푸시 합니다. (마이크의 작업 내역을 덮어쓰게 됩니다.)
또 버그가 발생합니다.
저의 경험담을 로지타의 입장에서 간략히만 적었습니다.
이렇게 협의하고 주의하더라도 사람은 실수할 수 있기 때문에 항상 조심해야 합니다.
force 푸시했을 때 동료의 작업 내역이 사라진 것을 바로 알게 된다면 그나마 괜찮지만, 나중에 알게 될 수도 있습니다.
조금 더 안전한 푸시 방법 --force-with-lease
--force-with-lease
옵션도 원격 저장소의 커밋 히스토리를 덮어쓰는 작업이지만 안전장치가 있습니다.
그 안전장치는 푸시할때 원격 추적 브랜치와 원격 브랜치를 비교합니다.
둘의 상태가 같으면 푸시가 허용되고, 다르면 푸시가 거부됩니다.
용어 | 위치 | 설명 |
---|---|---|
원격 브랜치 | 원격 저장소 | 원격 저장소(서버)에 실제로 존재하는 브랜치 (원격의 main) |
원격 추적 브랜치 | 로컬 저장소 | 로컬에서 원격 브랜치를 추적하는 브랜치 (origin/main) |
로컬 브랜치 | 로컬 저장소 | 로컬에서 작업 중인 브랜치 (main) |
마이크, 로지타의 시나리오로 설명하겠습니다.
-
원격 저장소의 상태
A --- B -
로지타가 C 커밋 생성 (푸시는 하지 않음)
- 로컬 브랜치 상태
A --- B --- C - 원격 추적 브랜치 상태
A --- B
- 로컬 브랜치 상태
-
마이크가 D 커밋을 원격 저장소에 푸시
- 원격 브랜치 변경 발생
A --- B --- D
- 원격 브랜치 변경 발생
-
로지타가
git push --force-with-lease
실행
Git은 원격 추적 브랜치(origin/main)와 원격 저장소의 상태와 비교합니다.
- 로지타의 원격 추적 브랜치 : A --- B
- 원격 브랜치 : A --- B --- D
둘의 상태가 다르기 때문에 푸시가 거부됩니다.
로지타는 푸시가 거부되었기 때문에 원격 브랜치에 커밋 히스토리 변경이 있다는 것을 알게 됩니다.
만약에 3번 과정이 없어서 원격 브랜치에 B 커밋 이후로 추가된 커밋이 없다면 안전하게 푸시가 됩니다.
주의사항
--force-with-lease는 조금 더 안전한 방법이지만 완전히 안전한 방법이 아닙니다.
그래서 조심해야 될 부분이 있습니다.
위의 시나리오 4번에서 로지타가 git push --force-with-lease
를 실행하기 전에 git fetch
를 실행하게 되면 원격 추적 브랜치와 원격 브랜치의 상태가 같아서 푸시가 허용됩니다.
git fetch를 하게되면 로지타의 원격 추적 브랜치 상태는 A --- B --- D 가 됩니다.
- 로지타의 로컬 브랜치 : A --- B --- C
- 로지타의 원격 추적 브랜치 : A --- B --- D
- 원격 브랜치 : A --- B --- D
원격 브랜치가 A --- B --- C 로 변경되고 마이크가 푸시한 D 커밋이 사라지게 됩니다.
결론
force 푸시가 필요한 경우 --force-with-lease
옵션을 사용해서 조금 더 안전하게 푸시하는 것을 추천합니다.
이 옵션을 사용하면 사례 1, 2 같은 문제를 방지할 수 있습니다.