Sposoby poprawy wydajności statusu git

80

Mam repozytorium 10 GB na komputerze z systemem Linux na NFS. Pierwszy raz git statuszajmuje 36 minut, a kolejny git status8 minut. Wygląda na to, że Git zależy od systemu operacyjnego do buforowania plików. Tylko pierwsze gitpolecenia podoba commit, statusże obejmuje pakiet / zapakować cały repo trwa bardzo długo na ogromnym repo. Nie jestem pewien, czy używałeś git statusna tak dużym repozytorium, ale czy ktoś spotkał się z tym problemem?

Próbowałem git gc, git clean, git repackale czas potrzebny jest jeszcze / prawie tak samo.

Czy podmoduły lub inne koncepcje, takie jak rozbicie repozytorium na mniejsze, pomogą? Jeśli tak, który jest najlepszy do podziału większego repozytorium. Czy istnieje inny sposób na skrócenie czasu potrzebnego na polecenia git w dużym repozytorium?

Senthil A Kumar
źródło
2
NFS jest tutaj raczej wąskim gardłem. lstat jest dość synchroniczną operacją.
user611775
1
Ewentualny duplikat statusu Git zajmuje dużo czasu
Seth Battin,

Odpowiedzi:

45

Aby być bardziej precyzyjnym, git zależy od wydajności lstat(2)wywołania systemowego, więc poprawianie „limitu czasu pamięci podręcznej atrybutów” klienta może załatwić sprawę.

Podręcznik dla git-update-index- zasadniczo tryb ręczny dla git-status- opisuje, co możesz zrobić, aby temu zaradzić, używając --assume-unchangedflagi, aby powstrzymać jego normalne zachowanie i ręcznie zaktualizować ścieżki, które zmieniłeś. Możesz nawet zaprogramować swój edytor tak, aby usuwał tę flagę za każdym razem, gdy zapisujesz plik.

Alternatywą, jak sugerujesz, jest zmniejszenie rozmiaru kasy (rozmiar plików paczek tak naprawdę nie ma tutaj znaczenia). Dostępne opcje to rzadkie płatności, moduły podrzędne lub narzędzie repozytorium Google .

(Istnieje wątek na liście dyskusyjnej o używaniu Git z NFS , ale nie zawiera on odpowiedzi na wiele pytań).

Josh Lee
źródło
31
To, co przegapiłeś: łatka Linusa faktycznie została scalona i można ją włączyć, ustawiając ją core.preloadindexna true - zobacz git-configdokumentację, aby uzyskać trochę więcej opisu. (Moje miejsce pracy korzysta z NFS i napotkałem dokładnie ten problem - ale nigdy nie zauważyłem ustawienia preloadindex. Dziękujemy za wskazanie mi właściwej drogi!)
Cascabel,
1
Do zaakceptowanej odpowiedzi w tym miejscu należy dodać „git config core.preloadindex true”. prawdopodobnie z flagą -uno od user1077329
ostler.c
2
core.preloadindexflaga jest domyślnie ustawiona na true od Git 2.1.0: git.kernel.org/pub/scm/git/git.git/tree/Documentation/RelNotes/…
Petr Gazarov
38

Widzę również ten problem w dużym projekcie udostępnianym przez NFS.

Zajęło mi trochę czasu, zanim odkryłem flagę -uno, którą można nadać zarówno git commit, jak i git status.

Ta flaga wyłącza wyszukiwanie nieśledzonych plików. Zmniejsza to znacznie liczbę operacji nfs. Powodem jest to, że aby git mógł wykryć nieśledzone pliki, musi przeszukać wszystkie podkatalogi, więc jeśli masz wiele podkatalogów, zaszkodzi ci to. Wyłączając git możliwość wyszukiwania nieśledzonych plików, eliminujesz wszystkie te operacje NFS.

Połącz to z flagą core.preloadindex, a uzyskasz rozsądną wydajność nawet na NFS.

user1077329
źródło
Jak wspomniano w git-status (1) , można go ustawić jako domyślny, ustawiając status.showUntrackedFilesplik config.
johankj
33

Spróbuj git gc . Pomocne może też być git clean .

AKTUALIZACJA - Nie jestem pewien, skąd się wzięło głosowanie negatywne, ale podręcznik git wyraźnie stwierdza:

Uruchamia szereg zadań porządkowych w bieżącym repozytorium, takich jak kompresowanie wersji plików (w celu zmniejszenia miejsca na dysku i zwiększenia wydajności ) i usuwanie nieosiągalnych obiektów, które mogły zostać utworzone w wyniku wcześniejszych wywołań git add.

