git / GitHub의 기록에서 폴더와 그 내용을 제거하십시오


318

내 GitHub 계정의 리포지토리에서 작업하고 있는데 이것이 우연히 발견 된 문제입니다.

  • 몇 개의 npm 패키지가 설치된 폴더가있는 Node.js 프로젝트
  • 패키지는 node_modules폴더에 있었다
  • 해당 폴더를 git 저장소에 추가하고 코드를 github로 푸시했습니다 (당시 npm 부분에 대해서는 생각하지 않았습니다)
  • 해당 폴더가 코드의 일부가 될 필요는 없다는 것을 깨달았습니다.
  • 해당 폴더를 삭제하고 푸시

이 경우 총 git repo의 크기는 약 6MB 였고 실제 코드 (해당 폴더 제외)는 약 300KB 였습니다 .

이제 마지막으로 찾고있는 것은 git의 기록에서 해당 패키지 폴더의 세부 정보를 제거하는 방법이므로 누군가 복제 할 경우 실제 파일이 얻을 수있는 6MB의 기록을 다운로드 할 필요가 없습니다. 마지막 커밋은 300KB입니다.

가능한 해결책을 찾고이 두 가지 방법을 시도했습니다.

Gist는 스크립트를 실행 한 후 해당 폴더를 제거하고 50 개의 다른 커밋이 수정되었음을 보여준 곳에서 작동하는 것처럼 보였다. 그러나 그것은 그 코드를 밀어 넣지 못했습니다. 내가 밀어하려 할 때, 말했다 Branch up to date하지만 50 개 커밋이에 수정 된 보였다 git status. 다른 두 가지 방법도 도움이되지 않았습니다.

이제 폴더의 기록을 제거했음을 보여 주었지만 로컬 호스트에서 해당 리포지토리의 크기를 확인할 때 여전히 약 6MB였습니다. (또한 refs/original폴더를 삭제 했지만 저장소 크기의 변화를 보지 못했습니다).

내가 분명히하고 싶은 것은 커밋 히스토리 (내가 생각한 유일한 것임)뿐만 아니라 git이 롤백하고 싶다고 가정하는 파일을 제거하는 방법이 있는지입니다.

해결책이 제시되어 내 로컬 호스트에 적용되었지만 해당 GitHub 저장소로 재현 할 수 없다고 가정 해보십시오. 해당 저장소를 복제하고 첫 번째 커밋으로 롤백하여 트릭을 수행하고 푸시 할 수 있습니까 (또는 git이 여전히 모든 커밋의 역사가 있습니까?-일명 6MB).

내 최종 목표는 기본적으로 git에서 폴더 내용을 제거하는 가장 좋은 방법을 찾는 것입니다. 따라서 사용자는 6MB 상당의 자료를 다운로드 할 필요가 없으며 여전히 모듈 폴더를 건드리지 않은 다른 커밋을 가질 수 있습니다 (예 : git의 역사에서).

어떻게해야합니까?


3
아래 답변 중 하나라도 문제를 해결 한 경우 질문에 대한 답변으로 수락하는 것이 좋습니다. meta.stackexchange.com/questions/5234/...는
starbeamrainbowlabs

답변:


556

코드를 복사하여 붙여 넣기하려면 :

이것은 node_modules역사에서 제거하는 예입니다

git filter-branch --tree-filter "rm -rf node_modules" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

자식이 실제로하는 일 :

첫 번째 줄은 --tree-filterHEAD (현재 분기) 와 동일한 트리 ( ) 에서 모든 참조를 반복 하여 명령을 실행합니다 rm -rf node_modules. 이 명령 은 사용자에게 프롬프트를 표시하지 -r않고 node_modules 폴더를 삭제합니다 ( ,없는 -r경우 rm폴더를 삭제하지 않음 -f). 추가 --prune-empty하면 쓸모없는 (아무것도 변경하지 않음) 커밋이 재귀 적으로 삭제됩니다.

두 번째 줄은 이전 분기에 대한 참조를 삭제합니다.

나머지 명령은 비교적 간단합니다.


3
참고 사항 : git count-objects -v파일이 실제로 제거되었는지 확인하는 데 사용 되었지만 저장소의 크기를 다시 복제 할 때까지 저장소의 크기는 동일하게 유지됩니다. Git은 내가 생각하는 모든 원본 파일의 사본을 관리합니다.
Davide Icardi

4
비 고대 git을 사용하면 아마도 --force-with-leasenot 이어야합니다 --force.
Griwes

4
이러한 명령은 Windows에서 작동하지 않습니다. 또는 적어도 Windows 10이 아닌 "잘라 내기 및 붙여 넣기"가 작동하는 OS를 게시하십시오
David

3
Windows 10 사용자의 경우 Windows 용 Bash에서 잘 작동합니다 (Ubuntu 사용)
Andrej Kyselica

3
Windows 쉘과 git bash로 시도했지만 작동하지 않았습니다. 첫 번째 명령 통과, 두 번째 명령 실패!
Mohy Eldeen의

240

나는 것을 발견 --tree-filter다른 답변에서 사용되는 옵션은 특히 커밋의 많은 큰 저장소에 매우 느릴 수 있습니다.

다음은 --index-filter옵션을 사용하여 git history에서 디렉토리를 완전히 제거하는 데 사용하는 방법입니다 .

# Make a fresh clone of YOUR_REPO
git clone YOUR_REPO
cd YOUR_REPO

