Jak usunąć / usunąć duży plik z historii zatwierdzeń w repozytorium Git?

708

Od czasu do czasu wrzucałem DVD-rip do projektu na stronie internetowej, a potem beztrosko git commit -a -m ..., a zap, repo było nadęte przez 2,2 koncerty. Następnym razem dokonałem edycji, usunąłem plik wideo i popełniłem wszystko, ale skompresowany plik jest nadal w repozytorium, w historii.

Wiem, że mogę zakładać gałęzie z tych zatwierdzeń i przestawiać jedną gałąź na drugą. Ale co powinienem zrobić, aby połączyć razem dwa zatwierdzenia, aby duży plik nie pojawił się w historii i został oczyszczony w procedurze usuwania śmieci?

culebrón
źródło
9
Ten artykuł powinien pomóc help.github.com/removing-sensitive-data
MBO
1
Pamiętaj, że jeśli duży plik znajduje się w podkatalogu, musisz podać pełną ścieżkę względną.
Johan
1
Również powiązany help.github.com/en/articles/…
frederj
Wiele odpowiedzi poniżej mówi o BFG jako łatwiejszych niż git filter-branch, ale uważam, że jest odwrotnie.
2540625

Odpowiedzi:

605

Użyj BFG Repo-Cleaner , prostszej, szybszej alternatywy dla git-filter-branchspecjalnie zaprojektowanej do usuwania niechcianych plików z historii Git.

Dokładnie postępuj zgodnie z instrukcjami użytkowania , podstawowa część jest taka:

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

Wszelkie pliki o rozmiarze przekraczającym 100 MB (które nie są w twoim ostatnim zatwierdzeniu) zostaną usunięte z historii repozytorium Git. Następnie możesz użyć git gcdo usunięcia martwych danych:

$ git gc --prune=now --aggressive

BFG jest zwykle co najmniej 10-50 razy szybszy niż bieganie git-filter-branchi ogólnie łatwiejszy w użyciu.

Pełne ujawnienie: jestem autorem BFG Repo-Cleaner.

Roberto Tyley
źródło
4
@tony Warto powtórzyć całą procedurę klonowania i czyszczenia, aby zobaczyć, czy komunikat z prośbą o ponowne pobranie pojawia się ponownie, ale prawie na pewno dlatego, że twój zdalny serwer jest skonfigurowany do odrzucania aktualizacji nie do szybkiego przewijania (tj. jest skonfigurowany, aby cię zatrzymać od utraty historii - właśnie to chcesz zrobić). Musisz zmienić to ustawienie na pilocie lub, jeśli to nie wystarczy, przenieś zaktualizowaną historię repozytoriów do zupełnie nowego repozytorium.
Roberto Tyley
1
@RobertoTyley Thanks. Próbowałem 3 razy i wszystkie przyniosły ten sam komunikat. Myślę też, że masz rację, że serwer zdalny jest skonfigurowany do odrzucania aktualizacji nie do szybkiego przewijania. Rozważę przeniesienie zaktualizowanego repozytorium do zupełnie nowego repozytorium. Dziękuję Ci!
Tony
7
@RobertoTyley Idealnie, oszczędzasz mój czas, dziękuję bardzo. Nawiasem mówiąc, być może powinienem zrobić git push --forcepo twoich krokach, w przeciwnym razie zdalne repo nadal się nie zmieni.
li2
3
+1 do dodania git push --force. Warto również zauważyć: wymuszone wypychanie może być niedozwolone przez zdalne (gitlab.com domyślnie nie. Musiał „odblokować” gałąź).
MatrixManAtYrService 10.09.15
25
Wydaje mi się, że żargon Trumpa przedstawiający narzędzia jest trochę większy.
Chris
564

To, co chcesz zrobić, jest bardzo destrukcyjne, jeśli opublikujesz historię innym programistom. Zobacz „Odzyskiwanie z wcześniejszego uruchomienia” w git rebasedokumentacji, aby dowiedzieć się, jakie kroki należy wykonać po naprawieniu historii.

Masz co najmniej dwie opcje: git filter-branchi interaktywną bazę, obie wyjaśnione poniżej.

Za pomocą git filter-branch

