본문으로 건너뛰기
개발 머꼬
개발 노트Git
hohyeon.dev14

git push --force로 동료 커밋을 날린 뒤 --force-with-lease를 쓰게 된 이유

  • #Common Pitfall
  • #Engineering Note
  • #Git

문제 발생

리베이스한 브랜치를 --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의 비교가 통과하고, 보호가 없는 상태에서 밀게 됩니다.

해결 방안

  1. 기본을 --force-with-lease로 바꿉니다.
git push --force-with-lease origin feature/search
  1. 자동 fetch가 도는 환경이면 --force-if-includes를 함께 씁니다. Git 2.30에 추가된 옵션으로, 원격의 최신 커밋이 내 로컬 히스토리에 실제로 반영되어 있는지까지 확인합니다. fetch만 되고 rebase/merge는 안 한 상태를 걸러냅니다.
git push --force-with-lease --force-if-includes origin feature/search
  1. 정말 확실히 하려면 기대값을 직접 적습니다. --force-with-lease=<refname>:<expect> 형태는 remote-tracking ref 대신 내가 적은 커밋과 비교합니다. 백그라운드 fetch의 영향을 받지 않습니다.
git fetch origin
git push --force-with-lease=feature/search:9f3a1c2 origin feature/search
  1. 거부당하면 덮어쓰지 말고 먼저 봅니다. stale info는 사고가 아니라 경고입니다.
git fetch origin
git log --oneline HEAD..origin/feature/search   # 내가 모르는 커밋
  1. 공유 브랜치는 애초에 되감지 않습니다. main처럼 여러 명이 기준으로 삼는 브랜치는 서버 설정(receive.denyNonFastForwards)이나 브랜치 보호로 막아두는 편이 안전합니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.