Jak usunąć / usunąć duży plik z historii zatwierdzeń w repozytorium Git?
Czasami wrzuciłem DVD-rip do projektu strony internetowej, potem niedbale git commit -a -m ...
, i, zap, repo było rozdęte przez 2,2 GIGA. Następnym razem zrobiłem kilka zmian, usunąłem plik wideo i zatwierdziłem wszystko, ale skompresowany plik nadal jest w repozytorium, w historii.
Wiem, że mogę uruchamiać gałęzie z tych commitów i zamieniać jedną gałąź na inną. Ale co mam zrobic aby scalic razem 2 commity tak aby duzy plik nie pojawil sie w historii i zostal wyczyszczony w procedura zbierania śmieci?
14 answers
Użyj BFG Repo-Cleaner, prostszej i szybszej alternatywy dla git-filter-branch
, zaprojektowanej specjalnie do usuwania niechcianych plików z historii Gita.
Uważnie postępuj zgodnie z instrukcjami użycia , podstawowa część jest taka:
$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
Wszystkie pliki o rozmiarze powyżej 100MB (które nie znajdują się w Twoim najnowszym commicie) zostaną usunięte z historii Twojego repozytorium Git. Następnie możesz użyć git gc
, aby wyczyścić martwe DANE:
$ git gc --prune=now --aggressive
BFG jest zazwyczaj co najmniej 10-50x szybszy niż bieganie git-filter-branch
i ogólnie łatwiejszy w użyciu.
pełne ujawnienie: jestem autorem BFG Repo-Cleaner.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-20 21:44:20
To, co chcesz zrobić, jest bardzo uciążliwe, jeśli opublikowałeś historię innym programistom. Zobacz "odzyskiwanie z Upstream Rebase" w git rebase
dokumentacja dla niezbędnych kroków po naprawie historii.
Masz co najmniej dwie opcje: git filter-branch
i interaktywną rebase, obie opisane poniżej.
Za pomocą git filter-branch
Miałem podobny problem z nieporęcznymi binarnymi danymi testowymi z importu Subversion i napisałem o usunięciu danych z Gita repozytorium .
Powiedz, że Twoja historia Gita 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
Zauważ, że git lola
jest niestandardowym, ale bardzo przydatnym aliasem. Za pomocą przełącznika --name-status
możemy zobaczyć modyfikacje drzewa powiązane z każdym zatwierdzeniem.
W commicie "Careless" (którego obiekt SHA1 nazywa się ce36c98) plik oops.iso
jest plikiem DVD-rip dodanym przypadkowo i usuniętym w następnym commicie, cb14efd. Stosując technikę opisaną we wspomnianym poście na blogu, polecenie do wykonania jest:
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-empty
usuwa commity, które stają się puste (tzn., nie zmieniają drzewa) w wyniku działania filtra. W typowym przypadku ta opcja tworzy czystszą historię. -
-d
nazywa tymczasowy katalog, który jeszcze nie istnieje, aby użyć go do budowania filtrowanej historii. Jeśli używasz nowoczesnej dystrybucji Linuksa, podanie drzewa w/dev/shm
spowoduje szybsze wykonanie. -
--index-filter
jest głównym wydarzeniem i biegnie przed indeksem na każdym kroku w historii. Chcesz usunąćoops.iso
gdziekolwiek zostanie znaleziony, ale nie jest obecny we wszystkich commitach. Poleceniegit rm --cached -f --ignore-unmatch oops.iso
usuwa plik DVD-rip, gdy jest obecny i nie zawodzi w przeciwnym razie. -
--tag-name-filter
opisuje sposób przepisywania nazw znaczników. Filtrcat
jest operacją tożsamości. Twoje repozytorium, podobnie jak przykład powyżej, może nie mieć żadnych tagów, ale włączyłem tę opcję dla pełnej ogólności. -
--
określa koniec opcje dogit filter-branch
-
--all
po--
jest skrótem dla wszystkich refów. Twoje repozytorium, podobnie jak przykład powyżej, może mieć tylko jeden ref (master), ale włączyłem tę opcję dla pełnej ogólności.
Po pewnym ubijaniu, 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 nowy commit "Careless" dodaje tylko other.html
i że commit "Remove DVD-rip" nie znajduje się już w gałęzi master. Gałąź oznaczona refs/original/refs/heads/master
zawiera oryginalne commity na wypadek, gdybyś popełnił błąd. Na usuń go, wykonaj kroki z "listy kontrolnej zmniejszania repozytorium."
$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now
Aby uzyskać prostszą alternatywę, Sklonuj repozytorium, aby usunąć niechciane bity.
$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
Użycie adresu URL file:///...
do klonowania kopiuje obiekty, a nie tylko tworzy hardlinki.
Teraz twoja historia jest:
$ 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 commitów ("Index "i" admin page") pozostały takie same, ponieważ operacja filtrowania nie zmodyfikowała tych commitów. "Careless" lost oops.iso
i "Strona logowania" ma nowego rodzica, więc ich SHA1s zmieniły się.
Interactive rebase
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.iso
z "nieostrożnego", jakbyś nigdy go nie dodał, a następnie "usuń DVD-rip" jest bezużyteczny dla Ciebie. Tak więc nasz plan przechodzenia do interaktywnej rebazy polega na utrzymaniu "strony administratora", edycji " nieostrożnej "i odrzuceniu" Usuń DVD-rip."
Uruchamianie $ git rebase -i 5af4522
uruchamia edytor z następującym zawartość.
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 linię z "Remove DVD-rip " i zmieniamy operację na" Careless " na edit
zamiast pick
.
Save-zamknięcie edytora powoduje wyświetlenie wiersza poleceń 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ść, jesteśmy na" nieostrożnym " commicie, który chcemy edytować, więc uruchamiamy dwa polecenia.
$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
Pierwszy usuwa plik z indeksu. Na druga modyfikuje lub zmienia "Careless" na zaktualizowany indeks i -C HEAD
instruuje git, aby ponownie użył starego komunikatu commit. W końcu git rebase --continue
kontynuuje 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
Tego właśnie chcesz.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-20 10:04:24
Dlaczego nie użyć tego prostego, ale potężnego polecenia?
git filter-branch --tree-filter 'rm -f DVD-rip' HEAD
Opcja --tree-filter
uruchamia podaną komendę po każdej kasie projektu, a następnie wznawia wyniki. W takim przypadku usuwasz plik o nazwie DVD-rip z każdej migawki, niezależnie od tego, czy istnieje, czy nie.
Zobacz ten link .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-27 02:39:52
Te komendy zadział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
Niewiele różni się od powyższych wersji.
Dla tych, którzy muszą wcisnąć to na 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-23 15:54:06
(najlepsza odpowiedź jaką widziałem na ten problem to: https://stackoverflow.com/a/42544963/714112 , skopiowany tutaj, ponieważ ten wątek pojawia się wysoko w rankingach wyszukiwarek Google, ale ten drugi nie)
Blazngly fast shell one-liner
Ten skrypt powłoki wyświetla wszystkie obiekty blob w repozytorium, posortowane od najmniejszego do największego.
Dla mojej próbki repo, to działało około 100 razy szybciej niż inne znalezione tutaj.
Na moim zaufanym Athlonie II X4 system obsługuje repozytorium jądra Linuksa z 5 622 155 obiektami w nieco ponad minutę.
Skrypt Bazowy
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ższy kod, otrzymasz ładne czytelne dla człowieka wyjście w następujący sposób:
...
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 a
i b
z każdego commita osiągalnego z HEAD
, możesz użyć tego polecenia:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-07 00:37:00
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 w moim repozytorium i pozwolił mi zsynchronizować ponownie: http://www.zyxware.com/articles/4027/how-to-delete-files-permanently-from-your-local-and-remote-git-repositories
CD do lokalnego folderu roboczego i uruchom następujące polecenie:
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all
Zastąp FOLDERNAME plikiem lub folderem, który chcesz usunąć z podanego repozytorium git.
Once w tym celu 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
To wyczyści zdalne repozytorium.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-26 17:35:48
git filter-branch --tree-filter 'rm -f path/to/file' HEAD
dla mnie zadziałało całkiem dobrze, chociaż napotkałem ten sam problem, co opisany TUTAJ , który rozwiązałem podążając za tą sugestią.
Książka pro-git zawiera cały rozdział o przepisywaniu historii - zajrzyj do filter-branch
/Usuwanie pliku z każdej sekcji Commit .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:57
Zauważ, że te polecenia mogą być bardzo destrukcyjne. Jeśli więcej ludzi pracuje nad repo, wszyscy będą musieli wycią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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 15:41:05
Jeśli wiesz, że Twój commit został niedawno, zamiast przechodzić przez całe drzewo, wykonaj następujące czynności:
git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-01 06:21:33
Natknąłem się na to z kontem bitbucket, na którym przypadkowo zapisałem gigantyczne*.kopie zapasowe jpa mojej strony.
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
z danym folderem, aby całkowicie przepisać historię ( wraz z tagami ).
Źródło: http://naleid.com/blog/2012/01/17/finding-and-purging-big-files-from-git-history
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-03 12:54:26
Możesz to zrobić używając branch filter
komendy:
git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-05 23:28:30
Użyj Git Extensions , jest to narzędzie interfejsu użytkownika. Posiada wtyczkę o nazwie "Find large files", która znajduje pliki lage w repozytoriach i umożliwia ich trwałe usuwanie.
Nie używaj 'git filter-branch' przed użyciem tego narzędzia, ponieważ nie będzie ono w stanie znaleźć plików usuniętych przez 'filter-branch' (oprócz 'filter-branch' nie usuwa całkowicie plików z repozytorium pack files).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-31 13:22:11
Gdy napotkasz ten problem, git rm
nie wystarczy, ponieważ git pamięta, że plik istniał raz w naszej historii i dlatego zachowa do niego odniesienie.
Co gorsza, rebasing również nie jest łatwy, ponieważ wszelkie odniesienia do obiektu blob zapobiegną czyszczeniu przestrzeni przez git garbage collector. Obejmuje to odwołania zdalne i odwołania reflog.
Stworzyłem git forget-blob
mały skrypt, który próbuje usunąć wszystkie odnośniki, a następnie używa git filter-branch aby przepisać każdy commit w gałęzi.
Gdy twój blob będzie całkowicie niezrealizowany, git gc
pozbędzie się go
Użycie jest dość proste git forget-blob file-to-forget
. Więcej informacji znajdziesz tutaj
Poskładałem to do kupy dzięki odpowiedziom ze Stack Overflow i kilku wpisom na blogu. Podziękowania dla nich!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-23 12:21:46
W zasadzie zrobiłem to, co było na tej odpowiedzi: https://stackoverflow.com/a/11032521/1286423
(dla historii, skopiuję-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 zadziałało, ponieważ lubię zmieniać nazwy i przenosić rzeczy. Więc jakiś duży plik był w folderach, które zostały przemianowane i myślę, że gc nie mógł usunąć odniesienia do tych plików z powodu odniesienia w obiektach tree
wskazujących na te pliki.
Moim ostatecznym rozwiązaniem do naprawdę zabicia 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
My repo (the .git
) zmieniono z 32MB na 388KB, że nawet filter-branch nie mógł wyczyścić.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-31 14:22:12