Server & Infra

Git에 잘못 올린 파일 삭제하기

리차드 2022. 8. 29. 04:19

 

Git에 잘못 올린 파일을 삭제한다 라는 표현은 다소 애매모호합니다.
하나의 케이스는 단순히 형상관리 대상에서 제외하기 위한 작업을 의미할 수 있고,
또다른 케이스는 모든 히스토리에서 완전히 제거하기 위한 작업을 의미할 수도 있습니다.
이번 포스팅에선 위 두 가지 케이스 경우 어떻게 대응해야할 지 알아봅니다.

 

 

git rm : 형상 관리 대상에서 제외하기


Remove files from the working tree and from the index

 

IDE 설정들을 담는 .idea 내 파일들,
java 파일을 빌드한 결과들인 .class 파일 등은 형상 관리 대상이 아닙니다.

이처럼 형상 관리 대상이 아닌 파일들을 실수로 staging하여
commit, push까지 한 경우, 이들을 형상 관리 대상에서 제외하는 방법을 먼저 알아보겠습니다.

사용할 명령어는 git rm 입니다.
옵션으로 --cached 를 추가할 경우, 로컬 파일은 남겨두게 됩니다.

# 로컬에서도 삭제하기 원할 경우
git rm {경로/파일명}

# 로컬에서는 남겨두기 원할 경우
git rm --cached {경로/파일명}

 

 

git rm --cached
after git rm -cached

위 이미지는 git rm 명령어로 application.yml 파일을 index에서 제거한 모습입니다.
이 경우, Changes를 보면, index에서 파일이 삭제되었고, (형상 관리 대상에서 제외되었고)
또한 --cached 옵션으로 인해 로컬에선 파일이 남아있어서
application.yml 파일이 Unversioned 로 나타나고 있는 모습입니다.

 

 

.gitignore

.class 파일처럼 단순히 형상관리 대상이 아닌데 잘못 올려서
향후 형상관리 대상에서 제외하기만 하면 되는 경우,
.gitignore 설정을 추가하여 Unversioned 에서도 사라진 모습을 확인한 후,
commit, push하면 작업이 완료됩니다.

 

 

 

git filter-branch 를 이용한 히스토리 완전 제거


Rewrite branches

 

git rm 명령어는 이전 히스토리를 제거하진 않습니다.
따라서 실수로 민감정보가 담긴 application.yml 파일을 
Github과 같은 리모트에 public으로 push할 경우, 이에 대한 대응으로는 충분하지 않습니다.
이전 커밋 이력에서는 해당 파일 내용을 확인할 수 있기 때문이죠.

이 경우에는 해당 파일이 포함된 커밋 이력을 모두 수정해야만 합니다.
또한 여러 브랜치를 운영하고 있다면, 그 브랜치들에 대해서도 모두 그러해야합니다.
git 공식문서에서는 completely forget a file 이라는 표현을 사용합니다.

물론, 민감 정보가 정말 public 상태의 리모트에 push된 적이 있다면,
해당 정보는 유출되었다고 가정하고 접속 정보를 파기하거나, 변경하는 게 맞습니다.
그러나 히스토리에서 삭제하는 작업도 동시에 필요하므로 이에 대해 알아보겠습니다.

사용할 명령어는 다음과 같습니다.

git filter-branch -f --prune-empty --index-filter "git rm -r --cached --ignore-unmatch **/application.yml" HEAD

 

filter-branch 명령어는 굉장히 주의해서 다뤄야 하는 명령입니다.
약간은 귀엽기도 한 버그가 발생하기도 한다는 내용도 포함되어있고,
Safety 항목에는 상당히 많은 고지와 주의들이 담겨있습니다.

하지만 필요할 때는 다소 위험한 도구라도 주의해서 사용해야겠지요!
하나씩 옵션을 살펴보겠습니다.

-f 는 filter-branch 명령의 수행을 강제하는 옵션입니다.
임시 디렉토리가 존재할 경우 명령 실행이 거절되는데, 이를 방지하기 위한 옵션입니다.

--prune-empty 는 빈 커밋을 제거하는 옵션입니다.
히스토리 삭제 대상 파일 하나에 대해서만 변경이 적용된 커밋이 있을 경우,
이 파일의 히스토리가 제거되며 그 커밋 자체가 비어있게 될 수 있습니다.
이 커밋은 추후 혼란을 야기할 수 있기에 완전히 제거해버리는 것입니다.

--index-filter <command>는 rewrite 할 인덱스들을 필터링하기 위한 옵션입니다.
뒤에 나오는 command 에서는 앞서 살펴본 형상관리 대상에서 제외하는 명령인데요,
모든 브랜치들을 순회하면서 해당 명령이 수행됐을 경우, 인덱스들을 재작성하게 됩니다.

 

filter-branch
application.yml is gone
push after filter-branch

실제 명령을 수행한 결과입니다.

application.yml파일을 다른 파일들과 함께 커밋했고,
그 이후 두 개 정도의 추가 커밋을 수행한 뒤에,
application.yml 파일을 히스토리에서 제거하기 원한다는 시나리오로 가정해보았습니다.

이 때, application.yml 파일이 생긴 시점 이후로 모든 커밋들을 새롭게 Rewrite하게 됩니다.
Rewrite 된 커밋들의 커밋 그래프 상 색상이 바뀐 걸 볼 수 있습니다.

또한 push 를 하려 하면, 파일이 변경된 내역은 없음에도,
Rewrite 된 커밋들을 push할 수 있게 나타납니다.
force push로 기존 커밋들을 덮어버리면 완료입니다.

 

 

 

학습 출처


how-to-delete-file-on-git

git-filter-branch