Słyszałem w kilku miejscach, że jednym z głównych powodów, dla których świecą rozproszone systemy kontroli wersji, jest znacznie lepsze scalanie niż w tradycyjnych narzędziach, takich jak SVN. Czy jest to faktycznie spowodowane nieodłącznymi różnicami w działaniu tych dwóch systemów, czy też konkretne implementacje DVCS, takie jak Git / Mercurial, mają po prostu wyraźniejsze algorytmy scalania niż SVN?
400
Odpowiedzi:
Twierdzenie, dlaczego scalanie jest lepsze w DVCS niż w Subversion, było w dużej mierze oparte na tym, jak rozgałęzianie i scalanie działało w Subversion jakiś czas temu. Subversion w wersji wcześniejszej niż 1.5.0 nie przechowywało żadnych informacji o tym, kiedy gałęzie zostały scalone, dlatego kiedy chciałeś scalić, musisz określić, który zakres wersji musiał zostać scalony.
Dlaczego więc połączenia Subversion są do bani ?
Zastanów się nad tym przykładem:
Gdy chcemy scalić zmiany b1 z bagażnikiem, wydamy następujące polecenie, stojąc na folderze, który został wyewidencjonowany:
… Który spróbuje scalić zmiany z
b1
lokalnego katalogu roboczego. Następnie zatwierdzasz zmiany po rozwiązaniu wszelkich konfliktów i przetestowaniu wyniku. Po zatwierdzeniu drzewo wersji będzie wyglądać następująco:Jednak ten sposób określania zakresów wersji szybko wymyka się spod kontroli, gdy drzewo wersji rośnie, ponieważ subversion nie ma żadnych metadanych dotyczących tego, kiedy i jakie wersje zostały scalone. Zastanów się, co stanie się później:
Jest to w dużej mierze problem związany z projektem repozytorium, który ma Subversion, aby utworzyć gałąź, musisz utworzyć nowy katalog wirtualny w repozytorium, który będzie zawierał kopię pnia, ale nie przechowuje żadnych informacji dotyczących tego, kiedy i co wszystko z powrotem się połączyło. Czasami doprowadzi to do nieprzyjemnych konfliktów scalania. Jeszcze gorsze jest to, że Subversion domyślnie używał łączenia dwukierunkowego, co ma pewne paraliżujące ograniczenia w automatycznym łączeniu, gdy dwie głowy oddziałów nie są porównywane z ich wspólnym przodkiem.
Aby złagodzić to Subversion przechowuje teraz metadane do rozgałęzienia i scalenia. To rozwiązałoby wszystkie problemy, prawda?
A tak przy okazji, Subversion wciąż jest do bani…
W scentralizowanym systemie, takim jak subversion, wirtualne katalogi są do kitu. Dlaczego? Ponieważ każdy ma dostęp do ich przeglądania… nawet te eksperymentalne. Rozgałęzianie jest dobre, jeśli chcesz eksperymentować, ale nie chcesz widzieć eksperymentów wszystkich i ich ciotek . To poważny hałas poznawczy. Im więcej gałęzi dodasz, tym więcej badziewiasz zobaczysz.
Im więcej oddziałów publicznych znajduje się w repozytorium, tym trudniej będzie śledzić wszystkie różne oddziały. Pytanie, które zadajesz, brzmi: czy gałąź jest wciąż w fazie rozwoju, czy naprawdę jest martwa, co trudno powiedzieć w scentralizowanym systemie kontroli wersji.
Z tego, co widziałem, przez większość czasu organizacja i tak domyślnie korzysta z jednego dużego oddziału. Szkoda, bo to z kolei trudno będzie śledzić testowanie i wydawanie wersji, a wszystko, co dobre, pochodzi z rozgałęziania.
Dlaczego więc DVCS, takie jak Git, Mercurial i Bazaar, są lepsze od Subversion przy rozgałęzianiu i scalaniu?
Istnieje bardzo prosty powód: rozgałęzienie to koncepcja pierwszej klasy . Z założenia nie ma wirtualnych katalogów , a rozgałęzienia są twardymi obiektami w DVCS, które muszą być takie, aby działać po prostu z synchronizacją repozytoriów (tj. Push i pull ).
Pierwszą rzeczą, którą robisz podczas pracy z DVCS, jest klonowanie repozytoriów (git
clone
, hgclone
i bzrbranch
). Klonowanie jest koncepcyjnie tym samym, co tworzenie gałęzi kontroli wersji. Niektórzy nazywają to rozwidleniem lub rozgałęzieniem (chociaż ten drugi często jest również używany w odniesieniu do kolokacji rozgałęzień), ale to po prostu to samo. Każdy użytkownik prowadzi własne repozytorium, co oznacza, że masz rozgałęzienie dla poszczególnych użytkowników .Struktura wersji nie jest drzewem , lecz grafem . Mówiąc dokładniej, ukierunkowany wykres acykliczny (DAG, co oznacza wykres, który nie ma żadnych cykli). Naprawdę nie musisz zagłębiać się w specyfikację DAG, inaczej niż każde zatwierdzenie ma jedno lub więcej odwołań nadrzędnych (na podstawie których zatwierdzenie było oparte). Dlatego poniższe wykresy pokażą strzałki między wersjami w odwrotnej kolejności z tego powodu.
Byłby to bardzo prosty przykład łączenia; Wyobraź sobie centralne repozytorium o nazwie
origin
i użytkownika, Alice, klonującego repozytorium na jej maszynę.Podczas klonowania każda kopia jest kopiowana do Alicji dokładnie tak, jak była (co jest potwierdzane przez jednoznacznie identyfikowalne hash-id) i oznacza, gdzie znajdują się gałęzie źródła.
Alice następnie pracuje nad swoim repozytorium, angażując się we własne repozytorium i decyduje się na wprowadzenie zmian:
Rozwiązanie jest raczej proste, jedyne, co
origin
repozytorium musi zrobić, to wziąć wszystkie nowe wersje i przenieść swoją gałąź do najnowszej wersji (która git nazywa „przewijaniem do przodu”):Przypadek użycia, który zilustrowałem powyżej, nawet nie musi niczego scalać . Tak naprawdę problem nie dotyczy scalania algorytmów, ponieważ algorytm scalania trójstronnego jest prawie taki sam między wszystkimi systemami kontroli wersji. Problem dotyczy bardziej struktury niż czegokolwiek innego .
A co powiesz na przykład, który ma prawdziwe połączenie?
Trzeba przyznać, że powyższy przykład jest bardzo prostym przypadkiem użycia, więc zróbmy znacznie bardziej pokręcony, aczkolwiek bardziej powszechny. Pamiętasz, że
origin
zaczęło się od trzech wersji? Cóż, facet, który to zrobił, nazwał go Bob , pracował sam i dokonał zmian w swoim repozytorium:Teraz Bob nie może wypchnąć swoich zmian bezpośrednio do
origin
repozytorium. System wykrywa to poprzez sprawdzenie, czy wersje Boba bezpośrednio pochodzą od wersjiorigin
, co w tym przypadku nie. Każda próba wypchnięcia spowoduje, że system powie coś w stylu „ Uh… Obawiam się, że nie mogę pozwolić ci to zrobić, Bob ”.Więc Bob musi włączyć się, a następnie scalić zmiany (z git
pull
; lub hgpull
imerge
; lub bzrmerge
). Jest to proces dwuetapowy. Najpierw Bob musi pobrać nowe wersje, które skopiują je zorigin
repozytorium. Widzimy teraz, że wykres jest rozbieżny:Drugim krokiem procesu ściągania jest połączenie rozbieżnych wskazówek i zatwierdzenie wyniku:
Mamy nadzieję, że scalanie nie spowoduje konfliktów (jeśli ich przewidujesz, możesz wykonać dwa kroki ręcznie w git przy pomocy
fetch
imerge
). Później należy ponownie wprowadzić te zmianyorigin
, co spowoduje szybkie scalenie do przodu, ponieważ zatwierdzenie scalania jest bezpośrednim potomkiem najnowszego worigin
repozytorium:Istnieje inna opcja scalenia w git i hg, zwana rebase , która przeniesie zmiany Boba do najnowszych zmian. Ponieważ nie chcę, aby ta odpowiedź była bardziej szczegółowa, pozwolę ci zamiast tego przeczytać na ten temat dokumenty git , mercurial lub bazar .
W ramach ćwiczenia dla czytelnika spróbuj dowiedzieć się, jak to będzie działać z innym zaangażowanym użytkownikiem. Robi się to podobnie jak powyższy przykład z Bobem. Scalanie między repozytoriami jest łatwiejsze niż mogłoby się wydawać, ponieważ wszystkie zmiany / zatwierdzenia są jednoznacznie identyfikowalne.
Istnieje również kwestia wysyłania łat między poszczególnymi programistami, co było ogromnym problemem w Subversion, który jest łagodzony w git, hg i bzr dzięki unikalnie identyfikowalnym wersjom. Gdy ktoś połączy swoje zmiany (tj. Wykona zatwierdzenie scalenia) i wyśle je wszystkim pozostałym w zespole do konsumpcji poprzez wypchnięcie do centralnego repozytorium lub wysłanie łatek, wówczas nie muszą się martwić o scalenie, ponieważ to już się stało . Martin Fowler nazywa ten sposób pracy rozwiązłą integracją .
Ponieważ struktura różni się od Subversion, dzięki zastosowaniu DAG, umożliwia rozgałęzianie i scalanie w łatwiejszy sposób nie tylko dla systemu, ale także dla użytkownika.
źródło
Historycznie, Subversion był w stanie wykonać proste dwukierunkowe scalenie, ponieważ nie przechowywał żadnych informacji o scalaniu. Wymaga to przyjęcia zestawu zmian i zastosowania ich do drzewa. Nawet w przypadku informacji o scalaniu jest to wciąż najczęściej stosowana strategia scalania.
Git domyślnie korzysta z 3-kierunkowego algorytmu scalania, który polega na znalezieniu wspólnego przodka łączących się głów i wykorzystaniu wiedzy, która istnieje po obu stronach scalania. Dzięki temu Git jest bardziej inteligentny w unikaniu konfliktów.
Git ma również wyrafinowany kod umożliwiający wyszukiwanie nazw, co również pomaga. To nie przechowywać Zestawienia zmian lub przechowywać wszelkie informacje śledzenia - po prostu zapisuje stan plików na każdy popełnić i używa heurystyki, aby zlokalizować Zmienia nazwę i ruchy kodu wymagane (przechowywanie na dysku jest bardziej skomplikowany niż ten, ale interfejs przedstawia się w warstwie logicznej bez śledzenia).
źródło
Mówiąc prosto, implementacja scalania jest wykonywana lepiej w Git niż w SVN . Przed wersją 1.5 SVN nie rejestrował operacji scalania, więc nie był w stanie wykonywać przyszłych scaleń bez pomocy użytkownika, który musiał podać informacje, których SVN nie zarejestrował. Z 1.5 stało się to lepsze, a model pamięci SVN jest nieco bardziej wydajny niż DAG Gita. Ale SVN przechowywał informacje o scaleniu w dość zawiłej formie, która pozwala scaleniom zajmować znacznie więcej czasu niż w Git - zaobserwowałem czynniki 300 w czasie wykonywania.
Ponadto SVN twierdzi, że śledzi zmiany nazw w celu ułatwienia łączenia przeniesionych plików. Ale w rzeczywistości nadal przechowuje je jako kopię i oddzielną akcję usuwania, a algorytm scalania wciąż się nad nimi potyka w sytuacjach modyfikacji / zmiany nazwy, to znaczy, gdy plik jest modyfikowany w jednej gałęzi, a nazwa w drugiej, a te gałęzie są do połączenia. Takie sytuacje nadal będą powodować fałszywe konflikty scalania, aw przypadku zmian nazw katalogów nawet prowadzą do cichej utraty modyfikacji. (Ludzie SVN zwykle zwracają uwagę, że zmiany są nadal w historii, ale to nie pomaga, gdy nie są w wyniku scalenia, w którym powinny się pojawić.
Z drugiej strony Git nawet nie śledzi nazw, ale rozpoznaje je po fakcie (w czasie scalania) i robi to dość magicznie.
Reprezentacja scalania SVN ma również problemy; w wersji 1.5 / 1.6 można było automatycznie łączyć się z pniem do gałęzi tak często, jak tylko się podobało, ale scalenie w innym kierunku wymagało ogłoszenia (
--reintegrate
) i pozostawienia gałęzi w stanie niezdatnym do użytku. Znacznie później odkryli, że tak naprawdę nie jest tak i że a)--reintegrate
można to ustalić automatycznie, i b) możliwe są wielokrotne połączenia w obu kierunkach.Ale po tym wszystkim (co IMHO pokazuje brak zrozumienia tego, co robią), byłbym (OK, jestem) bardzo ostrożny, aby używać SVN w każdym nietrywialnym scenariuszu rozgałęziania, i idealnie spróbowałbym zobaczyć, co myśli Git wynik scalenia.
Inne punkty przedstawione w odpowiedziach, takie jak wymuszona globalna widoczność oddziałów w SVN, nie mają znaczenia dla scalania możliwości (ale dla użyteczności). Poza tym „Git przechowuje zmiany, podczas gdy sklepy SVN (coś innego)” są w większości nie na miejscu. Git koncepcyjnie przechowuje każde zatwierdzenie jako osobne drzewo (jak plik tar ), a następnie używa dość heurystyk do przechowywania tego wydajnie. Obliczanie zmian między dwoma zatwierdzeniami jest niezależne od implementacji pamięci. Prawdą jest, że Git przechowuje DAG historii w znacznie prostszej formie, niż SVN robi swoje połączenie. Każdy, kto próbuje zrozumieć to drugie, będzie wiedział, co mam na myśli.
W skrócie: Git używa znacznie prostszego modelu danych do przechowywania poprawek niż SVN, a zatem może włożyć dużo energii w rzeczywiste algorytmy scalania, zamiast próbować poradzić sobie z reprezentacją => praktycznie lepszym scaleniem.
źródło
Jedną z rzeczy, o których nie wspomniano w innych odpowiedziach, a która jest naprawdę dużą zaletą DVCS, jest to, że można zatwierdzić lokalnie przed wprowadzeniem zmian. W SVN, kiedy miałem jakąś zmianę, chciałem się zameldować, a w międzyczasie ktoś już dokonał zatwierdzenia w tym samym oddziale, oznaczało to, że musiałem zrobić
svn update
wcześniej, zanim mogłem zatwierdzić. Oznacza to, że moje zmiany i zmiany drugiej osoby są teraz mieszane razem i nie ma sposobu, aby przerwać scalanie (jak za pomocągit reset
lubhg update -C
), ponieważ nie ma zamiaru wrócić do. Jeśli scalenie nie jest trywialne, oznacza to, że nie możesz kontynuować pracy nad funkcją, dopóki nie wyczyścisz wyniku scalenia.Ale może to jest tylko zaleta dla osób, które są zbyt głupie, aby używać oddzielnych gałęzi (jeśli dobrze pamiętam, mieliśmy tylko jeden oddział, który był używany do rozwoju w firmie, w której użyłem SVN).
źródło
EDYCJA: Jest to przede wszystkim odpowiedź na tę część pytania:
czy jest to faktycznie spowodowane nieodłącznymi różnicami w działaniu obu systemów, czy też konkretne implementacje DVCS, takie jak Git / Mercurial, mają po prostu bardziej sprytne algorytmy scalania niż SVN?
TL; DR - te konkretne narzędzia mają lepsze algorytmy. Dystrybucja ma pewne zalety w przepływie pracy, ale jest prostopadła do zalet łączenia.
EDYCJA KOŃCOWA
Przeczytałem zaakceptowaną odpowiedź. To po prostu źle.
Łączenie SVN może być uciążliwe, a także kłopotliwe. Ale zignoruj, jak to działa przez minutę. Nie ma informacji, które Git przechowuje lub może uzyskać, których SVN nie przechowuje ani nie może uzyskać. Co ważniejsze, nie ma powodu, dla którego przechowywanie osobnych (czasem częściowych) kopii systemu kontroli wersji dostarczy Ci bardziej aktualnych informacji. Dwie struktury są całkowicie równoważne.
Załóżmy, że chcesz zrobić „jakąś sprytną rzecz”. Git jest „lepszy w”. I jesteś zarejestrowany w SVN.
Przekształć SVN w równoważny formularz Git, zrób to w Git, a następnie sprawdź wynik, być może używając wielu zatwierdzeń, dodatkowych gałęzi. Jeśli potrafisz wyobrazić sobie automatyczny sposób przekształcenia problemu SVN w problem Git, to Git nie ma fundamentalnej przewagi.
Pod koniec dnia pozwoli mi na to dowolny system kontroli wersji
Dodatkowo, do łączenia warto również wiedzieć (lub krytycznie)
Mercurial , Git i Subversion (teraz natywnie, wcześniej za pomocą svnmerge.py) mogą dostarczyć wszystkie trzy informacje. Aby zademonstrować coś zasadniczo lepszego w DVC, proszę wskazać czwartą informację, która jest dostępna w Git / Mercurial / DVC niedostępna w SVN / scentralizowanym VC.
Nie oznacza to, że nie są lepszymi narzędziami!
źródło
git merge-base
. Za pomocą git możesz powiedzieć „rozgałęzienia aib podzielone przy wersji x”. Ale sklepy svn „pliki zostały skopiowane z foo na pasek”, więc musisz użyć heurystyki, aby ustalić, że kopia do paska tworzy nową gałąź zamiast kopiować pliki w projekcie. Sztuczka polega na tym, że wersja w svn jest zdefiniowana przez numer wersji i ścieżkę podstawową. Mimo że przez większość czasu można założyć „pień”, gryzie on, jeśli faktycznie są gałęzie.SVN śledzi pliki, podczas gdy Git śledzi zmiany
treści. Jest wystarczająco sprytny, aby śledzić blok kodu, który został refaktoryzowany z jednej klasy / pliku do innej. Używają dwóch kompletnych różnych podejść do śledzenia twojego źródła.Nadal intensywnie używam SVN, ale jestem bardzo zadowolony z tego, że kilka razy korzystałem z Gita.
Niezła lektura, jeśli masz czas: dlaczego wybrałem Git
źródło
Wystarczy przeczytać artykuł na blogu Joela (niestety jego ostatni). Ten dotyczy Mercurial, ale tak naprawdę mówi o zaletach rozproszonych systemów VC, takich jak Git.
Przeczytaj artykuł tutaj .
źródło