Git force push의 안전장치 --force-with-lease

Git force push의 안전장치 --force-with-lease

2025년 01월 21일

들어가기 전에

Git 작업시 부득이하게 force 푸시를 해야하는 상황이 있습니다.
force 푸시 옵션에는 두가지가 있습니다.

  1. --force
  2. --force-with-lease

--force 옵션은 강제로 원격 저장소의 커밋 히스토리를 덮어쓰는 작업입니다.

force 푸시의 위험성에 대해 알아보고, 조금 더 안전하게 푸시하는 방법인 --force-with-lease 옵션에 대해서 알아보겠습니다.

force 푸시의 위험성

설명하지 않아도 이미 경험상 알고 있는 분들이 많을 것 같습니다.

혼자서 작업하는 경우 --force 옵션을 사용해도 크게 문제가 없겠지만, 협업하는 경우 동료가 올린 작업 내역을 덮어쓰게 되는 문제가 있습니다.

사례 1.

마이크, 로지타 두 사람이 같은 브랜치에서 협업하는 상황을 가정해봅시다.
(마이크는 오랜세월 혼자서 작업하는데 익숙했고 force 푸시를 남발했습니다. 협업에 익숙하지 않습니다.)

  1. 로지타는 앱의 버그를 수정해서 커밋을 푸시합니다.
  2. 마이크도 앱의 버그를 수정해서 커밋을 푸시하지만 실패합니다.
  3. 마이크는 왜 푸시가 실패했는지 모르지만 (알아보지도 않고) force 푸시합니다. (로지타가 작업한 내역을 덮어쓰게 됩니다.)

이후 앱을 배포했는데 버그가 발생했습니다.
로지타는 왜 수정한 버그가 또 발생했는지 이해가 안갔고, 찾는데 3시간이 걸렸습니다.

사례 2.

사례 1 같은 문제를 방지하기 위해서 로지타는 마이크에게 force 푸시하기 전에 꼭 협의하고 하자고 말합니다.

  1. 로지타가 force 푸시 할 일이 생겼고 마이크한테 지금 force 푸시 할테니 작업 내역 올리지 말라고 말합니다. 마이크도 알겠다고 했습니다.
  2. 하지만 마이크는 로지타가 force 푸시 하기 전에 자신의 작업 내역을 푸시합니다. 😮
  3. 로지타가 force 푸시 합니다. (마이크의 작업 내역을 덮어쓰게 됩니다.)

또 버그가 발생합니다.

저의 경험담을 로지타의 입장에서 간략히만 적었습니다.

이렇게 협의하고 주의하더라도 사람은 실수할 수 있기 때문에 항상 조심해야 합니다.
force 푸시했을 때 동료의 작업 내역이 사라진 것을 바로 알게 된다면 그나마 괜찮지만, 나중에 알게 될 수도 있습니다.

조금 더 안전한 푸시 방법 --force-with-lease

--force-with-lease 옵션도 원격 저장소의 커밋 히스토리를 덮어쓰는 작업이지만 안전장치가 있습니다.

그 안전장치는 푸시할때 원격 추적 브랜치와 원격 브랜치를 비교합니다.
둘의 상태가 같으면 푸시가 허용되고, 다르면 푸시가 거부됩니다.

용어위치설명
원격 브랜치원격 저장소원격 저장소(서버)에 실제로 존재하는 브랜치 (원격의 main)
원격 추적 브랜치로컬 저장소로컬에서 원격 브랜치를 추적하는 브랜치 (origin/main)
로컬 브랜치로컬 저장소로컬에서 작업 중인 브랜치 (main)

마이크, 로지타의 시나리오로 설명하겠습니다.

  1. 원격 저장소의 상태
    A --- B

  2. 로지타가 C 커밋 생성 (푸시는 하지 않음)

    • 로컬 브랜치 상태
      A --- B --- C
    • 원격 추적 브랜치 상태
      A --- B
  3. 마이크가 D 커밋을 원격 저장소에 푸시

    • 원격 브랜치 변경 발생
      A --- B --- D
  4. 로지타가 git push --force-with-lease 실행

Git은 원격 추적 브랜치(origin/main)와 원격 저장소의 상태와 비교합니다.

  • 로지타의 원격 추적 브랜치 : A --- B
  • 원격 브랜치 : A --- B --- D

둘의 상태가 다르기 때문에 푸시가 거부됩니다.

git push --force-with-lease
To github.com:2circumflex/force-push-test.git
 ! [rejected]        main -> main (stale info)
error: failed to push some refs to 'github.com:2circumflex/force-push-test.git'

로지타는 푸시가 거부되었기 때문에 원격 브랜치에 커밋 히스토리 변경이 있다는 것을 알게 됩니다.

만약에 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 같은 문제를 방지할 수 있습니다.