Zachęca się użytkowników do regularnego uruchamiania tego zadania w każdym repozytorium, aby utrzymać dobre wykorzystanie miejsca na dysku i dobrą wydajność operacyjną.

Zawsze zauważam różnicę po uruchomieniu git gc, gdy status git jest wolny!

UPDATE II - Nie wiem, jak to przegapiłem, ale OP już próbował git gci git clean. Przysięgam, że tego nie było, ale nie widzę żadnych zmian w edycjach. Przepraszam za to!

Jabari
źródło
5
Nie rozumiem też głosów przeciw; to jest naprawdę pomocne. git gcskrócić czas git loguruchamiania z 15 sekund do 0 w jednym z moich repozytoriów.
GreenRaccoon 23
@NicolasC Ah! Nie jestem pewien, jak to przegapiłem, ale zagłosowałbym również na moją odpowiedź. : - /
Jabari
1
git cg jest dobry, może git clean może usunąć niechciany plik?
Luca Reghellin
18

Jeśli Twoje repozytorium git w dużym stopniu korzysta z modułów podrzędnych, możesz znacznie przyspieszyć działanie statusu git, edytując plik konfiguracyjny w katalogu .git i ustawiając ignore = dirtydowolne szczególnie duże / ciężkie moduły podrzędne. Na przykład:

[submodule "mysubmodule"]
url = ssh://mysubmoduleURL
ignore = dirty

Stracisz wygodę przypomnienia, że ​​w którymkolwiek z modułów podrzędnych nastąpiły niestacjonarne zmiany, o których być może zapomniałeś, ale nadal zachowasz główną wygodę, jaką jest wiedza, kiedy podmoduły nie są zsynchronizowane z głównym repozytorium. Ponadto nadal możesz zmienić swój katalog roboczy na sam moduł podrzędny i jak zwykle używać w nim statusu git, aby zobaczyć więcej informacji. Zobacz to pytanie, aby uzyskać więcej informacji o tym, co oznacza „brudny”.

beno
źródło
7

Wydajność statusu git powinna ulec poprawie w Git 2.13 (Q2 2017).

Zobacz commit 950a234 (14 kwietnia 2017) autorstwa Jeffa Hostetlera ( jeffhostetler) .
(Scalone przez Junio ​​C Hamano - gitster- w zatwierdzeniu 8b6bba6 , 24 kwietnia 2017)

> string-list: użyj ALLOC_GROWmakra podczas ponownego przydzielaniastring_list

Użyj ALLOC_GROW()makra podczas ponownego przydzielania string_listtablicy, zamiast po prostu zwiększać ją o 32.
Jest to optymalizacja wydajności.

W przypadku statusu bardzo dużego repozytorium i wielu zmian znaczny procent całkowitego czasu wykonywania jest poświęcany na ponowne przydzielanie wt_status.changesmacierzy .

Ta zmiana skraca czas wt_status_collect_changes_worktree()z 125 sekund do 45 sekund w moim bardzo dużym repozytorium.


Dodatkowo, Git 2.17 (Q2 2018) wprowadzi nowy ślad, do pomiaru czasu spędzanego na operacjach z dużą liczbą indeksów.

Zobacz commit ca54d9b (27 stycznia 2018) autorstwa Nguyễn Thái Ngọc Duy ( pclouds) .
(Scalone przez Junio ​​C Hamano - gitster- w zatwierdzeniu 090dbea , 15 lutego 2018 r.)

trace: miara, gdzie czas jest spędzany na operacjach z dużą liczbą indeksów

Mierzone są wszystkie znane ciężkie bloki kodu (z wyjątkiem dostępu do obiektowej bazy danych). Powinno to pomóc określić, czy optymalizacja jest skuteczna, czy nie.
Niezoptymalizowany status gita dałby coś takiego jak poniżej:

0.001791141 s: read cache ...
0.004011363 s: preload index
0.000516161 s: refresh index
0.003139257 s: git command: ... 'status' '--porcelain=2'
0.006788129 s: diff-files
0.002090267 s: diff-index
0.001885735 s: initialize name hash
0.032013138 s: read directory
0.051781209 s: git command: './git' 'status'

Ten sam Git 2.17 (Q2 2018) poprawia się git statusdzięki:

revision.c: redukcja zapytań do bazy danych obiektów

