git push --force로 동료 커밋을 날린 뒤 --force-with-lease를 쓰게 된 이유
문제 발생
리베이스한 브랜치를 --force로 밀었더니, 그사이 동료가 같은 브랜치에 올린 커밋 두 개가 사라졌습니다.
$ git push --force origin feature/search
+ 9f3a1c2...4b7e0d1 feature/search -> feature/search (forced update)forced update라고만 나오고, 무엇을 덮어썼는지는 알려주지 않았습니다.
원인 분석
--force는 조건 없이 원격 브랜치를 내 커밋으로 바꿉니다. 원격에 무엇이 있었는지 보지 않습니다. 그래서 "내가 마지막으로 fetch한 이후에 남이 올린 커밋"이 있어도 그대로 사라집니다.
공식 문서가 대안으로 제시하는 것이 --force-with-lease입니다. 이건 밀기 전에 원격의 현재 ref가 내 remote-tracking ref와 같은지 확인합니다. 다르면 "내가 모르는 변경이 있다"는 뜻이므로 거부합니다.
$ git push --force-with-lease origin feature/search
! [rejected] feature/search -> feature/search (stale info)여기에 실제로 사람들이 물리는 함정이 하나 있습니다. 공식 문서가 명시합니다 — 백그라운드에서 도는 git fetch가 이 옵션과 매우 나쁘게 상호작용합니다. IDE나 셸 프롬프트가 주기적으로 git fetch를 돌리면, 내가 보지도 않은 동료 커밋으로 remote-tracking ref가 조용히 갱신됩니다. 그러면 --force-with-lease의 비교가 통과하고, 보호가 없는 상태에서 밀게 됩니다.
해결 방안
- 기본을
--force-with-lease로 바꿉니다.
git push --force-with-lease origin feature/search- 자동 fetch가 도는 환경이면
--force-if-includes를 함께 씁니다. Git 2.30에 추가된 옵션으로, 원격의 최신 커밋이 내 로컬 히스토리에 실제로 반영되어 있는지까지 확인합니다. fetch만 되고 rebase/merge는 안 한 상태를 걸러냅니다.
git push --force-with-lease --force-if-includes origin feature/search- 정말 확실히 하려면 기대값을 직접 적습니다.
--force-with-lease=<refname>:<expect>형태는 remote-tracking ref 대신 내가 적은 커밋과 비교합니다. 백그라운드 fetch의 영향을 받지 않습니다.
git fetch origin
git push --force-with-lease=feature/search:9f3a1c2 origin feature/search- 거부당하면 덮어쓰지 말고 먼저 봅니다.
stale info는 사고가 아니라 경고입니다.
git fetch origin
git log --oneline HEAD..origin/feature/search # 내가 모르는 커밋- 공유 브랜치는 애초에 되감지 않습니다.
main처럼 여러 명이 기준으로 삼는 브랜치는 서버 설정(receive.denyNonFastForwards)이나 브랜치 보호로 막아두는 편이 안전합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.