Git 리포지토리의 커밋 기록에서 큰 파일을 제거 / 삭제하는 방법은 무엇입니까?


708

때때로 나는 DVD-rip을 웹 사이트 프로젝트에 떨어 뜨린 다음, 부주의하게 git commit -a -m ..., 그리고 zap, repo는 2.2 기가 늘어났다. 다음에 편집 할 때 비디오 파일을 삭제하고 모든 것을 커밋했지만 압축 파일은 여전히 ​​저장소의 저장소에 있습니다.

나는 그 커밋에서 지점을 시작하고 한 지점을 다른 지점으로 리베이스 할 수 있다는 것을 알고 있습니다. 그러나 큰 파일이 히스토리에 표시되지 않고 가비지 수집 절차에서 정리되도록 두 커밋을 병합하려면 어떻게해야합니까?




1
큰 파일이 하위 디렉토리에 있으면 전체 상대 경로를 지정해야합니다.
Johan


아래의 많은 답변이 BFG보다 쉬운 것으로 선전 git filter-branch하지만 그 반대가 사실이라고 생각했습니다.
2540625

답변:


605

Git 히스토리에서 원하지 않는 파일을 제거하도록 특별히 설계된 것보다 간단하고 빠른 대안 인 BFG Repo-Cleaner를 사용하십시오 git-filter-branch.

사용 지침을주의 깊게 따르십시오. 핵심 부분은 다음과 같습니다.

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git

100MB 이상의 파일 ( 최근 커밋에 없는 파일 )은 Git 리포지토리 기록에서 제거됩니다. 그런 다음 git gc죽은 데이터를 정리 하는 데 사용할 수 있습니다 .

$ git gc --prune=now --aggressive

BFG는 일반적으로 달리기보다 10-50 배 이상 빠르며git-filter-branch 일반적으로 사용하기가 더 쉽습니다.

전체 공개 : 저는 BFG Repo-Cleaner의 저자입니다.


4
@tony 풀 복제를 요청하는 메시지가 다시 발생하는지 확인하기 위해 전체 복제 및 지우기 절차를 반복 할 가치가 있지만 원격 서버가 빨리 감기가 아닌 업데이트를 거부하도록 구성되어 있기 때문에 거의 확실합니다 (예 : 중지) 역사를 잃어 버리는 것-정확히 당신이하고 싶은 것입니다). 리모컨에서 해당 설정을 변경하거나 실패하면 업데이트 된 리포지토리 기록을 새로운 빈 리포지토리로 푸시해야합니다.
Roberto Tyley

1
@RobertoTyley 감사합니다. 나는 그것을 3 번 다른 시도했고 모두 같은 메시지를 보냈습니다. 그래서 나는 당신이 원격 서버가 빨리 감기가 아닌 업데이트를 거부하도록 구성되어 있다고 생각합니다. 업데이트 된 리포지를 새로운 리포지토리로 푸시하는 것을 고려할 것입니다. 감사합니다!
Tony

7
@RobertoTyley Perfect, 당신은 내 시간을 절약합니다. 대단히 감사합니다. 그건 그렇고, git push --force단계 후에 해야 할 수도 있습니다. 그렇지 않으면 원격 저장소가 여전히 변경되지 않았습니다.
li2

3
추가하는 일 git push --force. 또한 주목할 가치가 있습니다 : 리모컨은 강제 푸시를 허용하지 않을 수 있습니다 (gitlab.com은 기본적으로 허용하지 않습니다. 분기를 "보호 해제"해야 함).
MatrixManAtYrService

25
나는 도구가 출력하는 트럼프 전문 용어가 약간 많다고 생각합니다.
Chris

564

다른 개발자에게 히스토리를 공개 한 경우 수행하려는 작업이 크게 손상됩니다. 히스토리 복구 후 필요한 단계 git rebase설명서의 "업스트림 리베이스에서 복구"를 참조하십시오 .

git filter-branch아래 설명 된 두 가지 옵션 과 대화식 리베이스가 있습니다.

사용 git filter-branch

Subversion 가져 오기에서 부피가 큰 이진 테스트 데이터와 비슷한 문제가 있었고 git 저장소에서 데이터를 제거하는 방법 에 대해 썼습니다 .

