내 자식 저장소에서 참조되지 않은 Blob을 제거하는 방법


124

마스터 및 릴리스의 두 가지 분기가있는 GitHub 저장소가 있습니다.

릴리스 분기에는 매우 큰 저장소 크기 (> 250MB)에 기여하는 바이너리 배포 파일이 포함되어 있으므로 정리하기로 결정했습니다.

먼저 원격 릴리스 분기를 삭제했습니다. git push origin :release

그런 다음 로컬 릴리스 브랜치를 삭제했습니다. 먼저 시도 git branch -d release했지만 git은 "오류 : 'release'브랜치가 현재 HEAD의 조상이 아닙니다."라고 말했습니다. 사실이므로 git branch -D release강제로 삭제했습니다.

그러나 로컬 및 GitHub에서 내 저장소 크기는 여전히 컸습니다. 그래서 나는 git gc --prune=today --aggressive운없이 git 명령과 같은 일반적인 git 명령 목록을 실행했습니다 .

SO 1029969 에서 Charles Bailey의 지침에 따라 가장 큰 얼룩에 대한 SHA1 목록을 얻을 수있었습니다. 그런 다음 SO 460331 의 스크립트를 사용하여 blob을 찾았습니다. 더 작은 blob이 발견되었지만 가장 큰 5 개는 존재하지 않으므로 스크립트가 작동하고 있음을 알고 있습니다.

이 블로그는 릴리스 브랜치의 바이너리라고 생각하며, 브랜치를 삭제 한 후 어떻게 든 남겨져 있습니다. 그것들을 제거하는 올바른 방법은 무엇입니까?


어떤 버전의 Git을 사용하고 있습니까? 그리고 stackoverflow.com/questions/1106529/… 을 사용해 보셨습니까 ?
VonC

git version 1.6.2.3 나는 gc를 시도하고 다양한 인수로 정리했습니다. 나는 repack -a -d -l을 시도하지 않았고, 단지 그것을 실행했다.
kkrugler 2009

2
새로운 정보-GitHub의 새로운 클론에는 더 이상 참조되지 않은 Blob이 없으며 250MB에서 "만"84MB로 줄었습니다.
kkrugler 2009

답변:


219

... 그리고 더 이상 고민하지 않고, 추가 설정 변수가 나올 때까지 모든 git 쓰레기 를 제거 수있는 유용한 명령 인 "git-gc-all"을 보여 드릴 까요?

git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc

먼저 이와 같은 것을 실행해야 할 수도 있습니다. 오 이런, 자식은 복잡합니다!

