[Git] git push -f를 언제, 어떻게 써야 덜 위험한지 정리
협업 시 git push -f가 위험한 이유
“커밋 메시지를 수정했는데 push가 안 돼. 그냥 -f로 강제 푸시하면 되겠지?”
이런 생각으로 git push -f를 실행하는 순간, 동료의 작업이 증발할 수 있습니다. 이번 포스트에서는 force push의 위험성과 안전한 사용법, 그리고 민감한 파일을 완전히 삭제하는 방법까지 다뤄보겠습니다.
왜 push가 거부되는가?
git commit --amend나 git reset을 사용하면 로컬 저장소의 히스토리가 변경됩니다. 이때 원격 저장소에 push하려고 하면 Git은 다음과 같은 오류를 뱉습니다.
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.
이유: 로컬의 히스토리가 원격보다 뒤처지거나 갈라졌기 때문입니다. Git은 기본적으로 히스토리를 덮어쓰는 것을 허용하지 않습니다.
Force Push: 강제로 덮어쓰기
이런 상황에서 사용하는 것이 force push입니다.
# 기본 force push (위험!)
git push origin [브랜치명] -f
# 또는 더 안전한 옵션 (추천)
git push origin [브랜치명] --force-with-lease
☠️ 협업 시 위험성
git push -f를 실행하는 순간, 원격 저장소의 히스토리가 내 로컬 기준으로 강제로 덮어씌워집니다.
시나리오:
- 나: 로컬에서
git reset HEAD~3으로 커밋 3개를 되돌림 - 동료 A: 그 사이에 새로운 커밋을 원격에 push
- 나:
git push -f실행 - 결과: 동료 A의 커밋이 영구적으로 삭제되어 증발
이것이 협업 환경에서 force push가 위험한 이유입니다. 동료의 작업을 무의식적으로 지워버릴 수 있기 때문이죠.
안전한 Force Push: –force-with-lease
--force-with-lease는 “누가 그 사이에 푸시하지 않았을 때만 강제 푸시해줘”라는 안전장치가 달린 옵션입니다.
작동 원리
git push origin main --force-with-lease
이 명령어는 다음과 같이 동작합니다:
- 원격 브랜치의 현재 상태를 확인: 마지막으로 fetch한 시점의 원격 브랜치 상태를 기억
- 상태 비교: 현재 원격 브랜치가 기억한 상태와 같으면 push 허용
- 상태가 다르면 거부: 누군가 그 사이에 push했다면 push를 거부하고 오류 메시지 출력
실무에서의 사용 패턴
✅ 안전한 사용처
나 혼자 쓰는 Feature 브랜치: PR을 올리기 전, 커밋 메시지를 예쁘게 정리(rebase)하거나 오타를 수정(amend)한 뒤에는 자유롭게 사용해도 됩니다.
# Feature 브랜치에서 커밋 정리 후
git rebase -i HEAD~5
git push origin feature/my-feature --force-with-lease
❌ 절대 하지 말아야 할 것
- main/master 브랜치에 직접 force push: 팀 전체의 히스토리를 망가뜨릴 수 있습니다
- 다른 사람이 작업 중인 브랜치에 force push: 동료의 작업을 날릴 수 있습니다
Pro Tip
실무에서는 --force-with-lease를 습관화하는 것이 좋습니다. -f보다 안전하면서도 필요한 상황에서는 동일하게 작동합니다.
# alias로 설정하면 더 편리합니다
git config --global alias.pushf 'push --force-with-lease'
BFG Repo-Cleaner: 민감한 파일 완전 삭제
git rm으로 파일을 지우고 커밋해도, Git의 과거 히스토리(Log)에는 파일이 여전히 남아있습니다. 해커들은 이 로그를 뒤져 AWS Key, 비밀번호, API 토큰 등을 찾아냅니다.
문제 상황
# 실수로 비밀번호 파일을 커밋
git add secret.json
git commit -m "Add config"
git push
# 나중에 깨달음
git rm secret.json
git commit -m "Remove secret file"
git push
이렇게 해도 git log를 보면 과거 커밋에 secret.json이 그대로 남아있습니다. 누구나 git show [커밋해시]:secret.json으로 내용을 볼 수 있습니다.
해결책: BFG Repo-Cleaner
BFG Repo-Cleaner는 git filter-branch보다 10~720배 빠르고 사용법이 간단한 도구입니다.
1. BFG 다운로드
BFG 공식 사이트에서 bfg.jar를 다운로드합니다.
2. 저장소 클론 (bare repository)
BFG는 bare repository에서만 작동합니다.
# 원격 저장소를 bare로 클론
git clone --mirror https://github.com/username/my-repo.git my-repo.git
3. 민감한 파일 삭제
# 저장소의 모든 역사에서 'secret.json'을 찾아 삭제
java -jar bfg.jar --delete-files secret.json my-repo.git
# 여러 파일을 한 번에 삭제
java -jar bfg.jar --delete-files "{secret.json,config.json,password.txt}" my-repo.git
추가 옵션
# 특정 크기 이상의 파일 삭제 (예: 100MB 이상)
java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
# 특정 폴더 삭제
java -jar bfg.jar --delete-folders sensitive-data my-repo.git
⚠️ 주의사항
- 백업 필수: BFG는 히스토리를 영구적으로 변경합니다. 실행 전 반드시 백업하세요.
- 팀 협의: 공유 저장소라면 팀원들과 반드시 협의 후 진행하세요.
- 재클론 권장: 작업 후 팀원들은 저장소를 다시 클론하는 것이 안전합니다.
참고 자료
- Git 공식 문서:
git push,git reset,git rebase사용 방법 - BFG Repo-Cleaner 공식 문서: BFG 사용 예시와 주의사항
- Git Pro 2nd Edition: 히스토리 재작성(History Rewrite) 관련 장
실무 체크리스트
Force Push 사용 전 확인사항
- 혼자 쓰는 브랜치인가?
- 동료가 이 브랜치에서 작업 중이 아닌가?
--force-with-lease를 사용하는가?- main/master 브랜치가 아닌가?
민감한 파일 커밋 시 대응
- 즉시
git rm으로 삭제하고 커밋 - BFG Repo-Cleaner로 히스토리에서 완전 삭제
- 관련된 모든 키/토큰 재발급
- 팀원들에게 알리고 재클론 안내
결론
“실수는 누구나 한다. 하지만 흔적 없이 지우는 건 실력이다.”
Git은 강력한 타임머신 기능을 제공합니다. git commit --amend, git reset, git push -f, BFG Repo-Cleaner를 이해한다면, 어떤 실수를 하더라도 당황하지 않고 우아하게 대처할 수 있을 것입니다.
다만 협업 환경에서는 항상 신중해야 합니다. --force-with-lease를 습관화하고, 히스토리를 변경하기 전에는 팀원들과 소통하는 것이 가장 중요합니다.
궁금한 점이나 추가로 다뤘으면 하는 내용이 있다면 댓글로 남겨 주세요.
댓글남기기