git history가 다음과 같다고 가정 해보십시오.

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

참고 git lola비표준이지만 매우 유용 별명입니다. --name-status스위치를 사용하면 각 커밋과 관련된 트리 수정을 볼 수 있습니다.

“Careless”커밋 (SHA1 객체 이름이 ce36c98 임)에서 파일 oops.iso은 실수로 추가되고 다음 커밋 cb14efd에서 제거 된 DVD-rip입니다. 앞에서 언급 한 블로그 게시물에 설명 된 기술을 사용하여 실행 명령은 다음과 같습니다.

git filter-branch --prune-empty -d /dev/shm/scratch \
  --index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
  --tag-name-filter cat -- --all

옵션 :

  • --prune-empty필터 작업의 결과로 비어있는 커밋을 제거합니다 ( , 트리를 변경하지 마십시오). 일반적인 경우이 옵션은 더 명확한 기록을 생성합니다.
  • -d필터링 된 기록을 작성하는 데 아직 존재하지 않는 임시 디렉토리의 이름을 지정합니다. 최신 Linux 배포판에서 실행중인 경우 트리를/dev/shm 지정하면 실행 속도가 빨라 집니다.
  • --index-filter주요 이벤트이며 기록의 각 단계에서 색인에 대해 실행됩니다. oops.iso발견 된 곳 을 제거하려고 하지만 모든 커밋에 존재하지는 않습니다. git rm --cached -f --ignore-unmatch oops.isoDVD-rip이 있으면 명령이 삭제되고 그렇지 않으면 실패하지 않습니다.
  • --tag-name-filter태그 이름을 다시 쓰는 방법을 설명합니다. 필터 cat는 신원 작업입니다. 위의 샘플과 같이 리포지토리에 태그가 없을 수도 있지만 전체 일반을 위해이 옵션을 포함했습니다.
  • -- 옵션의 끝을 git filter-branch
  • --all다음 --은 모든 심판에 대한 속기입니다. 위의 샘플과 같이 리포지토리에는 하나의 심판 (마스터) 만있을 수 있지만이 옵션은 전체 일반을 위해 포함되었습니다.

약간의 이탈 후 역사는 다음과 같습니다.

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A   login.html
| * cb14efd Remove DVD-rip
| | D   oops.iso
| * ce36c98 Careless
|/  A   oops.iso
|   A   other.html
|
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

새로운 "Careless"커밋은 추가 만하고 " other.htmlRemove DVD-rip"커밋은 더 이상 마스터 브랜치에 있지 않습니다. refs/original/refs/heads/master실수 한 경우를 대비하여 레이블 이 지정된 분기에 원래 커밋 이 포함됩니다. 제거하려면 “리포지토리 축소를위한 점검표” 의 단계를 따르십시오 .

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

더 간단한 대안을 위해, 저장소를 복제하여 원하지 않는 비트를 폐기하십시오.

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo

file:///...복제 URL을 사용하면 하드 링크 만 만들지 않고 개체가 복사됩니다.

이제 당신의 역사는 :

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

필터 작업이 해당 커밋을 수정하지 않았기 때문에 처음 두 커밋 ( "인덱스"및 "관리 페이지")의 SHA1 개체 이름은 동일하게 유지되었습니다. “무심한”상실 oops.iso과“로그인 페이지”에 새로운 부모가 생겨 SHA1 변경되었습니다.

대화식 리베이스

역사 :

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

oops.iso"Careless"를 추가 한 적이없는 것처럼 제거하려고하면 "DVD-rip 제거"는 쓸모가 없습니다. 따라서 대화식 리베이스로 들어 가려는 계획은 "관리자 페이지"를 유지하고 "무심한"을 편집하고 "DVD-rip 제거"를 버리는 것입니다.

Running $ git rebase -i 5af4522은 다음 내용으로 편집기를 시작합니다.

pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

계획을 실행하면 다음과 같이 수정됩니다.

edit ce36c98 Careless
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
# ...

즉, 우리가 함께 줄을 삭제 "DVD-RIP 제거"하고로 "부주의"의 조작을 변경 edit하기보다는 pick.

편집기를 저장 종료하면 다음 메시지와 함께 명령 프롬프트가 나타납니다.

