문제 발단
GitHub에 보안 파일이 실수로 올라간 적이 있었다.
불행하게도, 이 파일이 올라간 커밋 이후 다른 개발자의 pc에서 새로운 커밋들이 마구마구 됐었다.
실수로 올라간 보안 파일을 지우고, 다시 커밋해도 GitHub엔 History 버튼을 누르면 파일 안의 패스워드 등을 그대로 확인할 수 있다.
포스팅 목적
이 포스팅은 보안 문제가 될 수 있는 패스워드, DB 주소 등이 실수로 리포지토리에 올라갔을 때
그 파일의 히스토리까지 완벽하게 삭제하고, 그 이후의 문제까지 다룬다.
해결 방법
전체적인 흐름은 터미널을 이용해 보안 파일에 해당하는 git log 파일을 삭제하고, 이 파일이 포함된 모든 커밋 기록을 변경한다.
삭제하려고 하는 파일은 `/Users/Github/my_repository/analysis/테스트분석.ipynb` 파일이다.
* 이 작업을 수행하기 전에, 해당 파일이 있는 브랜치로 check out 확인 후 수행한다.
사전 작업 1. 터미널에서 리포지토리의 최상위 디렉터리로 이동한다. (ex. `/Users/.../Github/my_repository`)
cd /Users/Github/my_repository
이렇게 하는 이유는 수행하려는 git 명령어 작업은 최상위 디렉터리에서만 가능하기 때문이다.
사전 작업 2. 작업하려는 브랜치로 전환한다.
git checkout [브랜치 이름]
Step 1. 로컬 저장소와 원격 저장소의 브랜치를 동기화한다.
git pull origin [브런치 이름]
Step 2. 삭제할 파일의 이름을 갖고 아래 명령어를 입력하자.
(파일 이름엔 "최상위 디렉터리로부터 삭제할 파일의 경로까지" 입력돼야 한다. 이 글에선 analysis 폴더 안에 있는 `테스트분석.ipynb` 파일을 제거하려고 한다.)
git filter-branch -f --index-filter 'rm --cached --ignore-unmatch analysis/테스트분석.ipynb' --prune-empty -- --all
또는
git filter-branch --tree-filter 'rm -f analysis/테스트분석.ipynb' --prune-empty HEAD
이 두 가지 명령어의 차이는 맨 아래에서 설명하겠다.
Step 3. 원격 브랜치를 강제로 업데이트한다.
git push --force origin [브랜치 이름]
이 작업 후, GitHub 웹의 리포지토리로 이동해서 해당 파일의 히스토리가 삭제됐는지 확인한다.
만약, 보안 파일 업로드 후, 다른 개발자들이 같은 브런치에 커밋을 했다면, 다른 개발자의 PC에서 힘들게 삭제한 파일의 히스토리가 있을 수 있다. 이 경우, 이 보안 파일의 히스토리(git log)가 스테이징 되어 본의 아니게 다른 커밋들과 함께 원격 저장소에 커밋되면 다시 원점으로 돌아가버린다.
이런 경우엔 Step 4를 시도한다.
Step 4. (다른 개발자 PC) 원격 저장소와 동기화 후 파일의 히스토리가 삭제됐는지 확인한다.
git log -- analysis/테스트분석.ipynb
만약, log가 있다면, 원격 저장소와 동기화가 제대로 되지 않은 것이므로 다시 동기화를 해준다.
git pull origin [브랜치 이름]
만약 위와 같은 동기화 문제에도 해결되지 않는다면, 원격 저장소를 지우고 다시 복제하는 과정을 거쳐야 한다.
git clone [리포지토리 url]
git checkout [해당 브랜치 이름]
git pull origin [해당 브랜치 이름]
git 명령어 설명
1) git filter-branch -f --index-filter 'rm --cached --ignore-unmatch analysis/테스트분석.ipynb' --prune-empty -- --all
2) git filter-branch --tree-filter 'rm -f analysis/테스트분석.ipynb' --prune-empty HEAD
- `filter-branch`
- Git 저장소의 커밋 기록을 변경하거나 필터링하는데 사용되는 Git 명령어. 이 명령어를 사용하면 Git의 커밋 기록에서 원치 않는 파일을 삭제하거나, 파일의 내용을 수정하거나, 커밋 메시지를 변경하는 등의 작업을 수행할 수 있음. 이미 다른 사용자들이 이용 중인 저장소에서 사용하지 않는 것이 좋다.
- `--tree-filter`
- 필터링 작업을 워킹 디렉토리를 기준으로 수행한다. 즉, 필터링 작업 중에 워킹 디렉토리에서 파일을 삭제하거나 추가하는 등의 변경 작업을 수행할 수 있다. 이 방식은 Git의 인덱스를 건드리지 않기 때문에 필터링 작업 중에 다른 파일들의 변경 내역이 무시되지 않도록 유의해야 한다.
- `--index-filter`
- 필터링 작업을 Git의 인덱스를 기준으로 수행한다. 즉, 필터링 작업 중에 Git의 인덱스에서 파일을 삭제하거나 추가하는 등의 변경 작업을 수행. 이 방식은 Git의 커밋 기록을 수정할 때 인덱스에 있는 내용을 기반으로 작업을 수행하기 때문에, 워킹 디렉토리의 변경 내역이 포함되지 않음.
- `--tree-filter` vs `--index-filter`
- 어떤 옵션을 선택할지는 작업의 목적과 필요에 따라 달라질 수 있다. 만약 작업 중에 워킹 디렉토리의 변경 내역을 포함해야 할 경우에는 --tree-filter를 선택하고, 작업 중에 워킹 디렉토리를 건드리지 않고 인덱스를 기준으로 작업을 수행해야 할 경우에는 --index-filter를 선택한다.
- `'rm -f analysis/테스트분석.ipynb'`
- 워킹 디렉토리에서 analysis 폴더 안의 '테스트분석.ipynb' 파일을 삭제하는 명령어
- -f는 --force 옵션의 의미와 같이 Git 작업 중 발생하는 충돌을 무시하고 작업을 강제로 수행함을 의미한다.
- `--cached`
- Git의 인덱스나 워킹 디렉토리에서 삭제할 파일을 찾을 수 없을 경우에도 오류를 발생시키지 않고 넘어가도록 한다. 즉, 해당 파일이 없을 경우에는 그냥 무시하고 계속해서 작업을 수행.
- `--ignore-unmatch`
- Git의 인덱스에서 파일을 삭제할 때 사용. 즉, Git 저장소에서 파일을 삭제하는 것이 아니라, 해당 파일을 Git의 인덱스에서만 삭제. 이를 통해 워킹 디렉토리에서는 파일이 유지되면서, Git의 인덱스에서만 파일이 삭제되는 작업을 수행.
- --index-filter와 함께 사용
- `--prune-empty`
- 빈 커밋을 제거하는 명령어
- `--all`
- Git 저장소의 모든 브랜치와 태그에 대해 작업을 수행
- --index-filter 인수를 사용할 때, --all 앞의 `--`
(git filter-branch -f --index-filter 'rm --cached --ignore-unmatch analysis/테스트분석.ipynb' --prune-empty -- --all)- git filter-branch 명령어에서 revision range를 나타내는 역할. 이 구분자 이후 오는 모든 인수는 filter-branch의 명령의 인수로 간주. 즉, `--prune-empty -- --all` 명령은 Git 저장소의 모든 브랜치와 태그를 대상으로 하되, 빈 커밋을 제거하는 `--prune-empty` 옵션을 함께 적용하도록 지시하는 것.
- `HEAD`
- 현재 브랜치의 모든 커밋에 대해 작업을 수행
'DevOps > Git & GitHub' 카테고리의 다른 글
[Git] 협업을 위한 Commit Convention (Conventional Commits) (0) | 2023.05.08 |
---|