100===Dev Ops/Git

Git 특정 커밋 히스토리 삭제 - 과거 기록 깔끔하게 정리하기 🧹

블로글러 2025. 4. 14. 13:33

혹시 실수로 중요한 비밀번호를 커밋해버렸거나, 지저분한 실험용 커밋들을 합치기 전에 정리하고 싶었던 적 없으신가요? Git은 기본적으로 모든 변경 기록을 차곡차곡 쌓아나가지만, 때로는 과거 기록을 수정해야 할 필요가 생기죠.

등장 배경

예전에는 Git 히스토리를 한번 만들면 되돌리기 어렵다고 생각했어요. 특히 여러 사람과 함께 작업하는 프로젝트에서는 히스토리 변경이 큰 혼란을 야기할 수 있었죠. 하지만 개인 브랜치에서 작업하거나, 팀원들과 합의된 상황에서는 히스토리를 깔끔하게 정리하는 것이 오히려 더 좋을 때가 많아요. 바로 이럴 때 git rebase -i가 강력한 해결사로 등장합니다! 🦸‍♂️

Git 히스토리 수정이 필요한 경우:

  1. 🤫 실수로 민감 정보 커밋: 비밀번호, API 키 등 공개되면 안 되는 정보를 커밋했을 때 해당 커밋을 삭제해야 합니다.
  2. 🧹 지저분한 히스토리 정리: 기능 개발 중 너무 잘게 쪼개진 커밋이나 실험적인 커밋들을 배포 전에 정리하고 싶을 때 사용합니다.
  3. ✨ 커밋 메시지 수정: 오타가 있거나 내용이 부족한 커밋 메시지를 수정하고 싶을 때도 활용할 수 있습니다.

핵심 원리: Interactive Rebase 마법 ✨

git rebase -i (interactive rebase)는 이름처럼 사용자와 상호작용하며 커밋 히스토리를 재정렬하거나 수정할 수 있는 강력한 명령어입니다. 특정 커밋을 삭제하는 과정을 단계별로 알아볼까요?

  1. Rebase 시작: 삭제하고 싶은 커밋 바로 이전 커밋의 해시(hash) 값을 이용해 rebase를 시작합니다. 만약 c703d02 커밋을 삭제하고 싶다면, 그 이전 커밋의 해시를 사용하거나, HEAD~N (N은 현재로부터 몇 번째 커밋까지 볼 것인지) 형식으로 범위를 지정할 수 있습니다. 사용하신 예시처럼 git rebase -i <삭제할 커밋의 부모 커밋 해시> 또는 git rebase -i <삭제할 커밋 범위의 시작점 직전 커밋 해시> 명령어를 입력합니다.

     # 예시: c703d02 커밋을 포함한 이후 커밋들을 수정 대상으로 삼기
     # c703d02의 부모 커밋 해시가 1234abcd 라고 가정
     git rebase -i 1234abcd
    
     # 또는 최근 5개의 커밋을 수정 대상으로 삼기
     git rebase -i HEAD~5
  2. 편집기 등장: 명령어를 실행하면 아래와 같이 편집기가 열리면서 rebase 대상 커밋 목록이 나타납니다. 각 줄은 명령어 커밋해시 커밋메시지 형식으로 되어 있습니다.

     pick a1b2c3d 초기 설정 추가
     pick e4f5g6h 중요한 기능 개발  # 이 커밋을 삭제하고 싶다고 가정!
     pick i7j8k9l 버그 수정
    
     # Rebase <시작 커밋 해시>..<현재 커밋 해시> onto <시작 커밋 해시>
     #
     # Commands:
     # p, pick <commit> = use commit
     # r, reword <commit> = use commit, but edit the commit message
     # e, edit <commit> = use commit, but stop for amending
     # s, squash <commit> = use commit, but meld into previous commit
     # f, fixup <commit> = like "squash", but discard this commit's log message
     # x, exec <command> = run command (the rest of the line) using shell
     # b, break = stop here (continue rebase later with 'git rebase --continue')
     # d, drop <commit> = remove commit # <--- 바로 이 명령어!
     # l, label <label> = label current HEAD with a name
     # t, reset <label> = reset HEAD to a label
     # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
     # .       create a merge commit using the original merge commit's
     # .       message (or the oneline, if no original merge commit was
     # .       specified). Use -c <commit> to re-edit the commit message.
     #
     # These lines can be re-ordered; they are executed from top to bottom.
     # ... (이하 생략) ...
  3. 커밋 삭제: 삭제하고 싶은 커밋 앞의 pick 명령어를 drop으로 변경하거나, 해당 줄 자체를 삭제합니다.

     pick a1b2c3d 초기 설정 추가
     drop e4f5g6h 중요한 기능 개발  # 'pick'을 'drop'으로 변경!
     pick i7j8k9l 버그 수정

    또는

     pick a1b2c3d 초기 설정 추가
     # e4f5g6h 중요한 기능 개발  <-- 해당 라인을 아예 지워버려도 됩니다!
     pick i7j8k9l 버그 수정
  4. 저장 및 종료: 편집기를 저장하고 닫습니다. (Vim 편집기라면 :wq 입력 후 Enter)

  5. Rebase 진행: Git이 지정된 작업(drop 또는 줄 삭제)을 수행하며 커밋을 재구성합니다. 충돌(Conflict)이 발생하지 않으면 자동으로 완료됩니다. 충돌이 발생하면, Git이 알려주는 대로 충돌을 해결하고 git add .git rebase --continue를 실행해야 합니다. 만약 rebase를 취소하고 싶다면 git rebase --abort를 사용하세요.

  6. 원격 저장소 강제 푸시 (Force Push): 로컬 저장소의 히스토리가 변경되었으므로, 원격 저장소(예: GitHub, GitLab)의 히스토리와 달라졌습니다. 변경된 히스토리를 원격 저장소에 반영하려면 git push --force 또는 git push --force-with-lease 명령어를 사용해야 합니다. ⚠️ 이 작업은 매우 주의해야 합니다!

     # 변경된 로컬 히스토리를 원격 저장소(dev 브랜치)에 강제로 덮어쓰기
     git push origin dev --force
     # 또는 조금 더 안전한 force-with-lease 사용
     git push origin dev --force-with-lease