Stopped at ce36c98... Careless
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

메시지에서 알 수 있듯이 우리는 편집하고자하는“Careless”커밋에 있으므로 두 가지 명령을 실행합니다.

$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue

첫 번째는 문제가되는 파일을 인덱스에서 제거합니다. 두 번째는“Careless”를 업데이트 된 인덱스로 수정하거나 수정하고 -C HEADgit에게 이전 커밋 메시지를 재사용하도록 지시합니다. 마지막으로 git rebase --continue나머지 rebase 작업을 진행합니다.

이것은 다음과 같은 역사를 제공합니다.

$ git lola --name-status
* 93174be (HEAD, master) Login page
| A     login.html
* a570198 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

당신이 원하는 것입니다.


4
git filter-branch를 사용할 때 푸시 할 수없는 이유는 'git@bitbucket.org : product / myproject.git'으로 일부 참조를 푸시하지 못했습니다. 히스토리를 잃지 않도록 빨리 감기가 아닌 업데이트가 거부되었습니다. 다시 밀기 전에 변경합니다.
Agung Prasetyo

11
명령에 -f(또는 --force) 옵션을 추가하십시오 git push. 이 플래그는 검사를 비활성화합니다. 이로 인해 원격 저장소가 커밋을 잃을 수 있습니다. 조심해서 사용하십시오.”
Greg Bacon

5
이것은 원하지 않는 큰 파일을 히스토리에서 제거하기 위해 git-filter-branch를 사용하는 방법을 설명하는 훌륭한 답변이지만, Greg가 자신의 답변을 작성한 이후 BFG Repo-Cleaner가 출시 된 것을 주목할 가치가 있습니다. 사용-자세한 내용은 내 답변을 참조하십시오.
Roberto Tyley

1
위의 절차 중 하나를 수행 한 후 원격 저장소 (GitHub)는 큰 파일을 삭제하지 않습니다. 현지인 만합니다. 강요와 나다를 강요합니다. 내가 무엇을 놓치고 있습니까?
azatar

1
이것은 또한 dirs에서도 작동합니다. ... "git rm --cached -rf --ignore-unmatch path/to/dir"...
rynop

198

이 단순하지만 강력한 명령을 사용하지 않겠습니까?

git filter-branch --tree-filter 'rm -f DVD-rip' HEAD

--tree-filter옵션은 프로젝트를 체크 아웃 할 때마다 지정된 명령을 실행 한 다음 결과를 다시 커밋합니다. 이 경우, 존재 여부에 관계없이 모든 스냅 샷에서 DVD-rip이라는 파일을 제거합니다.

어떤 커밋이 큰 파일 (예 : 35dsa2)을 도입했는지 알고 있다면 HEAD를 35dsa2..HEAD로 교체하여 너무 많은 기록을 다시 쓰지 않도록 할 수 있으므로 아직 푸시하지 않은 경우 커밋이 분기되는 것을 피할 수 있습니다. @ alpha_989에 대한이 의견은 너무 중요합니다.

이 링크를 참조하십시오 .


3
이것은 좋은 해결책입니다! 나는 파일 및 목록을하는 파이썬 스크립트가있는 요점을 만든 자식에 cmd를 청소하고자하는 파일을 삭제할 것 gist.github.com/ariv3ra/16fd94e46345e62cfcbf
punkdata

5
bfg보다 훨씬 낫습니다. 나는 BFG와 자식 깨끗 파일 수 없습니다 만,이 명령은 도움이
podarok

4
대단하다. 큰 파일이 여러 브랜치에있는 경우 브랜치별로이 작업을 수행해야한다는 점을 참고하십시오.
James

2
Windows fatal: bad revision 'rm'에서는을 "대신 사용하여 수정했습니다 '. 전반적인 명령 :git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
marcotama

2
commit파일을 어디에 넣었 는지 알고 있다면 (예 35dsa2:) HEAD로 바꿀 수 있습니다 35dsa2..HEAD. 모든 커밋을 체크 아웃하고 다시 작성하려고 시도하지 않는 방식 tree-filter보다 훨씬 느립니다 index-filter. HEAD를 사용하면 그렇게하려고 시도합니다.
alpha_989

86

