Close

Jak obsługiwać duże repozytoria za pomocą systemu Git

Zdjęcie portretowe Nicoli Paolucciego
Nicola Paolucci

Developer Advocate


Git jest fantastycznym rozwiązaniem do śledzenia ewolucji bazy kodu i efektywnej współpracy z zespołem. Co się dzieje jednak, gdy repozytorium, które chcesz śledzić, jest naprawdę ogromne?

W tym wpisie opiszę kilka technik, które można wykorzystać w takiej sytuacji.

Dwie kategorie dużych repozytoriów


Istnieją zasadniczo dwa główne powody, dla których repozytoria osiągają ogromne rozmiary:

  • Gromadzą bardzo długą historię (projekt rośnie przez bardzo długi okres, w związku z czym zbiera się obszerny bagaż).
  • Obejmują wielkie zasoby binarne, które muszą być śledzone i sparowane z kodem.

...a może jedno i drugie.

Czasami problem należący do drugiej kategorii jest potęgowany przez fakt, że stare, wycofane artefakty binarne są nadal przechowywane w repozytorium. Jednak istnieje na to w miarę skuteczne — choć nieco irytujące — rozwiązanie (patrz poniżej).

Techniki i obejścia są inne, choć czasami się uzupełniają. Omówię je zatem osobno.

Klonowanie repozytoriów o bardzo długiej historii


Choć próg pozwalający zakwalifikować repozytorium jako „ogromne” jest dość wysoki, i tak klonowanie tego rodzaju repozytoriów jest problematyczne. I nie zawsze da się uniknąć długich historii. Niektóre repozytoria muszą pozostawać nienaruszone ze względów prawnych.

Prostym rozwiązaniem jest płytkie klonowanie w Git

Pierwszym rozwiązaniem pozwalającym na szybkie sklonowanie i oszczędzenie czasu programistów i systemów oraz miejsca na dysku jest skopiowanie wyłącznie najnowszych poprawek. Opcja płytkiego klonowania w systemie Git umożliwia pobranie z historii repozytoriów tylko określonej liczby najnowszych commitów.

Jak to zrobić? Wystarczy użyć opcji --depth. Przykład:

git clone --depth [głębokość] [zdalny-url]

Wyobraź sobie, że Twoje repozytorium zawiera historię projektu z co najmniej dziesięciu lat. Przykładowo przenieśliśmy system Jira (11-letnią bazę kodu) do systemu Git. Oszczędności czasu w przypadku takich repozytoriów mogą się sumować i być bardzo wyraźne.

Pełny klon Jira to 677 MB, a katalog roboczy to kolejne ponad 320 MB, co przekłada się na ponad 47 000 commitów. Wykonanie płytkiego klona repozytorium trwało 29,5 sekundy w porównaniu z 4 minutami i 24 sekundami w przypadku pełnego klona z całą historią Korzyści rosną proporcjonalnie do liczby zasobów binarnych, które nagromadziły się w projekcie na przestrzeni czasu.

Bazy danych
materiały pokrewne

Jak przenieść pełne repozytorium Git

Logo Bitbucket
POZNAJ ROZWIĄZANIE

Poznaj środowisko Git z rozwiązaniem Bitbucket Cloud

Porada: płytkie klonowanie jest korzystne także dla systemów kompilacji połączonych z repozytorium Git.

Płytkie klony były niegdyś nieco gorszymi obywatelami świata Git, ponieważ niektóre operacje nie były w pełni obsługiwane. Jednak w najnowszych wersjach (od 1.9) sytuacja uległa znacznej poprawie i można obecnie poprawnie wykonywać polecenia pull i push w odniesieniu do repozytoriów nawet z płytkiego klona.

Rozwiązanie interwencyjne: git filter branch

W przypadku ogromnych repozytoriów, które mają obszerny błędny kod binarny zatwierdzony przez pomyłkę lub stare zasoby, które nie są już potrzebne, świetnym rozwiązaniem jest użycie polecenia git filter-branch. Umożliwia ono przejście przez całą historię projektu w celu filtrowania, modyfikowania i pomijania plików oraz manipulowania nimi według wstępnie zdefiniowanych wzorców.