주의사항 및 팁 💡

⚠️ 이것만은 주의하세요!

  1. Force Push의 위험성: git push --force는 원격 저장소의 히스토리를 강제로 덮어씁니다. 만약 다른 팀원이 이미 이전 히스토리를 기반으로 작업을 하고 있었다면, 그들의 작업 내역과 충돌하거나 유실될 수 있습니다! 😱
    • 상세 설명: 공유된 브랜치(예: main, master, dev)에 대해서는 절대 함부로 force push를 사용하면 안 됩니다.
    • 해결 방법:
      • 개인 브랜치에서만 rebase -iforce push를 사용하세요.
      • 꼭 공유 브랜치의 히스토리를 수정해야 한다면, 모든 팀원과 충분히 소통하고 동의를 얻은 후에 진행하세요.
      • --force-with-lease 옵션은 원격 브랜치가 내가 마지막으로 pull 받은 이후 변경되지 않았을 때만 push를 허용하므로 --force보다 약간 더 안전합니다.
  2. Rebase 중 충돌: Rebase 과정에서 커밋들이 재적용될 때 코드 충돌이 발생할 수 있습니다.
    • 상세 설명: 삭제하려는 커밋 이후의 커밋들이 삭제된 커밋의 코드 변경 사항에 의존하고 있었다면 충돌이 발생합니다.
    • 해결 방법: Git이 알려주는 충돌 파일을 열어 직접 수정하고, git add <수정한 파일> 명령어로 스테이징한 뒤, git rebase --continue 명령어로 rebase를 계속 진행합니다. 해결이 어렵다면 git rebase --abort로 rebase를 취소할 수 있습니다.

💡 꿀팁

  • 되돌리기: 만약 rebase를 잘못했다면? git reflog 명령어로 로컬 저장소의 HEAD 변경 이력을 확인하고, git reset --hard <원하는 시점의 해시> 명령어로 이전 상태로 되돌릴 수 있습니다. (단, force push 이후라면 원격 저장소는 이미 변경된 상태일 수 있습니다.)
  • 대안: git revert: 이미 다른 사람들과 공유된 히스토리의 특정 변경 사항을 되돌리고 싶다면, 히스토리를 삭제하는 rebase 대신 git revert <취소할 커밋 해시>를 사용하는 것이 안전합니다. revert는 특정 커밋의 변경 내용을 거꾸로 적용하는 _새로운 커밋_을 생성하므로 히스토리가 보존됩니다.
  • 민감 정보 완전 삭제: 단순 rebase로는 히스토리에서만 보이지 않을 뿐, Git 데이터베이스 어딘가에 흔적이 남을 수 있습니다. 민감 정보를 완전히 제거하려면 git filter-branchbfg-repo-cleaner 같은 전문 도구를 사용하는 것이 좋습니다. (이 도구들은 사용법이 더 복잡합니다.)

마치며

지금까지 git rebase -i를 사용하여 Git 커밋 히스토리에서 특정 기록을 삭제하는 방법을 알아보았습니다. 처음에는 조금 복잡하게 느껴질 수 있지만, 몇 번 연습해보면 Git 히스토리를 훨씬 깔끔하고 효율적으로 관리할 수 있게 될 거예요! 😎 특히 force push의 위험성을 항상 기억하고 신중하게 사용하는 것이 중요합니다.

혹시 이 과정에서 궁금한 점이나 더 알고 싶은 부분이 있으신가요? 댓글로 자유롭게 질문해주세요! 🙋‍♀️

참고 자료 🔖


#Git #Rebase #CommitHistory #버전관리 #개발팁 #깃허브 #Git커밋삭제 #InteractiveRebase

728x90
반응형