(이 문제에 대해 본 가장 좋은 답변은 https://stackoverflow.com/a/42544963/714112 입니다. 이 스레드는 Google 검색 순위가 높지만 다른 스레드는 그렇지 않기 때문에 여기에 복사되었습니다)

🚀 엄청나게 빠른 쉘 원 라이너 🚀

이 쉘 스크립트는 저장소의 모든 Blob 오브젝트를 가장 작은 것에서 가장 큰 것으로 정렬하여 표시합니다.

내 샘플 저장소의 경우 여기에있는 다른 것보다 약 100 배 빠릅니다 .
신뢰할 수있는 Athlon II X4 시스템에서는 1 분만 에 5,622,155 개의 객체로 Linux Kernel 저장소 를 처리합니다 .

기본 스크립트

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

위의 코드를 실행하면 다음 과 같이 사람이 읽을 수있는 좋은 결과를 얻을 수 있습니다 .

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

🚀 빠른 파일 제거 🚀

그런 다음 파일을 제거 a하고 b에서 도달 가능한 모든 커밋에서 HEAD다음 명령을 사용할 수 있다고 가정하십시오 .

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD

3
--tag-name-filter catgit filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD
리포지토리에

3
Mac 지침 및 기타 정보는 원래 링크 된 게시물에 나타납니다
nruth

3
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEAD박쥐의 작업
순서

내가 가장 좋아하는 답변. (GNU 명령을 사용하여) 맥 OS에서 사용하기에 약간의 비틀기git rev-list --objects --all \ | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \ | awk '/^blob/ {print substr($0,6)}' \ | sort --numeric-sort --key=2 \ | gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
플로리안 오스왈드

rev-list가있는 멋진 스크립트이지만 별명으로 작동하지 않았습니다. 어떻게해야합니까?
Robin Manoli

47

SO의 거의 모든 대답을 시도한 후에 마침내 저장소에서 큰 파일을 신속하게 제거하고 삭제하고 다시 동기화 할 수있는이 보석을 발견했습니다 .http : //www.zyxware.com/articles/4027/how-to-delete -영구적으로-로컬 및 원격 git 저장소에서 파일

로컬 작업 폴더에 CD를 넣고 다음 명령을 실행하십시오.

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all

주어진 git 저장소에서 제거하려는 파일 또는 폴더로 FOLDERNAME을 바꾸십시오.

이 작업이 완료되면 다음 명령을 실행하여 로컬 저장소를 정리하십시오.

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

이제 모든 변경 사항을 원격 저장소로 푸시하십시오.

git push --all --force

원격 저장소가 정리됩니다.


나를 위해 매력처럼 일했습니다.
Ramon Vasconcelos

3
이것은 나를 위해 일했다. 리포지토리에서 특정 폴더 (필자의 경우 파일이 너무 큰 폴더 또는 Github 저장소)를 제거하지만 로컬 파일 시스템이있는 경우이를 유지합니다.
skizzo

나를 위해 일했다! , 확인 (지금 복제에 누군가가있는 경우) 당신이 어떤 깨진 링크, 의존성 등을 업데이트 할 계획이없는 역사는 잠재적으로 혼동되는 남지
ruoho ruotsi

38

이 명령은 제 경우에는 효과가있었습니다.

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

위 버전과 조금 다릅니다.

이것을 github / bitbucket으로 푸시 해야하는 사람들을 위해 (비트 버켓으로 만 테스트했습니다) :

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work

4
위와 어떻게 다른가요? 왜 더 낫습니까?
Andy Hayden

1
어떤 이유로 mkljun 버전이 내 경우 git 공간이 줄어들지 않았으므로 이미을 사용하여 색인에서 파일을 제거했습니다 git rm --cached files. Greg Bacon의 제안은 더 완전하고이 광산과 거의 동일하지만 필터 분기를 여러 번 사용하는 경우에 대한 --force 색인을 놓쳤으며 많은 정보를 작성하여 내 버전이 이력서와 같습니다. 그것의.
Kostanos 2016 년

1
이것은 정말 도움하지만 난 사용하는 데 필요한 -f뿐만 아니라 옵션을 -rf여기에 git rm --cached -rf --ignore-unmatch oops.iso대신 git rm --cached -r --ignore-unmatch oops.iso아래 @ lfender6445 당
drstevok

10

이 명령은 매우 파괴적 일 수 있습니다. 더 많은 사람들이 레포 작업을하고 있다면 모두 새 나무를 가져와야합니다. 목표가 크기를 줄이지 않는 경우 세 가지 중간 명령이 필요하지 않습니다. 필터 브랜치는 제거 된 파일의 백업을 작성하고 오랫동안 유지할 수 있기 때문입니다.

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

11
스스로 큰 고통을 겪고 싶지 않다면 이러한 명령을 실행하지 마십시오. 원본 소스 코드 파일을 많이 삭제했습니다. 나는 그것이 원래 질문에 따라 GIT의 커밋 기록에서 큰 파일을 제거한다고 가정했지만이 명령은 원래 소스 코드 트리에서 파일을 영구적으로 제거하도록 설계되었다고 생각합니다 (큰 차이!). 내 시스템 : Windows, VS2012, Git Source Control Provider.
Contango

2
이 명령을 사용했습니다 : git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all코드에서 첫 번째 명령 대신
Kostanos


8

커밋이 전체 트리를 통하지 않고 최근에 확인 된 경우 다음을 수행하십시오. git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD


7

비트 버킷 계정으로 실수로 사이트의 엄청난 * .jpa 백업을 저장했습니다.

git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all

Relpace MY-BIG-DIRECTORY문제의 폴더와는 완전히 (당신의 역사를 다시 작성하는 태그를 포함하여 ).

출처 : https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/


1
답변의 스크립트에 약간의 문제가 있고 모든 지점에서 검색하지 않는 것을 제외 하고는이 답변이 도움이되었습니다. 그러나 링크의 명령은 완벽하게 수행했습니다.
Ali B

5

이것은 당신의 역사에서 그것을 제거합니다

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all

이것은 나를 위해 감사했습니다!
Sonja Brits

이것은 내 경우에 작동합니다. 나는 당신의 마스터 지점에서 이것을 실행합니다.
S. Domeng

4

나는 기본적 으로이 답변에 무엇을했는지 : https://stackoverflow.com/a/11032521/1286423

(역사를 위해 여기에 복사하여 붙여 넣기)

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

이름을 바꾸고 물건을 많이 옮기고 싶기 때문에 작동하지 않았습니다. 따라서 일부 큰 파일은 이름이 바뀐 폴더에 있었고 gc는 해당 파일을 tree가리키는 객체 의 참조로 인해 해당 파일에 대한 참조를 삭제할 수 없다고 생각 합니다. 정말로 그것을 죽이는 나의 궁극적 인 해결책은 다음과 같습니다.

# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive

내 repo ( .git)가 32MB에서 388KB로 변경되어 필터 브랜치조차도 청소할 수 없었습니다.


4

git filter-branch커밋 기록에서 큰 파일을 삭제하는 데 사용할 수있는 강력한 명령입니다. 파일은 잠시 동안 유지되며 Git은 다음 가비지 콜렉션에서 파일을 제거합니다. 아래는 커밋 히스토리에서 파일을 삭제 하는 전체 프로세스입니다 . 안전을 위해 아래 프로세스는 먼저 새 브랜치에서 명령을 실행합니다. 결과가 필요한 경우 실제로 변경하려는 지점으로 다시 설정하십시오.

# Do it in a new testing branch
$ git checkout -b test

# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -d test

# Push it with force
$ git push --force origin master

2

Git Extensions를 사용하십시오 .UI 도구입니다. 리포지토리에서 레이지 파일을 찾아 영구적으로 제거 할 수있는 "큰 파일 찾기"라는 플러그인이 있습니다.

'filter-branch'로 제거 된 파일을 찾을 수 없으므로이 도구를 사용하기 전에 'git filter-branch'를 사용하지 마십시오 ( 'filter-branch'가 저장소 팩 파일에서 파일을 완전히 제거하지는 않습니다) .


이 방법은 큰 리포지토리에 비해 너무 느립니다. 큰 파일을 나열하는 데 1 시간 이상 걸렸습니다. 그런 다음 파일을 삭제하려고하면 한 시간 후에 삭제하려는 첫 번째 파일을 처리하는 방법의 1/3에 불과합니다.
kristianp

예, 느리지 만 작업은 ... 더 빠른 것을 알고 있습니까?
Nir

1
이 페이지의 다른 답변에 따라 BFG Repo-Cleaner를 사용하지 않았습니다.
kristianp

2

다음 branch filter명령을 사용하여이를 수행 할 수 있습니다 .

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD


2

이 글타래에는 아주 좋은 답변이 있지만 그중 많은 것들이 구식입니다. git-filter-branch큰 저장소에서는 사용하기 어렵고 너무 느리기 때문에 더 이상 사용 하지 않는 것이 좋습니다.

git-filter-repo 사용이 훨씬 빠르고 간단합니다.

git-filter-repohttps://github.com/newren/git-filter-repo 에서 사용할 수있는 Python 스크립트 입니다.

Python3 스크립트 git-filter-repo라는 하나의 파일 만 있으면됩니다. PATH 변수에 포함 된 경로로 복사하십시오. Windows에서는 스크립트의 첫 번째 행을 변경해야 할 수도 있습니다 (INSTALL.md 참조). 시스템에 Python3이 설치되어 있어야하지만 큰 문제는 아닙니다.

먼저 당신은 실행할 수 있습니다

git filter-repo --analyze

이는 다음에 수행 할 작업을 결정하는 데 도움이됩니다.

어디에서나 DVD-rip 파일을 삭제할 수 있습니다.

 git filter-repo --invert-paths --path-match DVD-rip

Filter-repo는 정말 빠릅니다. 컴퓨터에서 filter-branch로 약 9 시간이 걸리는 작업은 filter-repo에 의해 4 분 안에 완료되었습니다. filter-repo로 더 많은 좋은 일을 할 수 있습니다. 해당 설명서를 참조하십시오.

경고 : 저장소 사본에서이 작업을 수행하십시오. filter-repo의 많은 작업은 취소 할 수 없습니다. filter-repo는 수정 된 모든 커밋 (물론)과 모든 하위 항목의 커밋 해시를 마지막 커밋으로 변경합니다!


1

이 문제가 발생하면 git rmgit이 파일이 우리의 역사에 한 번 존재했음을 기억하므로 참조를 유지할 것이므로 충분하지 않습니다.

Blob에 대한 참조는 git 가비지 수집기가 공간을 정리하지 못하기 때문에 문제를 악화시키기 위해 rebasing도 쉽지 않습니다. 여기에는 원격 참조 및 참조 참조가 포함됩니다.

내가 함께 넣어 git forget-blob시도가 모든 참조를 제거하고 모든이 지점에서 커밋 재 작성 자식 필터 - 분기를 사용하는, 약간의 스크립트를.

블롭이 완전히 참조되지 git gc않으면 제거합니다.

사용법은 매우 간단 git forget-blob file-to-forget합니다. 여기에서 더 많은 정보를 얻을 수 있습니다

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

Stack Overflow의 답변과 일부 블로그 항목 덕분에 이것을 함께 모았습니다. 그들에게 크레딧!


당신은 homebrew에서 이것을 얻어야합니다
Cameron E

0

git filter-branch(느리지 만 순수한 자식 솔루션) 및 BFG 이외 (쉽고 매우 확대됨), 좋은 성능을 가진 필터에 다른 도구도 있습니다 :

https://github.com/xoofx/git-rocket-filter

설명에서 :

git-rocket-filter의 목적은 git-filter-branch다음과 같은 고유 한 기능을 제공하면서 명령과 유사 합니다.

  • 커밋과 트리의 빠른 재 작성 (x10 ~ x100 순서)
  • --keep을 통한 화이트리스트 (파일 또는 디렉토리 유지) 및 --remove 옵션을 통한 블랙리스트를 모두 지원합니다.
  • 트리 필터링에 .gitignore와 같은 패턴 사용
  • 커밋 필터링 및 트리 필터링을위한 빠르고 쉬운 C # 스크립팅
  • 파일 / 디렉토리 패턴별로 트리 필터링에서 스크립팅 지원
  • 병합 커밋을 포함하여 비어 있거나 변경되지 않은 커밋을 자동으로 제거
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.