Miałem podobny problem z dużymi danymi binarnymi z importu Subversion i pisałem o usuwaniu danych z repozytorium git .

Powiedz, że twoja historia gitów to:

$ 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

Pamiętaj, że git lolajest to niestandardowy, ale bardzo użyteczny alias. Za pomocą --name-statusprzełącznika możemy zobaczyć modyfikacje drzewa związane z każdym zatwierdzeniem.

W zatwierdzeniu „Nieostrożny” (którego nazwa obiektu SHA1 to ce36c98) plik oops.isoto zgrywanie DVD dodane przypadkowo i usunięte w następnym zatwierdzeniu, cb14efd. Korzystając z techniki opisanej we wspomnianym blogu, polecenie do wykonania to:

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

Opcje:

  • --prune-emptyusuwa zatwierdzenia, które stają się puste ( tj. nie zmieniają drzewa) w wyniku operacji filtrowania. W typowym przypadku ta opcja zapewnia czystszą historię.
  • -dnazywa tymczasowy katalog, który jeszcze nie istnieje, aby użyć go do zbudowania przefiltrowanej historii. Jeśli korzystasz z nowoczesnej dystrybucji Linuksa, określenie drzewa /dev/shmspowoduje szybsze wykonanie .
  • --index-filterjest głównym wydarzeniem i działa na podstawie indeksu na każdym etapie historii. Chcesz usunąć, oops.isogdziekolwiek się znajdzie, ale nie jest obecny we wszystkich zatwierdzeniach. Polecenie git rm --cached -f --ignore-unmatch oops.isousuwa zgrywanie DVD, gdy jest obecne, i w przeciwnym razie nie zawiedzie.
  • --tag-name-filteropisuje, jak przepisać nazwy znaczników. Filtr catto operacja tożsamości. Twoje repozytorium, podobnie jak powyższy przykład, może nie zawierać żadnych tagów, ale dodałem tę opcję dla pełnej ogólności.
  • -- określa koniec opcji dla git filter-branch
  • --allnastępujące --jest skrótem dla wszystkich referencji. Twoje repozytorium, podobnie jak powyższy przykład, może mieć tylko jeden odnośnik (master), ale włączam tę opcję dla pełnej ogólności.

Po krótkiej przerwie historia jest teraz:

$ 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

Zauważ, że nowe zatwierdzenie „Nieostrożne” dodaje tylko other.htmli że zatwierdzenie „Usuń DVD-rip” nie jest już w gałęzi master. Oddział oznaczony refs/original/refs/heads/masterzawiera oryginalne zatwierdzenia na wypadek pomyłki. Aby go usunąć, wykonaj czynności opisane w „Liście kontrolnej zmniejszania repozytorium”.

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

Dla prostszej alternatywy sklonuj repozytorium, aby odrzucić niechciane bity.

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

Użycie file:///...sklonowanego adresu URL powoduje kopiowanie obiektów zamiast tworzenia wyłącznie linków stałych.

Teraz twoja historia to:

$ 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

Nazwy obiektów SHA1 dla pierwszych dwóch zatwierdzeń („Indeks” i „Strona administratora”) pozostały takie same, ponieważ operacja filtrowania nie zmodyfikowała tych zatwierdzeń. „Nieostrożny” stracił, oops.isoa „Strona logowania” ma nowego rodzica, więc ich SHA1 się zmieniły.

Interaktywna baza danych

Z historią:

$ 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

chcesz usunąć oops.isoz „Nieostrożnego”, jakbyś nigdy go nie dodał, a wtedy „Usuń DVD-rip” jest dla ciebie bezużyteczne. Dlatego naszym planem przejścia na interaktywny rebase jest utrzymanie „Strony administracyjnej”, edycja „Nieostrożny” i odrzucenie „Usuń DVD-rip”.

Uruchamianie $ git rebase -i 5af4522uruchamia edytor o następującej treści.

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.
#

Realizując nasz plan, modyfikujemy go do

edit ce36c98 Careless
pick f772d66 Login page

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

Oznacza to, że usuwamy wiersz za pomocą polecenia „Remove DVD-rip” i zmieniamy operację na „Careless” na editzamiast pick.

Opuszczenie edytora powoduje wyświetlenie wiersza polecenia z następującym komunikatem.

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