Jest to bardzo zaawansowane narzędzie stosowane wtedy, gdy już ustalisz obszary dużego obciążenia repozytorium. Dostępne są skrypty pomocnicze pozwalające na identyfikację dużych obiektów, więc to zadanie powinno być dość łatwe.

Składnia wygląda następująco:

git filter-branch --tree-filter 'rm -rf [/path/to/spurious/asset/folder]'

Polecenie git filter-branch ma jedną drobną wadę: po użyciu _filter-branch_ cała historia projektu jest zapisywana na nowo. Oznacza to, że wszystkie identyfikatory commitów się zmieniają. Wymaga to od każdego programisty ponownego sklonowania zaktualizowanego repozytorium.

Jeśli więc planujesz przeprowadzić czyszczenie za pomocą polecenia git filter-branch, uprzedź swój zespół, zaplanuj krótką pauzę podczas wykonywania operacji, a następnie powiadom wszystkich, że powinni wykonać polecenie clone w odniesieniu do repozytorium.

Porada: więcej informacji na temat polecenia git filter-branch zawiera wpis na temat rozbijania repozytorium Git.

Alternatywa dla płytkiego klonowania w Git: klonowanie tylko jednej gałęzi

Począwszy od wydania Git 1.7.10, możesz także ograniczyć zakres klonowanej historii, klonując pojedynczą gałąź w następujący sposób:

git clone [remote url] --branch [branch_name] --single-branch [folder]

To konkretne rozwiązanie przydaje się, gdy korzystasz z długofalowych i rozbieżnych gałęzi lub jeśli masz wiele gałęzi, ale jednocześnie pracujesz tylko na kilku z nich. Jeśli masz tylko kilka gałęzi z bardzo niewielkimi różnicami, nie będzie ono szczególnie użyteczne.

Zarządzanie repozytoriami z ogromnymi zasobami binarnymi


Drugim rodzajem dużych repozytoriów są te, które zawierają ogromne zasoby binarne. Są one spotykane w wielu różnych rodzajach zespołów zajmujących się tworzeniem oprogramowania (i nie tylko!). Zespoły tworzące gry muszą żonglować ogromnymi modelami 3D, zespoły web developerów mogą potrzebować śledzić zasoby obrazów RAW, a zespoły CAD mogą musieć manipulować binarnymi elementami dostarczanymi i śledzić ich status.

Git nie jest bardzo zły, jeśli chodzi o obsługę zasobów binarnych, ale też nie jest szczególnie dobry. Domyślnie Git kompresuje i zapisuje wszystkie kolejne pełne wersje zasobów binarnych, co oczywiście nie jest optymalne, jeśli jest ich dużo.

Istnieje kilka prostych ulepszeń, które pozwalają temu zaradzić: na przykład uruchomienie polecenia (git gc) do usunięcia zbędnych elementów lub poprawienie użycia commitów różnicowych (delta) dla niektórych typów danych binarnych w .gitattributes.

Trzeba się zastanowić nad charakterem zasobów binarnych w projekcie, ponieważ od tego zależy dobór odpowiedniego rozwiązania. Oto kilka zagadnień, które warto rozważyć:

  • W przypadku plików binarnych, które ulegają znaczącym zmianom — wykraczającym poza nagłówki metadanych — kompresja wersji różnicowych jest na ogół nieprzydatna. Dlatego warto wyłączyć wersje delta dla tych plików, aby uniknąć niepotrzebnej kompresji podczas ponownego tworzenia pakietów.
  • W powyższym scenariuszu prawdopodobne jest, że kompresja zlib tych plików nie będzie zbyt skuteczna, dlatego można wyłączyć kompresję za pomocą ustawienia core.compression 0 lub core.loosecompression 0. To ustawienie globalne, które negatywnie wpłynie na wszystkie niebinarne pliki, które faktycznie dobrze się kompresują, więc ma to sens, jeśli podzielisz zasoby binarne do oddzielnego repozytorium.
  • Warto pamiętać, że polecenie git gc zamienia „zduplikowane” luźne obiekty w pojedynczy plik pakietu. Jeśli jednak pliki nie kompresują się w jakikolwiek sposób, prawdopodobnie różnica w porównaniu z otrzymanym plikiem pakietu nie będzie znacząca.
  • Zapoznaj się z możliwościami dostosowania ustawienia core.bigFileThreshold. Wersje różnicowe plików większych niż 512 MB i tak nie są kompresowane (o ile nie ustawimy .gitattributes) więc może warto wprowadzić tu parę modyfikacji.

