PLIK · hero_char.mbROZMIAR · 2.8 GBDIFF · niedostępny
Rozdział 5 / Dane i wersje

Dlaczego Git nie wystarcza do plików VFX

Git świetnie radzi sobie z tekstem i różnicami linia po linii. Plik Maya na dwa gigabajty tego nie ma — i to jedna z tych rzeczy, które łamią intuicję programisty wchodzącego do VFX.

TL;DR

Programista, który trafia do VFX, zwykle zadaje to samo pytanie: skoro Git tak dobrze sprawdza się w kodzie, dlaczego cała branża nie wersjonuje nim po prostu wszystkiego? Odpowiedź nie jest oczywista, dopóki nie zrozumiesz, na jakim założeniu Git w ogóle stoi — i dlaczego pliki, z którymi pracuje modelarka czy artysta FX, to założenie łamią niemal natychmiast.

Etap: Jak Git w ogóle działa

Różnica linii, nie kopia całości

Git został zbudowany wokół jednej idei: zmiana w pliku tekstowym da się zapisać jako różnica — które linie zniknęły, które się pojawiły. Kiedy dwie osoby edytują ten sam plik kodu w różnych miejscach, Git potrafi automatycznie połączyć obie zmiany w jeden, spójny wynik — to właśnie merge. Do tego każdy, kto sklonuje repozytorium, dostaje pełną historię wszystkich zmian od początku istnienia projektu, przechowywaną lokalnie — stąd określenie „system rozproszony".

  specularRoughness = 0.4
- ambientColor = 0.2
+ ambientColor = 0.35
  refractionIndex = 1.5

To działa bez zarzutu, dopóki plik jest tekstem — kodem źródłowym narzędzia, plikiem konfiguracyjnym, configiem OpenColorIO z Rozdziału 12. Git widzi każdą linię z osobna, wie dokładnie, co się zmieniło, i potrafi to zmienione miejsce wskazać.

Etap: Co się psuje na plikach binarnych

Blob, którego nie da się otworzyć jako tekst

Plik sceny Maya, cache Alembica czy plik tekstury nie są zapisane jako linie, które można porównać. To formaty binarne — ciągi bajtów o wewnętrznej strukturze zrozumiałej tylko dla programu, który je stworzył. Dla Gita dwie wersje takiego pliku wyglądają tak:

hero_char_v1.mb  →  2 847 391 kB — zmieniło się: nieznane (format binarny)
hero_char_v2.mb  →  2 851 022 kB — zmieniło się: nieznane (format binarny)

Git nie ma sposobu, żeby powiedzieć „zmieniła się tylko pozycja jednego palca lewej dłoni". Widzi dwa całkowicie różne bloby i przy każdym commicie zapisuje kolejną, pełną kopię całego pliku. To ma dwie poważne konsekwencje. Po pierwsze — brak mergowania: jeśli riger i modelarka oboje otworzą hero_char.mb, oboje wprowadzą zmiany i oboje spróbują je zapisać, Git nie potrafi połączyć ich pracy tak, jak łączy dwie linie kodu. Ktoś commituje jako drugi i albo nadpisuje pracę pierwszej osoby, albo dostaje nierozwiązywalny konflikt na pliku, którego nie da się otworzyć w edytorze tekstu, żeby ręcznie zdecydować, co zostaje. Po drugie — koszt miejsca: skoro każda wersja to pełna kopia, historia repozytorium rośnie liniowo z liczbą wersji razy rozmiar pliku, bez żadnej z korzyści kompresji różnicowej, na której Git buduje swoją reputację przy kodzie.

Etap: Problem skali

Git LFS łata jedną dziurę, nie obie

Rozwiązaniem, o którym słyszy większość programistów wchodzących do branży, jest Git LFS (Large File Storage) — rozszerzenie, które trzyma duże pliki binarne na osobnym serwerze, a w samym repozytorium zostawia tylko lekki wskaźnik. To realnie rozwiązuje problem rozmiaru repozytorium: klonowanie projektu nie ściąga już całej historii każdej wersji każdej tekstury. Nie rozwiązuje jednak drugiego problemu — modelu współpracy. Git LFS wciąż zakłada mergowanie i wciąż nie potrafi połączyć dwóch wersji pliku Mayi w jedną. Dwoje artystów pracujących nad tym samym plikiem tego samego dnia wciąż kończy z tym samym konfliktem, tylko przechowywanym gdzie indziej.

Do tego dochodzi coś, czego programista rzadko doświadcza: w VFX historia wersji nie jest archiwum, do którego zagląda się sporadycznie — to aktywna, codzienna przestrzeń robocza. Artysta chce mieć natychmiastowy dostęp do dowolnej wcześniejszej wersji swojego pliku, bez czekania na pobranie jej z zewnętrznego magazynu, i chce mieć absolutną pewność, że w danej chwili tylko on edytuje dany plik — nie dowiedzieć się o tym po fakcie, porównując dwa nieczytelne bloby.

Etap: Czego więc potrzeba

Model blokady zamiast modelu mergowania

Skoro nie da się bezpiecznie łączyć zmian w plikach binarnych, jedynym niezawodnym rozwiązaniem jest niedopuszczenie do sytuacji, w której dwie osoby edytują ten sam plik naraz — nie naprawiać konflikt po fakcie, tylko uniemożliwić go z góry. To dokładnie model, wokół którego zbudowany jest Perforce: zanim zaczniesz edytować plik, jawnie go „wypisujesz" (checkout), co blokuje go dla wszystkich innych, aż go z powrotem „wpiszesz" (submit). To rozwiązanie mniej eleganckie niż automatyczny merge Gita, ale za to dopasowane do plików, których po prostu nie da się połączyć. Poznasz je dokładnie w następnym rozdziale — razem z odpowiedzią na pytanie, dlaczego to wcale nie oznacza, że Git jest w VFX bezużyteczny (bo do kodu samego pipeline'u wraca w Rozdziale 9).

Słowniczek
Diff (różnica)
Zapis tego, co dokładnie zmieniło się między dwiema wersjami pliku — działa niezawodnie tylko dla tekstu.
Merge (scalenie)
Automatyczne połączenie dwóch niezależnych zestawów zmian w jeden, spójny wynik.
Blob binarny
Plik przechowywany jako nieprzezroczysty ciąg bajtów — bez wewnętrznej struktury czytelnej dla narzędzia wersjonującego.
Git LFS
Rozszerzenie Gita przechowujące duże pliki poza głównym repozytorium; rozwiązuje problem rozmiaru, nie rozwiązuje problemu równoczesnej edycji.