FrontEnd Develop/Project : AI TUTOR

.env를 모르고 올렸을때의 대처 : Git Filter-Repo 이후 충돌 해결 및 복구 시나리오

Frisbeen 2025. 7. 4. 22:12

큰일난 상황

우리 팀은 .env 파일이 Git 커밋 히스토리에 올라간 상황을 발견했다. 해당 파일은 민감한 환경변수 정보를 담고 있었고, 단순히 .gitignore 처리로는 과거 커밋 내역에서 제거되지 않기 때문에 근본적인 해결이 필요했다.

결국 회의 결과 우리는 다음 명령어로 Git 히스토리를 완전히 재작성하게 된다:

git filter-repo --path .env --invert-paths --force

 

이 명령은 .env가 포함된 모든 커밋을 히스토리에서 제거하고, 커밋 SHA를 전면 수정한다.

 

결과적으로 develop브랜치 (default branch) 는 전혀 다른 히스토리로 바뀌게 되었고, 기존의 어떤 브랜치와도 공통 조상을 가지지 않는 상태가 된다.

 

이때 유라님은 기존에 작업하던 커밋들을 로컬에서 그대로 보유하고 있었고, 모르고 forced push가 된 우리의 default 브랜치는 

유라님 입장에서는 훼손된 브랜치가 되었다.

 

즉, 유라님이 git pull을 한 이후 리모트 develop과의 연결이 끊긴 상태가 된 것.

 

PR을 생성하려 해도 GitHub에서 “비교 대상이 완전히 다릅니다”는 메시지가 출력되며, 기존 작업을 반영할 방법이 없어졌다.

완전히 다른 이유는 forced Push로 인해, 커밋 SHA가 전면 수정되었기 때문이다.


설계한 유라님의 복구 여정

  1. 유라님은 실수로 git pull을 한 후 로컬 develop이 나의 강제 푸시 결과로 바뀐 상태였지만,
  2. 다행히 git reflog를 통해 pull 직전 HEAD를 확인할 수 있었다.
  3. 그 시점을 기준으로 다음 명령어로 복구 브랜치를 만들었다.
git checkout -b restore/yura-final
git reset --hard HEAD@{1}  # pull 이전 상태로 복원

-> 이 브랜치는 유라님의 작업 커밋들이 포함된 상태이다.


충돌 없는 상태를 만들기 위한 유라님의 로컬에도 filter-repo 적용

유라님은 나와 동일한 환경을 만들기 위해, 자신이 복구한 브랜치 위에 다음 명령어를 실행했다.

git filter-repo --path .env --invert-paths --force

이제 restore/yura-final 브랜치도 .env를 포함하지 않게 되었고,

내가 forced Push했던 develop 브랜치와 동일한 기준에서 비교가 가능해졌다.


이후의 충돌 해결 플랜

선택지 1: Rebase 방식

유라님의 restore branch의 커밋 사항들을 default branch에 쌓는 방식이다.

완전히 새 커밋을 만들어 두 브랜치의 히스토리를 강제로 fast-forward처럼 “만들어주는” 방식이겠다.

따라서 결과적으로는 유라님과 히스토리를 공유할 수 있게 된다.

git rebase origin/develop

 

그러나

만약 이 방식으로 rebase를 진행하면 유라님의 커밋들이 내 develop 커밋 위에 쌓이게 되는데,

이때 매우 높은 확률로 충돌이 발생할 것이다. Git이 멈추고 수동 해결을 요구하게 된다.

git status           # 충돌 파일 확인
# 각 파일을 열어 충돌 마커(<<<<<<<) 정리

git add .
git rebase --continue

선택지 2: Cherry-pick 방식 (안전하고 선별적)

git checkout -b yura/clean-pr origin/develop

git cherry-pick <유라님의 커밋 해시들>
  • 이 방식은 .env가 포함된 커밋은 제외하고, 꼭 필요한 커밋만 선택적으로 이식할 수 있음

PR 생성 및 병합

최종 브랜치 (yura/clean-pr)가 완성되면:

git push origin yura/clean-pr

 

GitHub에서 develop을 대상으로 한 PR을 생성하면 드디어 정상적인 비교가 작동하겠다.


결론 : 하지 않아도 됐던  초고난도 git 충돌 해결이었다.

사실 env를 실수로 올리지 말았어야했고, git filtering 말고 좀 더 간단한 방법이 있을 수도 있다.

그러나, git filtering은 명령어 한 줄로 git history를 바꿀 수 있는 매우 강력하지만 위험한 방식이기에 잘 handle하면

매우 강력한 도구라고 생각한다.

 

어찌됐건, 이 모든 작업은 단순한 충돌 해결이 아니라, 커밋 히스토리를 완전히 재작성하고 다시 이어붙이는 과정이었다. 한 명은 보안을 위해 과감하게 필터링을 감행했고, 다른 한 명은 그 이후 잃어버린 커밋을 끝까지 복구해냈다.

 

서로의 커밋이 살아 있는 상태로 develop 히스토리를 하나로 정리한 이 시나리오는 Git 협업에서 드물게 발생하는 고난이도의 병합 사례였다.