W programie mark_parents_uninteresting()sprawdzamy istnienie pliku obiektowego, aby zobaczyć, czy powinniśmy traktować zatwierdzenie jako przeanalizowane. Rezultatem jest ustawienie bitu „przeanalizowanego” w zatwierdzeniu.

Zmodyfikuj warunek, aby sprawdzić tylko, has_object_file()czy wynik zmieni przeanalizowany bit.

Kiedy lokalna gałąź różni się od jej odniesienia nadrzędnego, " git status" obliczy liczniki z wyprzedzeniem / opóźnieniem.
To używa paint_down_to_common()i uderza mark_parents_uninteresting().

Na kopii repozytorium Linuksa z lokalną instancją „master” za zdalną gałęzią „ origin/master” przy ~ 60 000 zatwierdzeń, okazało się, że wydajność „ git status” spadła z 1,42 sekundy do 1,32 sekundy, przy względnej różnicy -7,0%.


Git 2.24 (Q3 2019) proponuje inne ustawienie poprawiające git statuswydajność:

Zobacz commit aaf633c , commit c6cc4c5 , commit ad0fb65 , commit 31b1de6 , commit b068d9a , commit 7211b9e (13 sierpnia 2019) autorstwa Derrick Stolee ( derrickstolee) .
(Scalone przez Junio ​​C Hamano - gitster- w zatwierdzeniu f4f8dfe , 09 września 2019)

repo-settings: utwórz ustawienie feature.manyFiles

To feature.manyFilesustawienie jest odpowiednie dla repozytoriów z wieloma plikami w katalogu roboczym.
Ustawiając index.version=4i core.untrackedCache=true, polecenia takie jak „ git status” powinny ulec poprawie.

Ale:

W Git 2.24 (Q4 2019) ścieżka kodu odczytująca index.versionkonfigurację została zerwana podczas niedawnej aktualizacji, która została poprawiona.

Zobacz commit c11e996 (23 października 2019) autorstwa Derrick Stolee ( derrickstolee) .
(Scalone przez Junio ​​C Hamano - gitster- w zatwierdzeniu 4d6fb2b , 24 października 2019)

repo-settings: przeczytaj int dla index.version

Podpisał: Derrick Stolee

Kilka opcji konfiguracyjnych zostało połączonych w repo_settingsstrukturę w ds / feature-macros, w tym przeniesienie ustawienia konfiguracyjnego „index.version” w 7211b9e („ repo-settings: Consolidate some config settings”, 2019-08-13, Git v2.24.0-rc1 - scalenie wymienione w partii nr 0 ).

Niestety, ten plik wyglądał jak wiele standardowych szablonów i co jest oczywistym czynnikiem przeciążenia kopiuj-wklej, ustawienie konfiguracyjne jest analizowane za pomocą repo_config_ge_bool()zamiast repo_config_get_int(). Oznacza to, że ustawienie „index.version = 4” nie rejestrowałoby się poprawnie i przywracałoby domyślną wersję 3.

Złapałem to podczas włączania wersji 2.24.0-rc0 do bazy kodu VFS dla Git, gdzie naprawdę zależy nam, aby indeks był w wersji 4.

Nie zostało to przechwycone przez bazę kodów, ponieważ sprawdzanie wersji wprowadzone w t1600-index.shnie przetestowało wystarczająco scenariusza „podstawowego”. Tutaj modyfikujemy test, aby uwzględnić te normalne ustawienia, aby nie były zastępowane przez features.manyFileslub GIT_INDEX_VERSION.
Chociaż „domyślną” wersją jest 3, jest ona obniżana do wersji 2, do_write_index()gdy nie jest to konieczne.

VonC
źródło
Zobacz też stackoverflow.com/a/43667992/6309 i nowe index.threadsustawienie konfiguracji
VonC
GIT_TRACE = true git log W ten sposób uruchamiasz śledzenie i znajdujesz wąskie gardło
dhavale
@dhavale Właściwie od Git .22 masz również trace2: stackoverflow.com/a/56094711/6309
VonC
4

git config --global core.preloadIndex true

Wykonał robotę za mnie. Sprawdź oficjalną dokumentację tutaj .

klimat
źródło
Jakiej wersji Git używasz?
VonC,
2.7.4. Używam podsystemu Linux dla Windows i nawet zaktualizowany apt-getwydaje się mieć odniesienia do dość starych pakietów.
klimat
1
Ok, ma sens. Myślę, że nie jest to potrzebne w nowszej wersji.
VonC
To nawet pomogło mi z wersją gita 2.17.1
Markus Zeller
1