Jak mówi nam wiadomość, wykonujemy zatwierdzenie „Nieostrożne”, które chcemy edytować, więc uruchamiamy dwa polecenia.

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

Pierwszy usuwa szkodliwy plik z indeksu. Drugi modyfikuje lub zmienia „Nieostrożny”, aby był zaktualizowanym indeksem i -C HEADinstruuje git, aby ponownie użył starej wiadomości zatwierdzenia. Wreszcie, git rebase --continuekontynuuje resztę operacji rebase.

To daje historię:

$ 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

co chcesz.

Greg Bacon
źródło
4
Dlaczego nie mogę wypychać, gdy korzystam z gałęzi filter git, nie udało mi się przesłać niektórych referencji na adres „[email protected]: product / myproject.git”. Aby zapobiec utracie historii, aktualizacje niezwiązane z szybkim przewijaniem zostały odrzucone Scal zdalne zmienia się przed ponownym naciśnięciem.
Agung Prasetyo
11
Dodaj opcję -f(lub --force) do swojego git pushpolecenia: „Zwykle polecenie odmawia aktualizacji zdalnego odwołania, które nie jest przodkiem lokalnego odwołania, którego użyto do zastąpienia. Ta flaga wyłącza czek. Może to spowodować utratę zatwierdzeń przez zdalne repozytorium; używaj go ostrożnie. ”
Greg Bacon
5
Jest to cudownie dokładna odpowiedź wyjaśniająca użycie git-filter-branch do usuwania niechcianych dużych plików z historii, ale warto zauważyć, że odkąd Greg napisał swoją odpowiedź, BFG Repo-Cleaner został wydany, który często jest szybszy i łatwiejszy użyj - zobacz moją odpowiedź, aby poznać szczegóły.
Roberto Tyley
1
Po wykonaniu jednej z powyższych procedur zdalne repozytorium (na GitHub) NIE usuwa dużego pliku. Tylko miejscowi. Wymuszam push i nada. czego mi brakuje?
azatar
1
działa to również na reż. ... "git rm --cached -rf --ignore-unmatch path/to/dir"...
rynop
198

Dlaczego nie skorzystać z tego prostego, ale potężnego polecenia?

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

--tree-filterOpcja uruchamia określonego polecenia po każdej kasie projektu, a następnie recommits wyniki. W takim przypadku usuwasz plik o nazwie DVD-rip z każdej migawki, niezależnie od tego, czy istnieje.

Jeśli wiesz, który zatwierdzenie wprowadził ogromny plik (powiedzmy 35dsa2), możesz zastąpić HEAD 35dsa2..HEAD, aby uniknąć przepisywania zbyt dużej historii, unikając w ten sposób rozbieżnych zatwierdzeń, jeśli jeszcze tego nie zrobiłeś. Ten komentarz dzięki uprzejmości @ alpha_989 wydaje się zbyt ważny, aby go tu pominąć.

Zobacz ten link .

Gary Gauh
źródło
3
To dobre rozwiązanie! Stworzyłem gist, która ma skrypt Pythona, aby wyświetlić listę plików, oraz git cmd, który usunie plik, który chcesz wyczyścić gist.github.com/ariv3ra/16fd94e46345e62cfcbf
punkdata
5
Znacznie lepiej niż BFG. Nie byłem w stanie wyczyścić pliku z git za pomocą bfg, ale to polecenie pomogło
podarok
4
To jest świetne. Uwaga dla innych, że będziesz musiał to zrobić dla każdej gałęzi, jeśli duży plik znajduje się w wielu gałęziach.
James
2
W systemie Windows dostałem fatal: bad revision 'rm', co naprawiłem, używając "zamiast '. Ogólne polecenie:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
marcotama,
2
Jeśli wiesz commit, gdzie można umieścić w pliku (powiedzmy 35dsa2), można zastąpić HEADz 35dsa2..HEAD. tree-filterjest znacznie wolniejszy niż w index-filterten sposób, nie będzie próbował sprawdzić wszystkich zmian i przepisać je. jeśli użyjesz HEAD, spróbuje to zrobić.
alpha_989
86

