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?
git filter-branch
, ale uważam, że jest odwrotnie.Odpowiedzi:
Użyj BFG Repo-Cleaner , prostszej, szybszej alternatywy dla
git-filter-branch
specjalnie zaprojektowanej do usuwania niechcianych plików z historii Git.Dokładnie postępuj zgodnie z instrukcjami użytkowania , podstawowa część jest taka:
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 gc
do usunięcia martwych danych:BFG jest zwykle co najmniej 10-50 razy szybszy niż bieganie
git-filter-branch
i ogólnie łatwiejszy w użyciu.Pełne ujawnienie: jestem autorem BFG Repo-Cleaner.
źródło
git push --force
po twoich krokach, w przeciwnym razie zdalne repo nadal się nie zmieni.git push --force
. Warto również zauważyć: wymuszone wypychanie może być niedozwolone przez zdalne (gitlab.com domyślnie nie. Musiał „odblokować” gałąź).To, co chcesz zrobić, jest bardzo destrukcyjne, jeśli opublikujesz historię innym programistom. Zobacz „Odzyskiwanie z wcześniejszego uruchomienia” w
git rebase
dokumentacji, aby dowiedzieć się, jakie kroki należy wykonać po naprawieniu historii.Masz co najmniej dwie opcje:
git filter-branch
i 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:
Pamiętaj, że
git lola
jest to niestandardowy, ale bardzo użyteczny alias. Za pomocą--name-status
przełą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.iso
to zgrywanie DVD dodane przypadkowo i usunięte w następnym zatwierdzeniu, cb14efd. Korzystając z techniki opisanej we wspomnianym blogu, polecenie do wykonania to:Opcje:
--prune-empty
usuwa zatwierdzenia, które stają się puste ( tj. nie zmieniają drzewa) w wyniku operacji filtrowania. W typowym przypadku ta opcja zapewnia czystszą historię.-d
nazywa 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/shm
spowoduje szybsze wykonanie .--index-filter
jest głównym wydarzeniem i działa na podstawie indeksu na każdym etapie historii. Chcesz usunąć,oops.iso
gdziekolwiek się znajdzie, ale nie jest obecny we wszystkich zatwierdzeniach. Poleceniegit rm --cached -f --ignore-unmatch oops.iso
usuwa zgrywanie DVD, gdy jest obecne, i w przeciwnym razie nie zawiedzie.--tag-name-filter
opisuje, jak przepisać nazwy znaczników. Filtrcat
to 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 dlagit filter-branch
--all
nastę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:
Zauważ, że nowe zatwierdzenie „Nieostrożne” dodaje tylko
other.html
i że zatwierdzenie „Usuń DVD-rip” nie jest już w gałęzi master. Oddział oznaczonyrefs/original/refs/heads/master
zawiera oryginalne zatwierdzenia na wypadek pomyłki. Aby go usunąć, wykonaj czynności opisane w „Liście kontrolnej zmniejszania repozytorium”.Dla prostszej alternatywy sklonuj repozytorium, aby odrzucić niechciane bity.
Użycie
file:///...
sklonowanego adresu URL powoduje kopiowanie obiektów zamiast tworzenia wyłącznie linków stałych.Teraz twoja historia to:
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.iso
a „Strona logowania” ma nowego rodzica, więc ich SHA1 się zmieniły.Interaktywna baza danych
Z historią:
chcesz usunąć
oops.iso
z „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 5af4522
uruchamia edytor o następującej treści.Realizując nasz plan, modyfikujemy go do
Oznacza to, że usuwamy wiersz za pomocą polecenia „Remove DVD-rip” i zmieniamy operację na „Careless” na
edit
zamiastpick
.Opuszczenie edytora powoduje wyświetlenie wiersza polecenia z następującym komunikatem.
Jak mówi nam wiadomość, wykonujemy zatwierdzenie „Nieostrożne”, które chcemy edytować, więc uruchamiamy dwa polecenia.
Pierwszy usuwa szkodliwy plik z indeksu. Drugi modyfikuje lub zmienia „Nieostrożny”, aby był zaktualizowanym indeksem i
-C HEAD
instruuje git, aby ponownie użył starej wiadomości zatwierdzenia. Wreszcie,git rebase --continue
kontynuuje resztę operacji rebase.To daje historię:
co chcesz.
źródło
-f
(lub--force
) do swojegogit push
polecenia: „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. ”... "git rm --cached -rf --ignore-unmatch path/to/dir"...
Dlaczego nie skorzystać z tego prostego, ale potężnego polecenia?
--tree-filter
Opcja 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 .
źródło
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
commit
, gdzie można umieścić w pliku (powiedzmy35dsa2
), można zastąpićHEAD
z35dsa2..HEAD
.tree-filter
jest znacznie wolniejszy niż windex-filter
ten sposób, nie będzie próbował sprawdzić wszystkich zmian i przepisać je. jeśli użyjesz HEAD, spróbuje to zrobić.(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
Gdy uruchomisz powyżej kodu, uzyskasz ładne, czytelne dla człowieka dane wyjściowe, takie jak to:
🚀 Szybkie usuwanie plików 🚀
Załóżmy, że chcesz usunąć pliki
a
ib
przy każdym dostępnym zatwierdzeniuHEAD
możesz użyć tego polecenia:źródło
--tag-name-filter cat
aby 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ź )git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEAD
robot na prawo od nietoperzagit 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
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:
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:
Teraz wypchnij wszystkie zmiany do zdalnego repozytorium:
Spowoduje to wyczyszczenie zdalnego repozytorium.
źródło
Te polecenia działały w moim przypadku:
Różni się niewiele od powyższych wersji.
Dla tych, którzy muszą przekazać to do github / bitbucket (testowałem to tylko z bitbucket):
źródło
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.-f
opcji nie tylko-rf
tutaj,git rm --cached -rf --ignore-unmatch oops.iso
zamiastgit rm --cached -r --ignore-unmatch oops.iso
jak na @ lfender6445 poniżejPamię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.
źródło
git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
zamiast pierwszego z twojego kodugit filter-branch --tree-filter 'rm -f path/to/file' HEAD
działało dla mnie całkiem dobrze, chociaż napotkałem ten sam problem, jak tutaj opisany , który rozwiązałem, stosując się do tej sugestii .Książka pro-git zawiera cały rozdział dotyczący przepisywania historii - spójrz na sekcję
filter-branch
/ Usuwanie pliku z każdego zatwierdzenia .źródło
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
źródło
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-DIRECTORY
z 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/
źródło
Spowoduje to usunięcie z Twojej historii
źródło
Zasadniczo zrobiłem to, co było na tej odpowiedzi: https://stackoverflow.com/a/11032521/1286423
(dla historii skopiuję i wkleję tutaj)
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
tree
obiektach wskazujących na ten plik. Moim najlepszym rozwiązaniem, aby naprawdę to zabić, było:Moje repozytorium
.git
zmieniło się z 32 MB na 388 KB, że nawet gałąź filtra nie może wyczyścić.źródło
git filter-branch
to 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ć.źródło
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) .
źródło
Możesz to zrobić za pomocą
branch filter
polecenia:git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
źródło
W tym wątku są bardzo dobre odpowiedzi, ale w międzyczasie wiele z nich jest nieaktualnych. Używanie
git-filter-branch
nie 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-repo
to 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
Pomoże to ustalić, co dalej.
Możesz usunąć plik zgrywający DVD wszędzie:
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ń!
źródło
Kiedy napotkasz ten problem,
git rm
nie 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 gc
pozbędziesz się goUżycie jest dość proste
git forget-blob file-to-forget
. Możesz uzyskać więcej informacji tutajhttps://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!
źródło
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:źródło