W naszej bazie kodów, w której mamy gdzieś w zakresie 20-30 podmodułów,
git status --ignore-submodules
znacznie przyspieszyło to dla mnie. Zwróć uwagę, że nie spowoduje to raportu o stanie podmodułów .

otoczone miastem
źródło
1

Coś, o czym jeszcze nie zostało wspomniane, to aktywacja pamięci podręcznej systemu plików na komputerach z systemem Windows (systemy plików linux są zupełnie inne i git został dla nich zoptymalizowany, dlatego prawdopodobnie pomaga to tylko w systemie Windows).

git config core.fscache true


W ostateczności, jeśli git nadal działa wolno, można wyłączyć kontrolę czasu modyfikacji, aby git musiał dowiedzieć się, które pliki uległy zmianie.

git config core.ignoreStat true

ALE: Zmienione pliki muszą być później dodane przez samego programistę za pomocą git add. Git sam nie znajduje zmian.

źródło

dCSeven
źródło
Pomogło mi to w systemie Windows 10, mimo że miałem dość nowszą wersję Git dla systemu Windows. Dziękuję Ci. Moje repozytorium to ~ 100 GB w folderze .git (git lfs)
Alex Sorokoletov
0

Pozostałe index.lockpliki

git statusmoże być patologicznie powolny w przypadku pozostawienia index.lockplików.

Dzieje się tak zwłaszcza, gdy masz git submodules, bo wtedy często nie zauważysz takich plików po lewej stronie.

Podsumowanie: Uruchom find .git/ -name index.locki usuń pozostałe pliki po sprawdzeniu, czy rzeczywiście nie są używane przez żaden aktualnie działający program.


Detale

Zauważyłem, że mój status git powłoki był bardzo powolny w moim repozytorium, z git 2.19 na Ubuntu 16.04.

Wkopałem się i stwierdziłem, że /usr/bin/time git statusw moim assetsmodule podrzędnym git zajęło to 1,7 sekundy.

Znaleziony z stracetym gitem, przeczytaj wszystkie moje duże pliki z mmap. Zwykle tego nie robi, zwykle statwystarczy.

Przeszukałem problem w Google i znalazłem problem z użyciem indeksu i Racy Git .

Próbowałem git update-index somefile(w moim przypadku gitignorew kasie modułu podrzędnego) pokazane tutaj, ale nie udało się

fatal: Unable to create '/home/niklas/src/myproject/.git/modules/assets/index.lock': File exists.

Another git process seems to be running in this repository, e.g.
an editor opened by 'git commit'. Please make sure all processes
are terminated then try again. If it still fails, a git process
may have crashed in this repository earlier:
remove the file manually to continue.

To klasyczny błąd. Zwykle zauważasz to podczas dowolnej operacji git, ale w przypadku modułów podrzędnych, do których często się nie angażujesz, możesz nie zauważyć tego przez miesiące, ponieważ pojawia się tylko podczas dodawania czegoś do indeksu; ostrzeżenie nie jest zgłaszane tylko do odczytu git status.

Usunięcie index.lockpliku git statusstało się natychmiastowe, mmapszniknęło i jest teraz ponad 1000x szybsze.

Więc jeśli twój status gita jest nienaturalnie wolny, sprawdź find .git/ -name index.locki usuń resztki.

nh2
źródło
0

To dość stare pytanie. Chociaż jestem zaskoczony, że nikt nie skomentował pliku binarnego ze względu na rozmiar repozytorium.

Wspomniałeś, że twoje repozytorium git ma ~ 10 GB. Wygląda na to, że oprócz problemu z NFS i innych problemów z gitem (rozwiązywalnym przez git gci zmiana konfiguracji git jako zarys w innych odpowiedziach), polecenia git (status git, git diff, git add) mogą być powolne z powodu dużej liczby plików binarnych w repozytorium . git nie radzi sobie dobrze z plikiem binarnym. Możesz usunąć niepotrzebny plik binarny za pomocą następującego polecenia (przykład podano dla pliku NetCDF; wcześniej wykonaj kopię zapasową repozytorium git):

git filter-branch --force --index-filter \  
'git rm --cached --ignore-unmatch *.nc' \   
--prune-empty --tag-name-filter cat -- --all

Nie zapomnij wstawić „* .nc” do pliku gitignore, aby uniemożliwić gitowi ponowne uruchomienie pliku.

SM_
źródło