git remote rm origin
rm -rf .git/refs/original/ .git/refs/remotes/ .git/*_HEAD .git/logs/
git for-each-ref --format="%(refname)" refs/original/ | xargs -n1 --no-run-if-empty git update-ref -d

Zitrax 덕분에 일부 태그를 제거해야 할 수도 있습니다.

git tag | xargs git tag -d

이 모든 것을 스크립트에 넣었습니다 : git-gc-all-ferocious .


1
흥미 롭군. 더 일반적인 대답에 대한 좋은 대안입니다. +1
VonC 2013

10
이것은 더 많은 찬성표를받을 가치가 있습니다. 마침내 다른 메서드가 유지하는 많은 git 객체를 제거했습니다. 감사!
Jean-Philippe Pellet

1
찬성. 와, 방금 뭘했는지 모르겠지만 정리가 많이되는 것 같아요. 그 기능에 대해 자세히 설명해 주시겠습니까? 나는 그것이 내 모든 objects. 그것들은 무엇이며 왜 (분명히) 무관합니까?
Redsandro

1
@Redsandro, 내가 이해하는 것처럼 "git rm origin", "rm"및 "git update-ref -d"명령은 원격 등에 대한 이전 커밋에 대한 참조를 제거하여 가비지 수집을 방해 할 수 있습니다. "git gc"옵션은 다양한 오래된 커밋을 유지하지 않도록 지시합니다. 그렇지 않으면 잠시 동안 유지됩니다. 예를 들어 gc.rereresolved는 "이전에 해결 한 충돌 병합 레코드"용이며 기본적으로 60 일 동안 보관됩니다. 이러한 옵션은 git-gc 맨 페이지에 있습니다. 나는 git의 전문가가 아니며 이러한 모든 일이 정확히 무엇을하는지 모릅니다. 나는 맨 페이지에서 그들을 찾았고 커밋 심판을 위해 .git을 찾았습니다.
Sam Watkins 2014 년

1
git 객체는 히스토리의 오래된 항목을 포함하여 git 저장소의 압축 파일 또는 트리 또는 커밋입니다. git gc는 불필요한 객체를 지 웁니다. 현재 리포지토리 및 해당 기록에 여전히 필요한 개체를 유지합니다.
Sam Watkins 2014 년

81

여기 에 설명 된대로 reflog를 통해서만 참조 된 모든 항목을 영구적으로 제거 하려면 다음을 사용하십시오.

git reflog expire --expire-unreachable=now --all
git gc --prune=now

git reflog expire --expire-unreachable=now --all에서 도달 할 수없는 커밋의 모든 참조를 제거합니다 reflog.

git gc --prune=now 커밋 자체를 제거합니다.

주의 : git gc --prune=now해당 커밋은 여전히 ​​reflog에서 참조되므로 using 만 사용할 수 없습니다. 따라서 reflog를 지우는 것은 필수입니다. 또한 사용하는 경우 rerere이러한 명령으로 지워지지 않은 추가 참조가 있습니다. 자세한 내용은를 참조 git help rerere하십시오. 또한 로컬 또는 원격 분기 또는 태그에서 참조하는 커밋은 git에서 중요한 데이터로 간주되기 때문에 제거되지 않습니다.


14
그것은 효과가 있었지만 어떻게 든 그 과정에서 저장된 보관함을 잃어 버렸습니다 (내 경우에는 중요한 것은 없으며 다른 사람들에게는주의 사항입니다)
Amro

1
왜 공격적이지 않습니까?
JoelFan

2
이 답변에는 명확한 경고가 필요하다고 생각합니다. 댓글로 작성자에게 제안해야하므로 수정 제안이 거부되었습니다. 이 편집 stackoverflow.com/review/suggested-edits/26023988을 수락 하거나 자신의 방식으로 경고를 추가하십시오. 또한 이것은 모든 은닉 물을 떨어 뜨립니다 . 그것은 경고에도 기억되어야합니다!
Inigo

나는 git 버전 2.17로 테스트했으며 숨겨진 커밋은 위의 명령으로 제거되지 않습니다. 추가 명령을 실행하지 않았습니까?
Mikko Rantalainen

1
git fetch --prune로컬 Blob을 삭제하기 때문에 크기를 더 줄입니다.
hectorpal

33

에서 언급 한 바와 같이 ,이 SO 응답 , git gc실제로 REPO의 크기를 늘릴 수 있습니다!

이 스레드 참조

이제 git에는 ' '를 실행할 때 참조되지 않은 객체를 즉시 삭제 하지 않는 안전 메커니즘이 있습니다 git gc.
기본적으로 참조되지 않은 개체는 2 주 동안 보관됩니다. 이는 실수로 삭제 된 브랜치 또는 커밋을 쉽게 복구 할 수 있도록하거나 아직 참조되지 않은 프로세스에서 방금 생성 된 객체 git gc가 병렬로 실행 되는 ' '프로세스에 의해 삭제 될 수있는 경합을 방지하기위한 것 입니다.

따라서 압축되었지만 참조되지 않은 개체에 유예 기간을 제공하기 위해 재 압축 프로세스는 참조되지 않은 개체를 팩에서 느슨한 형태로 밀어 넣어 노화되고 결국 정리 될 수 있도록합니다.
참조되지 않는 객체는 일반적으로 그렇게 많지 않습니다. 404855 개의 참조되지 않은 개체를 갖는 것은 상당히 많은 일이며, 복제를 통해 이러한 개체를 처음에 보내는 것은 어리 석고 네트워크 대역폭을 완전히 낭비하는 것입니다.

어쨌든 ... 문제를 해결하려면 유예 기간을 비활성화하고 참조되지 않은 객체를 즉시 제거 git gc하는 --prune=now인수 와 함께 ' ' 를 실행하기 만하면 됩니다 (동시에 다른 git 활동이 발생하지 않는 경우에만 안전함). 워크 스테이션에서 쉽게 확인할 수 있습니다).

그리고 BTW, ' git gc --aggressive'를 최신 git 버전 (또는 ' git repack -a -f -d --window=250 --depth=250') 과 함께 사용

같은 스레드는 언급 :

 git config pack.deltaCacheSize 1

이는 델타 캐시 크기를 무제한을 의미하는 기본값 0 대신 1 바이트 (효과적으로 비활성화)로 제한합니다. 이를 통해 git repack4GB RAM과 4 개의 스레드 (쿼드 코어)를 사용하는 x86-64 시스템 에서 위의 명령을 사용하여 해당 저장소를 다시 포장 할 수 있습니다. 하지만 상주 메모리 사용량은 거의 3.3GB로 증가합니다.

시스템이 SMP이고 RAM이 충분하지 않은 경우 스레드 수를 하나만 줄일 수 있습니다.

git config pack.threads 1

또한 --window-memory argument' git repack' 를 사용하여 메모리 사용량을 추가로 제한 할 수 있습니다 .
예를 들어를 사용 --window-memory=128M하면 델타 검색 메모리 사용량에 대한 합리적인 상한을 유지해야하지만 저장소에 많은 대용량 파일이 포함 된 경우 최적의 델타 일치가 줄어들 수 있습니다.


필터 분기 전면 에서이 스크립트 를 신중하게 고려할 수 있습니다.

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

stackoverflow.com/questions/359424/… 또한 filter-branch명령 사용을 위한 좋은 시작입니다 .
VonC

안녕하세요 VonC-NI는 git gc prune = now를 시도했습니다. 분기 삭제 후 로컬에서 참조되지 않은 blob으로 상처를 입었다는 점에서 정말 git 버그처럼 보이지만 GitHub 저장소의 새로운 복제본이 없기 때문에 로컬 저장소 문제 일뿐입니다. 하지만 지우고 싶은 추가 파일이 있으므로 위에서 언급 한 스크립트는 훌륭합니다. 감사합니다!
kkrugler 2009


12

HEAD가 움직일 때마다 git은 reflog. 커밋을 제거한 경우 reflog~ 30 일 동안 에서 계속 참조하므로 여전히 "매달린 커밋"이 있습니다 . 이것은 실수로 커밋을 삭제할 때의 안전망입니다.

git reflog특정 커밋 제거, 리팩 등 명령을 사용 하거나 상위 수준 명령 만 사용할 수 있습니다 .

git gc --prune=now

5

사용할 수 있습니다 git forget-blob.

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

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

히스토리, 리플 로그, 태그 등의 모든 커밋에서 사라집니다.

나는 때때로 같은 문제에 부딪 히고,이 게시물과 다른 사람들로 돌아와야 할 때마다 프로세스를 자동화했습니다.

Sam Watkins와 같은 기여자에 대한 크레딧


2

git-filter-branch 를 사용해보세요 -큰 얼룩을 제거하지는 않지만 전체 저장소에서 지정한 큰 파일을 제거 할 수 있습니다. 나를 위해 리포지토리 크기를 수백 MB에서 12 MB로 줄입니다.


6
이제 무서운 명령입니다 :) 내 자식 - Fu는 강한 느낌을 때 그것을 시도를 제공해야합니다.
kkrugler 2009

당신은 다시는 말할 수 있습니다. 나는 항상 저장소의 기록을 조작하는 명령을 경계합니다. 여러 사람이 해당 저장소에서 밀고 당기고 갑자기 git이 예상하는 많은 객체가 거기에 없을 때 상황이 매우 잘못되는 경향이 있습니다.
Jonathan Dumaine 2011-08-12

1

때때로, "gc"가별로 좋은 일을하지 않는 이유는 이전 커밋을 기반으로 미완성 된 rebase 또는 stash가 있기 때문입니다.


또는 이전 커밋은 HEAD, ORIG_HEAD, FETCH_HEAD, reflog 또는 git이 가치있는 것을 잃지 않도록 자동으로 유지하는 기타 항목에 의해 참조됩니다. 정말로 모든 것을 잃고 싶다면 그렇게하기 위해 더 많은 노력을 기울여야합니다.
Mikko Rantalainen

1

다른 팁을 추가하려면 git gc 를 사용하기 전에 git remote prune 을 사용하여 원격 의 오래된 브랜치를 삭제하는 것을 잊지 마십시오.

git branch -a로 볼 수 있습니다.

github 및 분기 저장소에서 가져올 때 종종 유용합니다.


1

git filter-branch및을 수행하기 전에 git gc저장소에있는 태그를 검토해야합니다. 지속적인 통합 및 배포와 같은 항목에 대한 자동 태그 지정 기능이있는 실제 시스템은 이러한 태그에서 원치 않는 개체를 계속 참조하므로 gc제거 할 수 없으며 repo의 크기가 여전히 왜 그렇게 큰지 계속 궁금해 할 것입니다.

가장 좋은 방법은 모든 유엔 원하는 물건을 제거하는 것은 실행하는 것입니다 git-filtergit gc다음 새 베어의 repo에 마스터를 누릅니다. 새 베어 저장소에는 정리 된 트리가 있습니다.

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