(Najlepsza odpowiedź, jaką widziałem na ten problem, to: https://stackoverflow.com/a/42544963/714112 , skopiowane tutaj, ponieważ ten wątek pojawia się wysoko w rankingach wyszukiwania Google, ale ten inny nie)

B Niesamowicie szybka, jednoczęściowa skorupa 🚀

Ten skrypt powłoki wyświetla wszystkie obiekty obiektów blob w repozytorium, posortowane od najmniejszych do największych.

W przypadku mojej próbki repozytorium działało około 100 razy szybciej niż inne znalezione tutaj.
W moim zaufanym systemie Athlon II X4 obsługuje on repozytorium jądra systemu Linux z jego 562155 obiektami w nieco ponad minutę .

Skrypt podstawowy

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

Gdy uruchomisz powyżej kodu, uzyskasz ładne, czytelne dla człowieka dane wyjściowe, takie jak to:

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

🚀 Szybkie usuwanie plików 🚀

Załóżmy, że chcesz usunąć pliki ai bprzy każdym dostępnym zatwierdzeniu HEADmożesz użyć tego polecenia:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
Sridhar Sarnobat
źródło
3
Jeśli twoje repozytorium ma jakieś tagi, prawdopodobnie również chcesz dodać flagę, --tag-name-filter cataby ponownie otagować nowe odpowiednie zatwierdzenia, ponieważ są one przepisywane, tj. git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD(Patrz odpowiednia odpowiedź )
naitsirhc
3
Instrukcje dla Maca i niektóre inne informacje pojawiają się w oryginalnie połączonym poście
nruth
3
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEADrobot na prawo od nietoperza
eleijonmarck
moja ulubiona odpowiedź. drobna poprawka do użycia w systemie Mac OS (za pomocą poleceń GNU)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
Florian Oswald
fajny skrypt z listą rev, ale nie działał dla mnie jako alias, jakiś pomysł, jak to zrobić?
Robin Manoli,
47

Po wypróbowaniu praktycznie każdej odpowiedzi w SO, w końcu znalazłem ten klejnot, który szybko usunął i usunął duże pliki z mojego repozytorium i pozwolił mi ponownie zsynchronizować: http://www.zyxware.com/articles/4027/how-to-delete -plik-trwale-z-twoich-lokalnych-i-zdalnych-repozytoriów git

CD na lokalny folder roboczy i uruchom następujące polecenie:

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

zamień FOLDERNAME na plik lub folder, który chcesz usunąć z danego repozytorium git.

Po wykonaniu tej czynności uruchom następujące polecenia, aby wyczyścić lokalne repozytorium:

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

Teraz wypchnij wszystkie zmiany do zdalnego repozytorium:

git push --all --force

Spowoduje to wyczyszczenie zdalnego repozytorium.

Justin
źródło
Dla mnie zadziałało jak urok.
Ramon Vasconcelos
3
To również działało dla mnie. Pozbywa się określonego folderu (w moim przypadku zawierającego zbyt duże pliki lub repozytorium Github) w repozytorium, ale zachowuje go w lokalnym systemie plików na wypadek, gdyby istniał.
skizzo
Pracował dla mnie! nie pozostała żadna historia, która mogłaby być myląca (jeśli ktoś ma teraz klonować), upewnij się, że masz plan aktualizacji wszelkich uszkodzonych linków, zależności itp.
ruoho ruotsi
38

Te polecenia działały w moim przypadku:

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

Różni się niewiele od powyższych wersji.

Dla tych, którzy muszą przekazać to do github / bitbucket (testowałem to tylko z 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
Kostanos
źródło
4
Czym różni się od powyższego, dlaczego jest lepszy?
Andy Hayden
1
Z jakiegoś powodu wersja mkljun nie zmniejsza przestrzeni git w moim przypadku, już usunąłem pliki z indeksu za pomocą git rm --cached files. Twierdzenie Grega Bacona jest bardziej kompletne i całkiem podobne do tej kopalni, ale przeoczył indeks --force dla przypadków, gdy używasz rozgałęzienia filtru wiele razy, i napisał tak wiele informacji, że moja wersja jest jak wznowienie z tego.
Kostanos
1
To naprawdę pomogło, ale musiałem skorzystać z -fopcji nie tylko -rftutaj, git rm --cached -rf --ignore-unmatch oops.isozamiast git rm --cached -r --ignore-unmatch oops.isojak na @ lfender6445 poniżej
drstevok
10

Pamiętaj, że te polecenia mogą być bardzo destrukcyjne. Jeśli więcej osób pracuje nad repozytorium, wszyscy będą musieli ściągnąć nowe drzewo. Trzy środkowe polecenia nie są konieczne, jeśli Twoim celem NIE jest zmniejszenie rozmiaru. Ponieważ gałąź filtra tworzy kopię zapasową usuniętego pliku i może tam pozostać przez długi czas.

$ 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
mkljun
źródło
11
NIE uruchamiaj tych poleceń, chyba że chcesz wywołać dla siebie ogromny ból. Usunęło wiele moich oryginalnych plików kodu źródłowego. Zakładałem, że wyczyści niektóre duże pliki z mojej historii zatwierdzeń w GIT (zgodnie z pierwotnym pytaniem), jednak myślę, że to polecenie ma na celu trwałe usunięcie plików z twojego oryginalnego drzewa kodu źródłowego (duża różnica!). Mój system: Windows, VS2012, Git Source Control Provider.
Contango,
2
Użyłem tego polecenia: git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --allzamiast pierwszego z twojego kodu
Kostanos
8

Jeśli wiesz, że twoje zatwierdzenie było ostatnie zamiast przejścia przez całe drzewo, wykonaj następujące czynności: git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD

Soheil
źródło
7

Natknąłem się na to z kontem bitbucket, na którym przypadkowo zapisałem gigantyczne * .jpa kopie zapasowe mojej witryny.

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

Zmień położenie MY-BIG-DIRECTORYz danym folderem, aby całkowicie przepisać swoją historię (w tym tagi ).

źródło: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/

lfender6445
źródło
1
Ta odpowiedź pomogła mi, ale skrypt w odpowiedzi ma niewielki problem i nie wyszukuje we wszystkich gałęziach, które mnie tworzą. Ale polecenie w linku zrobiło to doskonale.
Ali B
5

Spowoduje to usunięcie z Twojej historii

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
iskierka
źródło
To zadziałało dla mnie dzięki !!
Sonja Brits
To działa w moim przypadku. Uruchomię to w twojej gałęzi master.
S. Domeng
4

Zasadniczo zrobiłem to, co było na tej odpowiedzi: https://stackoverflow.com/a/11032521/1286423

(dla historii skopiuję i wkleję tutaj)

$ 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

Nie działało, ponieważ lubię zmieniać nazwy i przenosić rzeczy. Niektóre duże pliki znajdowały się w folderach, których nazwy zostały zmienione, i myślę, że gc nie mógł usunąć odwołania do tych plików z powodu odwołania w treeobiektach wskazujących na ten plik. Moim najlepszym rozwiązaniem, aby naprawdę to zabić, było:

# 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

Moje repozytorium .gitzmieniło się z 32 MB na 388 KB, że nawet gałąź filtra nie może wyczyścić.

Dolanor
źródło
4

git filter-branchto potężne polecenie, którego można użyć do usunięcia dużego pliku z historii zatwierdzeń. Plik pozostanie przez chwilę, a Git usunie go w następnym śmieciu. Poniżej znajduje się pełny proces usuwania plików z historii zatwierdzeń . Dla bezpieczeństwa, poniżej proces uruchamia najpierw polecenia w nowym oddziale. Jeśli wynik jest tym, czego potrzebujesz, zresetuj go z powrotem do gałęzi, którą faktycznie chcesz zmienić.

# 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
zhangyu12
źródło
2

Użyj Git Extensions , to narzędzie interfejsu użytkownika. Ma wtyczkę o nazwie „Znajdź duże pliki”, która znajduje pliki repozytoriów i umożliwia ich trwałe usunięcie.

Nie używaj „git filter-branch” przed użyciem tego narzędzia, ponieważ nie będzie w stanie znaleźć plików usuniętych przez „filter-branch” (chociaż „filter-branch” nie usuwa plików całkowicie z plików pakietu repozytorium) .

Nir
źródło
Ta metoda jest zbyt wolna dla dużych repozytoriów. Lista dużych plików zajęła ponad godzinę. Następnie, gdy idę do usuwania plików, po godzinie jest to tylko 1/3 czasu przetwarzania pierwszego pliku, który chcę usunąć.
kristianp
Tak, jest powolny, ale działa ... Czy wiesz coś szybciej?
Nir,
1
Nie korzystałem z niego, ale BFG Repo-Cleaner, zgodnie z inną odpowiedzią na tej stronie.
kristianp
2

Możesz to zrobić za pomocą branch filterpolecenia:

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

John Foley
źródło
2

W tym wątku są bardzo dobre odpowiedzi, ale w międzyczasie wiele z nich jest nieaktualnych. Używanie git-filter-branchnie jest już zalecane, ponieważ jest trudne w użyciu i strasznie powolne w dużych repozytoriach.

git-filter-repo jest znacznie szybszy i prostszy w użyciu.

git-filter-repoto skrypt Pythona, dostępny na github: https://github.com/newren/git-filter-repo .

Potrzebujesz tylko jednego pliku: skrypt Python3 git-filter-repo. Skopiuj go do ścieżki zawartej w zmiennej PATH. W systemie Windows może być konieczna zmiana pierwszego wiersza skryptu (patrz INSTALL.md). Potrzebujesz zainstalowanego Python3 zainstalowanego w twoim systemie, ale to nie jest wielka sprawa.

Najpierw możesz biec

git filter-repo --analyze

Pomoże to ustalić, co dalej.

Możesz usunąć plik zgrywający DVD wszędzie:

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

Filtrowanie repo jest naprawdę szybkie. Zadanie, które zajęło około 9 godzin na moim komputerze według gałęzi filter, zostało zakończone w 4 minuty przez filter-repo. Z filtrem-repo możesz zrobić wiele innych fajnych rzeczy. Informacje na ten temat można znaleźć w dokumentacji.

Ostrzeżenie: zrób to na kopii swojego repozytorium. Wiele działań filtrowania repo nie może zostać cofnięte. filter-repo zmieni skróty zatwierdzania wszystkich zmodyfikowanych zatwierdzeń (oczywiście) i wszystkich ich potomków aż do ostatnich zatwierdzeń!

Donat
źródło
1

Kiedy napotkasz ten problem, git rmnie wystarczy, ponieważ git pamięta, że ​​plik istniał kiedyś w naszej historii, i dlatego zachowa odniesienie do niego.

Co gorsza, zmiana bazy danych również nie jest łatwa, ponieważ wszelkie odwołania do obiektu blob zapobiegną czyszczeniu przestrzeni przez git garbage collectora. Obejmuje to odwołania zdalne i odnośniki ponownego logowania.

Złożyłem razem git forget-blob, mały skrypt, który próbuje usunąć wszystkie te referencje, a następnie używa git filter-branch do przepisania każdego zatwierdzenia w gałęzi.

Gdy twój obiekt blob będzie całkowicie niepowiązany, git gcpozbędziesz się go

Użycie jest dość proste git forget-blob file-to-forget. Możesz uzyskać więcej informacji tutaj

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

Złożyłem to razem dzięki odpowiedziom z Przepełnienia stosu i niektórym wpisom blogu. Kredyty dla nich!

nachoparker
źródło
powinieneś dostać to w homebrew
Cameron E
0

Poza git filter-branch(powolnym, ale czystym rozwiązaniem git) i BFG (łatwiejszym i bardzo wydajnym) istnieje również inne narzędzie do filtrowania z dobrą wydajnością:

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

Z jego opisu:

Cel git-rocket-filter jest podobny do polecenia git-filter-branch, zapewniając następujące unikalne funkcje:

  • Szybkie przepisywanie zmian i drzew (w kolejności od x10 do x100).
  • Wbudowana obsługa zarówno białej listy z opcją --keep (przechowuje pliki lub katalogi), jak i czarnej listy z opcjami --remove.
  • Użycie .gitignore jak wzorzec do filtrowania drzew
  • Szybkie i łatwe wykonywanie skryptów w języku C # zarówno dla filtrowania zmian, jak i dla drzewa
  • Obsługa skryptów w filtrowaniu drzew według wzorca pliku / katalogu
  • Automatycznie przycinaj puste / niezmienione zatwierdzenia, w tym zatwierdzenia scalania
Philippe
źródło