Cytując Linusa Torvaldsa, gdy zapytano go, ile plików Git może obsłużyć podczas swojej Tech Talk w Google w 2007 roku (43:09):
… Git śledzi Twoje treści. Nigdy nie śledzi ani jednego pliku. Nie można śledzić pliku w Git. Możesz śledzić projekt, który ma jeden plik, ale jeśli twój projekt ma jeden plik, z pewnością to zrobisz i możesz to zrobić, ale jeśli śledzisz 10 000 plików, Git nigdy nie widzi ich jako pojedynczych plików. Git uważa wszystko za pełną treść. Cała historia w Git oparta jest na historii całego projektu…
(Transkrypcje tutaj .)
Jednak podczas nurkowania w książce Git pierwszą rzeczą, o której się mówi, jest to, że plik w Git może być śledzony lub nieśledzony . Ponadto wydaje mi się, że całe doświadczenie Git jest ukierunkowane na wersjonowanie plików. Podczas używania git diff
lub git status
dane wyjściowe są prezentowane dla poszczególnych plików. Podczas korzystania git add
możesz także wybrać dla poszczególnych plików. Możesz nawet przeglądać historię na podstawie pliku i błyskawicznie.
Jak należy interpretować to stwierdzenie? Jeśli chodzi o śledzenie plików, czym różni się Git od innych systemów kontroli źródła, takich jak CVS?
źródło
Odpowiedzi:
W CVS historię śledzono na podstawie pliku. Oddział może składać się z różnych plików z różnymi wersjami, każdy z własnym numerem wersji. CVS został oparty na RCS ( Revision Control System ), który w podobny sposób śledził poszczególne pliki.
Z drugiej strony Git wykonuje migawki stanu całego projektu. Pliki nie są śledzone i wersjonowane niezależnie; wersja w repozytorium odnosi się do stanu całego projektu, a nie jednego pliku.
Kiedy Git odnosi się do śledzenia pliku, oznacza to po prostu, że należy go włączyć do historii projektu. Rozmowa Linusa nie odnosiła się do plików śledzenia w kontekście Gita, ale przeciwstawiała model CVS i RCS modelowi opartemu na migawkach używanym w Git.
źródło
$Id$
w pliku. To samo nie działa w git, ponieważ konstrukcja jest inna.Zgadzam się z Brianem M. odpowiedź Carlson : Linus rzeczywiście rozróżnia, przynajmniej częściowo, systemy kontroli wersji zorientowane na pliki i zorientowane na zatwierdzanie. Ale myślę, że jest w tym coś więcej.
W mojej książce , która utknęła w martwym punkcie i może się nie skończyć, próbowałem wymyślić systematykę dla systemów kontroli wersji. W mojej taksonomii terminem, który nas tutaj interesuje, jest atomowość systemu kontroli wersji. Zobacz, co aktualnie znajduje się na stronie 22. Kiedy VCS ma atomowość na poziomie pliku, w rzeczywistości dla każdego pliku istnieje historia. VCS musi zapamiętać nazwę pliku i co się z nim działo w każdym punkcie.
Git tego nie robi. Git ma tylko historię zatwierdzeń - zatwierdzenie jest jednostką atomowości, a historia jest zbiorem zatwierdzeń w repozytorium. Zapamiętywanie, które zapamiętuje, to dane - całe drzewo pełne nazw plików i treści, które towarzyszą każdemu z tych plików - oraz niektóre metadane: na przykład, kto dokonał zatwierdzenia, kiedy i dlaczego oraz wewnętrzny identyfikator hash Git zatwierdzenia nadrzędnego zatwierdzenia. (Jest to rodzic, a graf skierowany acycling utworzone przez przeczytaniu wszystkich zobowiązuje i ich rodziców, że to historia w repozytorium).
Zauważ, że VCS może być zorientowany na zatwierdzanie, ale nadal przechowuje dane plik po pliku. To szczegół implementacji, choć czasem ważny, a Git też tego nie robi. Zamiast tego każde zatwierdzenie rejestruje drzewo , przy czym obiekt drzewa koduje nazwy plików , tryby (tj. Czy ten plik jest wykonywalny czy nie?) Oraz wskaźnik do rzeczywistej zawartości pliku . Sama treść jest przechowywana niezależnie w obiekcie blob . Podobnie jak obiekt zatwierdzenia, obiekt blob otrzymuje identyfikator skrótu, który jest unikalny dla jego zawartości - ale w przeciwieństwie do zatwierdzenia, które może pojawić się tylko raz, obiekt blob może występować w wielu zatwierdzeniach. Tak więc podstawowa zawartość pliku w Git jest przechowywana bezpośrednio jako obiekt blob, a następnie pośrednio w obiekcie drzewa, którego identyfikator skrótu jest zapisany (bezpośrednio lub pośrednio) w obiekcie zatwierdzenia.
Gdy poprosisz Gita o pokazanie historii pliku, używając:
to, co naprawdę robi Git, to przeglądanie historii zatwierdzeń , która jest jedyną historią, którą ma Git, ale nie pokazywanie żadnego z tych zatwierdzeń, chyba że:
(ale niektóre z tych warunków można modyfikować za pomocą dodatkowych
git log
opcji, a bardzo trudno jest opisać efekt uboczny o nazwie History Simplification, który powoduje, że Git całkowicie pomija niektóre zatwierdzenia w historii). Historia plików, którą tu widzisz, w pewnym sensie nie istnieje w repozytorium: jest to tylko syntetyczny podzbiór prawdziwej historii. Otrzymasz inną „historię plików”, jeśli użyjesz różnychgit log
opcji!źródło
Zagmatwany bit jest tutaj:
Git często używa 160-bitowych skrótów zamiast obiektów we własnym repozytorium. Drzewo plików to w zasadzie lista nazw i skrótów powiązanych z zawartością każdego z nich (plus niektóre metadane).
Ale 160-bitowy skrót jednoznacznie identyfikuje treść (we wszechświecie bazy danych git). Zatem drzewo z skrótami jako treść zawiera zawartość w jej stanie.
Jeśli zmienisz stan zawartości pliku, zmieni się jego skrót. Ale jeśli zmieni się jego skrót, zmieni się również skrót związany z zawartością nazwy pliku. Co z kolei zmienia skrót „drzewa katalogów”.
Gdy baza danych git przechowuje drzewo katalogów, drzewo to implikuje i zawiera całą zawartość wszystkich podkatalogów i wszystkich plików w nim zawartych .
Jest on zorganizowany w strukturę drzewa z (niezmiennymi, wielokrotnego użytku) wskaźnikami do obiektów blob lub innych drzew, ale logicznie jest to pojedyncza migawka całej zawartości całego drzewa. Reprezentacja w bazie git nie jest płaskie treść danych, ale logicznie to wszystko jego dane i nic innego.
Jeśli serializujesz drzewo do systemu plików, usuwasz wszystkie foldery .git i każesz gitowi dodać drzewo z powrotem do swojej bazy danych, skończysz na tym, że nic nie dodasz do bazy danych - element już tam będzie.
Pomóc może myśleć o skrótach gita jako o wskaźniku liczonym jako odniesienie do niezmiennych danych.
Jeśli stworzysz wokół niego aplikację, dokument to wiązka stron, które mają warstwy, grupy, obiekty.
Kiedy chcesz zmienić obiekt, musisz utworzyć dla niego zupełnie nową grupę. Jeśli chcesz zmienić grupę, musisz utworzyć nową warstwę, która potrzebuje nowej strony, która potrzebuje nowego dokumentu.
Za każdym razem, gdy zmieniasz pojedynczy obiekt, odradza się nowy dokument. Stary dokument nadal istnieje. Nowy i stary dokument udostępniają większość swoich treści - mają te same strony (oprócz 1). Ta jedna strona ma te same warstwy (oprócz 1). Ta warstwa ma te same grupy (oprócz 1). Ta grupa ma te same obiekty (oprócz 1).
I przez to rozumiem logicznie kopię, ale pod względem implementacji jest to po prostu kolejny wskaźnik zliczany referencją do tego samego niezmiennego obiektu.
Git repo jest bardzo podobny.
Oznacza to, że dany zestaw zmian git zawiera komunikat zatwierdzenia (jako kod skrótu), zawiera drzewo pracy i zawiera zmiany nadrzędne.
Te zmiany nadrzędne zawierają zmiany nadrzędne od samego początku.
Częścią repozytorium git, która zawiera historię, jest łańcuch zmian. Ten łańcuch zmian zmienia go na poziomie powyżej drzewa „katalogu” - z drzewa „katalogu” nie można jednoznacznie dostać się do zestawu zmian i łańcucha zmian.
Aby dowiedzieć się, co dzieje się z plikiem, zaczynasz od tego pliku w zestawie zmian. Ten zestaw zmian ma swoją historię. Często w tej historii istnieje ten sam nazwany plik, czasem o tej samej treści. Jeśli treść jest taka sama, nie było zmian w pliku. Jeśli jest inaczej, następuje zmiana i należy wykonać pracę, aby dokładnie ustalić, co.
Czasami plik zniknął; ale drzewo „katalogu” może mieć inny plik z tą samą zawartością (ten sam kod skrótu), więc możemy to prześledzić w ten sposób (uwaga; właśnie dlatego chcesz, aby zatwierdzenie przesunęło plik osobno od zatwierdzenia do -edytować). Lub ta sama nazwa pliku, a po sprawdzeniu plik jest wystarczająco podobny.
Git może więc łączyć „historię plików”.
Ale ta historia plików pochodzi z wydajnego analizowania „całego zestawu zmian”, a nie z łącza z jednej wersji pliku do innej.
źródło
„git nie śledzi plików” zasadniczo oznacza, że zatwierdzenia git składają się z migawki drzewa plików łączącej ścieżkę drzewa z „obiektu blob” i wykresu zatwierdzania śledzącego historię zatwierdzeń . Wszystko inne jest rekonstruowane w locie za pomocą poleceń takich jak „git log” i „git blame”. Tę rekonstrukcję można powiedzieć za pomocą różnych opcji, jak mocno powinna ona szukać zmian opartych na plikach. Domyślna heurystyka może określić, kiedy obiekt blob zmienia miejsce w drzewie plików bez zmian lub kiedy plik jest powiązany z innym obiektem blob niż wcześniej. Mechanizmy kompresji stosowane przez Git nie dbają zbytnio o granice obiektów blob / plików. Jeśli treść już gdzieś jest, spowoduje to niewielki wzrost repozytorium bez kojarzenia różnych obiektów blob.
To jest repozytorium. Git ma także działające drzewo, aw tym pracującym drzewie znajdują się pliki śledzone i nieśledzone. Tylko śledzone pliki są zapisywane w indeksie (obszar przejściowy? Pamięć podręczna?) I tylko to, co jest tam śledzone, trafia do repozytorium.
Indeks jest zorientowany na pliki i istnieje kilka poleceń zorientowanych na pliki do manipulowania nim. Ale to, co kończy się w repozytorium, to po prostu zatwierdzenia w postaci migawek drzewa plików i powiązanych danych obiektów blob oraz przodków zatwierdzenia.
Ponieważ Git nie śledzi historii plików i nazw, a ich wydajność nie zależy od nich, czasami musisz spróbować kilka razy z różnymi opcjami, dopóki Git nie stworzy historii / różnic / obwiniania, które Cię interesują dla nietrywialnych historii.
Inaczej jest w przypadku systemów takich jak Subversion, które rejestrują, a nie odtwarzają historie. Jeśli nie jest nagrywany, nie słyszysz o tym.
Zrobiłem kiedyś instalator różnicowy, który po prostu porównał drzewa wydań, sprawdzając je w Git, a następnie tworząc skrypt powielający ich działanie. Ponieważ czasami całe drzewa były przenoszone, produkowało to znacznie mniejszych instalatorów różnicowych niż nadpisywanie / usuwanie wszystkiego, co by wyprodukowało.
źródło
Git nie śledzi bezpośrednio pliku, ale śledzi migawki repozytorium, a te migawki składają się z plików.
Oto sposób, aby na to spojrzeć.
W innych systemach kontroli wersji (SVN, Rational ClearCase) można kliknąć plik prawym przyciskiem myszy i uzyskać historię zmian .
W Git nie ma bezpośredniego polecenia, które to robi. Zobacz to pytanie . Będziesz zaskoczony, ile jest różnych odpowiedzi. Nie ma jednej prostej odpowiedzi, ponieważ Git nie śledzi po prostu pliku , nie w sposób, w jaki robi to SVN lub ClearCase.
źródło
git log
lub jakiś program zbudowany na tym (lub jakiś alias, który robi to samo). Ale nawet jeśli było wiele różnych sposobów, jak mówi Joe, dotyczy to również historii branży. (równieżgit log -p <file>
jest wbudowany i robi dokładnie to)Nawiasem mówiąc, śledzenie „treści” doprowadziło do nie śledzenia pustych katalogów.
Dlatego jeśli otrzymasz ostatni plik folderu, sam folder zostanie usunięty .
Nie zawsze tak było i tylko Git 1.4 (maj 2006 r.) Egzekwował tę zasadę „śledzenia treści” przy pomocy commit 443f833 :
Zostało to powtórzone wiele lat później, w styczniu 2011 r., Z zatwierdzeniem 8fe533 , Git v1.7.4:
W międzyczasie, w Git 1.4.3 (wrzesień 2006), Git zaczyna ograniczać nieśledzoną zawartość do niepustych folderów, z zatwierdzeniem 2074cb0 :
Śledzenie treści jest tym, co pozwoliło winić gitowi bardzo wcześnie (Git 1.4.4, październik 2006, zatwierdzanie cee7f24 ):
To (śledzenie zawartości) jest również tym, co umieściło git add w Git API, z Git 1.5.0 (grudzień 2006, zatwierdzenie 366bfcb )
To stało się
git add --interactive
możliwe dzięki temu samemu Git 1.5.0 ( commit 5cde71d )Dlatego też, aby rekurencyjnie usunąć całą zawartość z katalogu, musisz przekazać
-r
opcję, a nie tylko nazwę katalogu jako<path>
(wciąż Git 1.5.0, zatwierdzenie 9f95069 ).Oglądanie zawartości pliku zamiast samego pliku pozwala na scenariusz scalania, taki jak ten opisany w zatwierdzeniu 1de70db (Git v2.18.0-rc0, kwiecień 2018)
Commit 37b65ce , Git v2.21.0-rc0, grudzień 2018 r., Ostatnio poprawiono rozwiązania konfliktów kolizji.
A zatwierdzenie bbafc9c i inne ilustruje znaczenie rozważenia zawartości pliku , poprzez poprawę obsługi konfliktów zmiany nazwy / zmiany nazwy (2to1):
źródło