LabHub
배우기 러닝패스 코스

Git実戦

トークンが履歴に入った — 消したのにまだ出てくる

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

이력에서 비밀 파일과 큰 파일을 실제로 들어냅니다. 그리고 들어낸 뒤에도 남는 것이 무엇인지까지 확인합니다.

왜 중요한가

git 은 내용 주소 저장소입니다. 파일 하나하나가 blob 객체가 되고, 그 blob 을 가리키는 트리와 커밋이 남아 있는 한 객체는 살아 있습니다. 그래서 git rm 은 "앞으로 없다" 는 뜻일 뿐 "없었던 일" 이 아닙니다. 비밀이 들어갔을 때 이 차이를 모르면, 지우고 커밋한 뒤 안심한 채로 토큰을 계속 쓰게 됩니다. 이 실습은 그 차이를 명령 출력으로 보여 주고, 실제로 없애는 순서(재작성 → 백업 참조 제거 → reflog 만료 → gc)를 손에 익힙니다. 마지막 단계가 결론입니다 — 이미 퍼진 사본에는 그대로 남으므로, 비밀은 청소가 아니라 폐기가 먼저입니다.

단계

  1. /root/gitx6/repo 에 토큰 파일과 900KB 덩어리가 섞인 이력을 만들고, 워킹 트리에서 지우는 커밋까지 넣습니다.
  2. 동료 사본 /root/gitx6/peer 를 떠 두고, 지금 상태를 notes/before.txt 에 남깁니다.
  3. git filter-branch 로 모든 ref 를 재작성합니다.
  4. 아직 객체가 남아 있는 이유를 notes/original.txt 에 적고 그 참조를 걷어냅니다.
  5. reflog 를 만료하고 gc --prune=now 로 실제로 지운 뒤 크기를 notes/after.txt 에 남깁니다.
  6. 해시가 전부 바뀐 사실을 notes/rewrite.txt 에 남깁니다.
  7. /root/gitx6/peer 를 새 이력으로 따라가게 하고, 그 사본에 비밀이 남아 있는지를 notes/collab.txt 에 적습니다.
  8. notes/report.md 에 사고 대응 순서를 정리합니다.

참고

토큰과 큰 덩어리가 섞인 이력 만들기

/root/gitx6/repo 에 토큰 파일과 900KB 덩어리가 섞인 이력을 만들고, 워킹 트리에서 지우는 커밋까지 넣습니다.

커밋 다섯 개를 쌓습니다 — 앱 파일, conf/prod.env, assets/blob.bin, 앱 수정, 그리고 git rm conf/prod.env 커밋. 중간에 side 브랜치를 하나 따고 주석 있는 태그 v1 도 붙이세요. 재작성이 모든 ref 를 건드린다는 것을 뒤에서 보려면 브랜치와 태그가 있어야 합니다. conf/prod.env 의 내용은 지시문 ## 참고 에 적힌 두 줄 그대로여야 합니다 — 뒤 단계가 그 blob 이 사라졌는지를 내용으로 확인하기 때문입니다.

지웠는데도 원문이 나온다

동료 사본 /root/gitx6/peer 를 떠 두고, 지금 상태를 notes/before.txt 에 남깁니다.

먼저 git clone /root/gitx6/repo /root/gitx6/peer재작성 전 사본을 떠 둡니다(7단계에서 씁니다). 그다음 git count-objects -vH 로 크기를 재고, git rev-list --objects --all 에서 두 경로가 나오는지 보고, git cat-file -p <커밋>:conf/prod.env 로 토큰 원문이 그대로 나오는 것까지 파일에 넣으세요. 지금 HEAD 해시도 적어 두어야 6단계에서 비교합니다.

모든 ref 를 재작성한다

git filter-branch 로 모든 ref 를 재작성합니다.

--index-filtergit rm -r --cached --ignore-unmatch <경로들> 을 주고, --prune-empty·--tag-name-filter cat·-- --all 을 함께 씁니다. 브랜치 하나만 주면 sidev1 이 옛 커밋을 붙잡은 채 남습니다. 10초 경고는 FILTER_BRANCH_SQUELCH_WARNING=1 로 넘깁니다.

아직 하나도 안 사라졌다

아직 객체가 남아 있는 이유를 notes/original.txt 에 적고 그 참조를 걷어냅니다.

재작성이 끝났는데도 git rev-list --objects --all 에 두 경로가 그대로 나옵니다. git for-each-ref refs/original 을 보면 이유가 보입니다 — filter-branch 가 되돌릴 수 있게 원래 ref 를 전부 백업해 둔 것입니다. 목록을 뽑아 git update-ref -d 로 하나씩 지우세요.

reflog 를 비우고 실제로 지운다

reflog 를 만료하고 gc --prune=now 로 실제로 지운 뒤 크기를 notes/after.txt 에 남깁니다.

reflog 도 참조입니다 — git reflog expire --expire=now --expire-unreachable=now --all 로 비웁니다. 그다음 git gc --prune=now 를 돌립니다. --prune=now 가 없으면 기본 유예 기간이 지나야 지워집니다. 전후 git count-objects -vH 를 나란히 적어 크기가 얼마나 줄었는지 보이게 하세요.

해시가 전부 바뀌었다

해시가 전부 바뀐 사실을 notes/rewrite.txt 에 남깁니다.

2단계에 적어 둔 옛 HEAD 해시를 지금 저장소에서 git cat-file -e 로 찾아 보세요 — 없습니다. 커밋 해시는 부모와 트리를 포함해 계산되므로, 이력 앞에서 하나를 고치면 그 뒤가 전부 달라집니다. 태그 v1 이 지금 어느 커밋을 가리키는지도 함께 적으세요.

이미 떠 간 사본에는 그대로 있다

/root/gitx6/peer 를 새 이력으로 따라가게 하고, 그 사본에 비밀이 남아 있는지를 notes/collab.txt 에 적습니다.

peer 에서 git fetch 하면 origin/main 이 새 이력으로 바뀝니다. git reset --hard origin/main 으로 따라가게 하세요. 그런 다음 그 사본에서 비밀 blob 을 git cat-file -e 로 찾아 보세요. 여기서 나오는 결론이 이 실습의 요점입니다 — 그래서 무엇을 먼저 해야 하는지 적으세요.

사고 대응 순서로 정리하기

notes/report.md 에 사고 대응 순서를 정리합니다.

번호 붙은 순서로 쓰세요. 무엇이 첫 번째인지가 가장 중요하고, 청소의 네 단계(재작성 · 백업 참조 제거 · reflog 만료 · gc)가 왜 그 순서인지도 한 줄씩 붙입니다. 팀에 무엇을 공지해야 하는지도 빠뜨리지 마세요.