Rozwiązanie problemu dużych drzew folderów: git sparse-checkout

Pewną pomocą w przypadku problemu z zasobami binarnymi jest opcja sparse checkout (dostępna od wydania Git 1.7.0). Ta technika pozwala zachować czystość katalogu roboczego poprzez jawne wyszczególnienie folderów, które chcemy wypełnić. Niestety nie wpływa to na rozmiar ogólnego lokalnego repozytorium, ale może być pomocne w przypadku bardzo rozległego drzewa folderów.

Jakie polecenia są wykorzystywane? Oto przykład:

  • Sklonuj raz całe repozytorium za pomocą polecenia git clone.
  • Aktywuj funkcję git config core.sparsecheckout true.
  • Dodaj jawnie foldery, które są potrzebne, pomijając foldery zasobów:
    • echo src/ › .git/info/sparse-checkout
  • Odczytaj drzewo za pomocą poniższego polecenia:
    • git read-tree -m -u HEAD

Po wykonaniu powyższych czynności możesz ponownie zacząć używać standardowych poleceń git, ale katalog roboczy będzie zawierał tylko foldery określone powyżej.

Rozwiązanie problemu kontroli podczas aktualizacji dużych plików: moduły podrzędne

[AKTUALIZACJA] …możesz też pominąć to wszystko i użyć Git LFS


Jeśli regularnie pracujesz z dużymi plikami, najlepszym wyjściem może być skorzystanie z technologii Large File Support (LFS) opracowanej przez Atlassian wspólnie z GitHub w 2015 roku. (Tak, wzrok Cię nie myli. Połączyliśmy siły z GitHub, wnosząc własny open-source'owy wkład w projekt Git).

Git LFS to rozszerzenie, które przechowuje wskaźniki (oczywiście!) do dużych plików w repozytorium, zamiast przechowywać same pliki. Rzeczywiste pliki są przechowywane na zdalnym serwerze. Jak można sobie wyobrazić, radykalnie skraca to czas potrzebny do sklonowania repozytorium.

Schemat git LFS

Bitbucket obsługuje Git LFS, podobnie jak GitHub. Jest więc prawdopodobne, że masz już dostęp do tej technologii. Jest ona szczególnie przydatna w przypadku zespołów, w których skład wchodzą projektanci, wideofilmowcy, muzycy lub użytkownicy CAD.

Wnioski


Nie musisz rezygnować z fantastycznych możliwości Git tylko dlatego, że masz olbrzymią historię repozytorium lub ogromne pliki. Istnieją realne rozwiązania obu tych problemów.

Więcej informacji na temat modułów podrzędnych, zależności w projekcie i rozszerzenia Git LFS znajdziesz w innych artykułach, do których łącza zamieściłem powyżej. A polecenia i przepływ pracy możesz sobie odświeżyć, sięgając do mnóstwa samouczków na naszej mikrowitrynie na temat systemu Git. Miłego kodowania!

Nicola Paolucci

Nicola is an all-round hacker who loves exploring and teaching bleeding edge technologies. He writes and talks about Git, development workflows, code collaboration and more recently about Docker. Prior to his current role as Developer Instigator at Atlassian he led software teams, built crowd sourcing applications for geo-spacial data, worked on huge e-commerce deployments. Little known facts about Nicola: he gesticulates a lot while speaking (being Italian), lives in Amsterdam and rides a Ducati.


Udostępnij ten artykuł
Następny temat

Zalecane lektury

Dodaj te zasoby do zakładek, aby dowiedzieć się więcej na temat rodzajów zespołów DevOps lub otrzymywać aktualności na temat metodyki DevOps w Atlassian.

Ludzie współpracujący przy ścianie pełnej narzędzi

Blog Bitbucket

Ilustracja DevOps

Ścieżka szkoleniowa DevOps

Demonstracje funkcji z ekspertami Atlassian

Zobacz, jak Bitbucket Cloud współpracuje z Atlassian Open DevOps

Zapisz się do newslettera DevOps

Thank you for signing up