# Create tracking branches of all branches
for remote in `git branch -r | grep -v /HEAD`; do git checkout --track $remote ; done

# Remove DIRECTORY_NAME from all commits, then remove the refs to the old commits
# (repeat these two commands for as many directories that you want to remove)
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch DIRECTORY_NAME/' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Ensure all old refs are fully removed
rm -Rf .git/logs .git/refs/original

# Perform a garbage collection to remove commits with no refs
git gc --prune=all --aggressive

# Force push all branches to overwrite their history
# (use with caution!)
git push origin --all --force
git push origin --tags --force

gcwith 전후에 리포지토리의 크기를 확인할 수 있습니다 .

git count-objects -vH

3
왜 이것이 더 빠른지 설명 할 수 있습니까?
knocte

7
@knocte : 문서에서 ( git-scm.com/docs/git-filter-branch ) "--index-filter : ... 트리 필터와 비슷하지만 트리를 체크 아웃하지 않아 훨씬 빠릅니다."
Lee Netherton

23
이것이 왜 대답이 아닌가? 너무 철저합니다.
Mad Physicist

2
Windows에서이 작업을 수행하는 경우 작은 따옴표 대신 큰 따옴표가 필요합니다.
Kris Morness

12
--quietgit rm내용을 전달 하면서 적어도 4 번 요소로 재 작성을
재개했습니다

46

의 인기있는 답변 외에도 Windows 시스템에 대한 몇 가지 메모를 추가하고 싶습니다 . 명령

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • 수정 없이 완벽하게 작동합니다 ! 따라서, 당신은 안 사용 Remove-Item, del또는 대신 다른 것 rm -rf.

  • 당신은 파일이나 디렉토리 사용에 대한 경로를 지정해야하는 경우 슬래시 등이./path/to/node_modules


디렉토리에가 포함되어 있으면 Windows에서 작동하지 않습니다. 이름에 (점).
Corneliu Serediuc

4
그리고 해결책을 찾았습니다. "rm -rf node.modules"와 같이 rm 명령에 이중 반전 쉼표를 사용하십시오.
Corneliu Serediuc

23

내가 찾은 가장 정확하고 가장 정확한 방법은 bfg.jar 파일을 다운로드하는 것입니다 : https://rtyley.github.io/bfg-repo-cleaner/

그런 다음 명령을 실행하십시오.

git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME  # i.e. 'node_modules' in other examples
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository

파일을 삭제하려면 대신 delete-files 옵션을 사용하십시오.

java -jar bfg.jar --delete-files *.pyc

1
매우 쉬운 :) 특정 폴더 만 제거되도록하려는 경우 다음과 같이 도움이됩니다. stackoverflow.com/questions/21142986/…
emjay

9

그것은이에 대한 최신 대답하는 것입니다 표시 하지 사용할 filter-branch직접 (적어도 자식에서 자체는 더 이상 권장하지 않습니다), 그리고 연기 외부 도구로 작업하는. 특히 git-filter-repo 가 권장됩니다. 해당 도구의 작성자는 직접 사용 이 문제를 일으킬 수있는 이유에 대한 논쟁제공filter-branch 합니다.

dir기록에서 제거하기위한 위의 여러 줄 스크립트는 다음과 같이 다시 작성할 수 있습니다.

git filter-repo --path dir --invert-paths

이 도구는 그보다 강력합니다. 작성자, 이메일, refname 등을 기준으로 필터를 적용 할 수 있습니다 ( 전체 맨 페이지는 여기 ). 또한 빠릅니다 . 설치가 간편합니다 . 다양한 형식으로 배포됩니다 .


좋은 도구! 우분투 20.04에서 잘 작동합니다. pip3 install git-filter-repostdlib 전용이므로 종속성을 설치하지 않습니다. Ubuntu 18에서는 배포판의 git 버전과 호환되지 Error: need a version of git whose diff-tree command has the --combined-all-paths optiondocker run -ti ubuntu:20.04
않지만

7

테스트 후 주석 (복사-붙여 넣기 솔루션)에 명령을 추가하여 복사 및 붙여 넣기 레시피를 완료하십시오.

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

그런 다음 .gitignore에서 "node_modules /"행을 제거 할 수 있습니다.


왜 다음 제거 할 node_modules에서 .gitignore? 그들이 실수로 다시 커밋 될 수 있도록 ??
Adamski

1
gitignore에서 제거되지 않고 gitignore에 추가됩니다. 커밋 메시지가 없습니다 "gitignore":) 말한다 "자식 역사"
대니 Tuppeny

그러나 의견은 node_modules에서 제거 할 수 있다고 말합니다 .gitignore.
zavr

7

Windows 사용자의 경우 다른 백업이 이미있는 경우 명령을 강제 실행하기 위해 추가됨 "대신에 사용하십시오 .'-f

git filter-branch -f --tree-filter "rm -rf FOLDERNAME" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo FOLDERNAME/ >> .gitignore
git add .gitignore
git commit -m "Removing FOLDERNAME from git history"
git gc
git push origin master --force

3

Windows의 git을 사용하여 오래된 C # 프로젝트에서 bin 및 obj 폴더를 제거했습니다. 조심해

git filter-branch --tree-filter "rm -rf bin" --prune-empty HEAD

git install 폴더에서 usr / bin 폴더를 삭제하여 git 설치의 무결성을 파괴합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.