Jak i / lub dlaczego scalanie w Git jest lepsze niż w SVN?

Słyszałem w kilku miejscach, że jednym z głównych powodów, dla których rozproszone systemy kontroli wersji świecą, jest znacznie lepsze łączenie się niż w tradycyjnych narzędziach, takich jak SVN. Czy jest to spowodowane nieodłącznymi różnicami w działaniu obu systemów, czy też implementacje DVCS, takie jak Git / Mercurial, mają sprytniejsze algorytmy scalania niż SVN?

Author: ks1322, 2010-03-18

7 answers

Twierdzenie, dlaczego scalanie jest lepsze w DVCS niż w Subversion, opierało się w dużej mierze na tym, jak rozgałęzianie i scalanie działało w Subversion jakiś czas temu. Subversion przed 1.5.0 nie przechowuje żadnych informacji o tym, kiedy gałęzie zostały scalone, więc gdy chcesz scalić, musisz określić zakres wersji, które mają zostać scalone.

Więc dlaczego Subversion Scala ssać ?

Zastanów się nad tym przykładem:

      1   2   4     6     8
trunk o-->o-->o---->o---->o
       \
        \   3     5     7
b1       +->o---->o---->o

Kiedy chcemy połączyć b1 zmienia się w trunk, gdy stajemy na folderze, który ma sprawdzony trunk, wydajemy następującą komendę:

svn merge -r 2:7 {link to branch b1}

... który spróbuje scalić zmiany z b1 do lokalnego katalogu roboczego. A następnie zatwierdzasz zmiany po rozwiązaniu wszelkich konfliktów i przetestowaniu wyniku. Po zatwierdzeniu drzewo rewizji wyglądałoby tak:

      1   2   4     6     8   9
trunk o-->o-->o---->o---->o-->o      "the merge commit is at r9"
       \
        \   3     5     7
b1       +->o---->o---->o

Jednak ten sposób określania zakresów wersji szybko wymyka się spod kontroli, gdy drzewo wersji rośnie jako subversion nie miał żadnych metadanych na temat tego, kiedy i jakie poprawki zostały połączone razem. Zastanów się nad tym, co dzieje się później:

           12        14
trunk  …-->o-------->o
                                     "Okay, so when did we merge last time?"
              13        15
b1     …----->o-------->o

Jest to w dużej mierze problem związany z projektem repozytorium, który ma Subversion, aby utworzyć gałąź, musisz utworzyć nowy wirtualny katalog w repozytorium, który będzie zawierał kopię trunka, ale nie przechowuje żadnych informacji o tym, kiedy i do czego rzeczy zostały ponownie scalone. To prowadzi czasami do nieprzyjemnych konfliktów scalania. Co było nawet co gorsza, Subversion używa domyślnie scalania dwukierunkowego, które ma pewne ograniczenia w automatycznym scalaniu, gdy dwie odgałęzienia nie są porównywane ze wspólnym przodkiem.

Aby złagodzić tę Subversion przechowuje teraz metadane dla gałęzi i scalania. To rozwiąże wszystkie problemy, prawda?

A tak przy okazji, Subversion wciąż jest do bani ... ]}

W scentralizowanym systemie, takim jak subversion, wirtualne katalogi są do bani. Dlaczego? Ponieważ każdy ma dostęp do widoku nawet te śmieciowe. Rozgałęzianie jest dobre, jeśli chcesz eksperymentować , ale nie chcesz widzieć eksperymentów everyones i ich ciotek . To poważny szum poznawczy. Im więcej gałęzi dodasz, tym więcej gówna zobaczysz.

Im więcej publicznych gałęzi masz w repozytorium, tym trudniej będzie śledzić wszystkie różne gałęzie. Więc pytanie będzie brzmiało, czy gałąź jest nadal w rozwoju lub czy jest naprawdę martwy, co jest trudne powiedzieć w dowolnym scentralizowanym systemie kontroli wersji.

Przez większość czasu, z tego co widziałem, organizacja domyślnie używa jednego dużego oddziału. A szkoda, bo to z kolei będzie trudne do śledzenia wersji testowych i wydawniczych, a co innego dobrego wynika z rozgałęzień.

Więc dlaczego DVC, takie jak Git, Mercurial i Bazaar, są lepsze niż Subversion w rozgałęzianiu i scalaniu?

Jest bardzo prosty powód, dla którego: rozgałęzianie jest koncepcja pierwszej klasy . Nie ma żadnych wirtualnych katalogów, a gałęzie są twardymi obiektami w DVC, które muszą być takie, aby działać po prostu z synchronizacją repozytoriów (np. push i pull).

Pierwszą rzeczą, którą robisz podczas pracy z DVCS, jest klonowanie repozytoriów (Gita clone, hg ' s clone i bzr branch). Klonowanie jest koncepcyjnie tym samym co tworzenie gałęzi w wersji Kontrola. Niektórzy nazywają to rozwidleniem lub rozgałęzieniem (chociaż to ostatnie często jest również używane w odniesieniu do współpołożonych gałęzi), ale jest to po prostu to samo. Każdy użytkownik prowadzi własne repozytorium, co oznacza, że masz dla każdego użytkownika rozgałęzienie.

Struktura wersji jest nie drzewem , ale raczej wykresem . A dokładniej skierowany Graf acykliczny (dag, co oznacza Graf, który nie ma żadnych cykli). Naprawdę. nie trzeba rozwodzić się nad specyfiką DAG innej niż każdy commit ma jedną lub więcej referencji nadrzędnych (na których podstawie powstał commit). Tak więc poniższe wykresy pokażą strzałki między wersjami w odwrotnej kolejności z tego powodu.

Bardzo prostym przykładem scalania może być to: wyobraź sobie centralne repozytorium o nazwie origin i użytkownika, Alice, klonującego repozytorium do swojej maszyny.
         a…   b…   c…
origin   o<---o<---o
                   ^master
         |
         | clone
         v

         a…   b…   c…
alice    o<---o<---o
                   ^master
                   ^origin/master

To, co dzieje się podczas klonu, to to, że każda zmiana jest kopiowana do Alice dokładnie tak, jak były one (co jest potwierdzone przez jednoznacznie identyfikowalne hash-id) i oznaczają, gdzie znajdują się gałęzie pochodzenia.

W tym samym czasie, Alice zostaje zmuszona do zmiany swojego repozytorium.]}
         a…   b…   c…
origin   o<---o<---o
                   ^ master

              "what'll happen after a push?"


         a…   b…   c…   d…   e…
alice    o<---o<---o<---o<---o
                             ^master
                   ^origin/master

Rozwiązanie jest dość proste, jedyne co repozytorium origin musi zrobić, to przyjąć wszystkie nowe wersje i przenieść gałąź do najnowszej wersji (którą git nazywa "fast-forward"): {]}

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

         a…   b…   c…   d…   e…
alice    o<---o<---o<---o<---o
                             ^master
                             ^origin/master

Przypadek użycia, który zilustrowałem powyżej, nie musi nawet łączyć niczego. Tak więc problem naprawdę nie dotyczy algorytmów scalania, ponieważ algorytm scalania trójstronnego jest prawie taki sam między wszystkimi systemami kontroli wersji. problem dotyczy bardziej struktury niż czegokolwiek .

Więc może pokażesz mi przykład, który ma prawdziwe połączenie?

Wprawdzie powyższy przykład jest bardzo prostym przypadkiem użycia, więc zróbmy o wiele bardziej Pokręcony, choć bardziej powszechny. Pamiętaj, że origin zaczęło się od trzech zmian? Cóż, facet, który to zrobił, nazwijmy go Bobem, pracował sam i zrobił commit na swoim repozytorium:]}

         a…   b…   c…   f…
bob      o<---o<---o<---o
                        ^ master
                   ^ origin/master

                   "can Bob push his changes?" 

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

Teraz Bob nie może przesyłać swoich zmian bezpośrednio do repozytorium origin. Jak system wykrywa to jest przez sprawdzenie, czy zmiany Boba bezpośrednio schodzi z origin ' s, co w tym przypadku nie. każda próba wciśnięcia spowoduje, że system powie coś podobnego do "Uh... Obawiam się, że nie mogę ci na to pozwolić. Bob ."

Więc Bob musi wciągnąć, a następnie połączyć zmiany (z Gitem pull; lub hg pull oraz merge; lub bzr merge). Jest to proces dwuetapowy. Najpierw Bob musi pobrać nowe wersje, które skopiują je tak, jak są z repozytorium origin. Teraz widzimy, że wykres jest rozbieżny:

                        v master
         a…   b…   c…   f…
bob      o<---o<---o<---o
                   ^
                   |    d…   e…
                   +----o<---o
                             ^ origin/master

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

Drugim krokiem procesu pull jest połączenie rozbieżnych końcówek i wykonanie commit z wynik:

                                 v master
         a…   b…   c…   f…       1…
bob      o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+
                             ^ origin/master

Mam nadzieję, że połączenie nie spowoduje konfliktów (jeśli je przewidujesz, możesz wykonać dwa kroki ręcznie w git z fetch oraz merge). Następnie należy ponownie wprowadzić te zmiany do origin, co spowoduje szybkie scalanie, ponieważ commit merge jest bezpośrednim potomkiem najnowszego w repozytorium origin:

                                 v origin/master
                                 v master
         a…   b…   c…   f…       1…
bob      o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+

                                 v master
         a…   b…   c…   f…       1…
origin   o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+

Istnieje jeszcze jedna opcja łączenia w git i hg, o nazwie rebase, która przenieś zmiany Boba na po najnowszych zmianach. Ponieważ nie chcę, aby ta odpowiedź była bardziej gadatliwa, pozwolę ci przeczytać git, mercurial lub bazaar docs about that instead.

Jako ćwiczenie dla czytelnika, spróbuj wyciągnąć, jak to będzie działać z innym użytkownikiem zaangażowanym. Podobnie jak w powyższym przykładzie z Bobem. Łączenie między repozytoriami jest łatwiejsze niż myślisz, ponieważ wszystkie zmiany/zmiany są unikalne / align = "left" /

Istnieje również problem wysyłania łatek pomiędzy każdym deweloperem, co było ogromnym problemem w Subversion, który jest łagodzony w git, hg i bzr przez unikalnie identyfikowalne poprawki. Gdy ktoś połączy swoje zmiany (np. zrobi commit scalający) i wyśle je do wszystkich innych członków zespołu, aby je zużyć poprzez wpychanie do centralnego repozytorium lub wysyłanie łatek, nie musi się martwić o scalenie, ponieważ już się to stało. Martin Fowler nazywa ten sposób praca rozwiązła integracja .

Ponieważ struktura różni się od Subversion, zamiast tego wykorzystuje DAG, umożliwia rozgałęzianie i scalanie w łatwiejszy sposób nie tylko dla systemu, ale także dla użytkownika.

 544
Author: Spoike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-22 09:38:59

Historycznie, Subversion był w stanie wykonać proste dwukierunkowe scalanie tylko dlatego, że nie przechowywał żadnych informacji o scalaniu. Wiąże się to z pobraniem zestawu zmian i nałożeniem ich na drzewo. Nawet w przypadku informacji merge, jest to nadal najczęściej używana strategia merge.

Git używa domyślnie 3-drożnego algorytmu scalania, który polega na znalezieniu wspólnego przodka dla scalanych głowic i wykorzystaniu wiedzy, która istnieje po obu stronach scalania. Pozwala to na Git być bardziej inteligentnym w unikaniu konfliktów.

Git posiada również wyrafinowany kod odnajdywania nazw, który również pomaga. nie przechowuje zestawów zmian ani żadnych informacji o śledzeniu -- przechowuje tylko stan plików przy każdym zatwierdzeniu i używa heurystyki, aby zlokalizować zmiany nazw i ruchy kodu zgodnie z wymaganiami (pamięć na dysku jest bardziej skomplikowana niż to, ale interfejs, który prezentuje warstwie logicznej, nie ujawnia śledzenia).

 27
Author: Andrew Aylett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-18 16:59:20

Mówiąc prościej, implementacja merge jest wykonywana lepiej w Git niż w SVN . Przed 1.5 SVN nie zapisywał akcji scalania, więc nie był w stanie wykonać przyszłych scaleń bez pomocy użytkownika, który musiał podać informacje, których SVN nie zarejestrował. Z 1.5 było lepiej i rzeczywiście model SVN storage jest nieco bardziej wydajny niż dag Gita. Ale SVN przechowuje informacje o scalaniu w dość zawiłej formie, dzięki czemu scalanie zajmuje znacznie więcej czasu niż w Git - I ' ve zaobserwowane czynniki 300 w czasie realizacji.

Ponadto SVN twierdzi, że śledzi zmiany nazw, aby wspomóc scalanie przeniesionych plików. Ale w rzeczywistości nadal przechowuje je jako kopię i oddzielną akcję delete, a algorytm scalania nadal natknie się na nie w sytuacjach Modyfikuj/Zmień nazwę, to znaczy, gdy plik jest modyfikowany w jednej gałęzi i zmienia nazwę w drugiej, a te gałęzie mają być scalone. Takie sytuacje nadal będą wywoływać fałszywe konflikty scalania, a w przypadku zmiany nazw katalogów prowadzi to nawet do cicha utrata modyfikacji. (Ludzie z SVN zwracają wtedy uwagę, że modyfikacje są nadal w historii, ale to niewiele pomaga, gdy nie są w wyniku scalenia, gdzie powinny się pojawić.

Git, z drugiej strony, nawet nie śledzi zmian nazw, ale wylicza je po fakcie (w czasie łączenia) i robi to całkiem magicznie.

Reprezentacja SVN merge ma również problemy; w 1.5/1.6 można łączyć się z trunk do branch tak często, jak tylko się lubi, automatycznie, ale połączenie w innym kierunku musiało zostać ogłoszone (--reintegrate) i pozostawić gałąź w stanie bezużytecznym. Znacznie później okazało się, że tak naprawdę nie jest i że a)--reintegrate można obliczyć automatycznie, A 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 dowolnym nietrywialnym scenariuszu rozgałęzień, a najlepiej spróbowałbym zobacz, co Git myśli o wyniku scalenia.

Inne punkty zawarte w odpowiedziach, jako wymuszona globalna widoczność gałęzi w SVN, nie mają znaczenia dla możliwości scalania (ale dla użyteczności). Ponadto, "Git stores changes while SVN stores (something different)" są w większości poza punktem. Git koncepcyjnie przechowuje każdy commit jako osobne drzewo (jak plik Tar), a następnie używa pewnych heurystyk, aby przechowywać je efektywnie. Obliczanie zmian pomiędzy dwoma commitami jest oddzielne z realizacji magazynu. Prawdą jest, że Git przechowuje historię dag w znacznie prostszej formie niż SVN robi swoje mergeinfo. Każdy, kto spróbuje zrozumieć to drugie, będzie wiedział, co mam na myśli.

W skrócie: Git używa znacznie prostszego modelu danych do przechowywania wersji niż SVN, a tym samym może włożyć dużo energii w rzeczywiste algorytmy scalania, zamiast próbować poradzić sobie z reprezentacją => praktycznie lepszym scalaniem.

 16
Author: Andreas Krey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-08 22:23:52

Przeczytałem zaakceptowaną odpowiedź. To po prostu złe.

SVN łączenie się może być bolesne, a także może być uciążliwe. Ale zignoruj, jak to naprawdę działa przez minutę. Nie ma informacji, które Git przechowuje lub może wyprowadzać, których SVN nie przechowuje lub nie może wyprowadzać. Co ważniejsze, nie ma powodu, dla którego trzymanie oddzielnych (czasami częściowych) kopii systemu kontroli wersji dostarczy Ci bardziej aktualnych informacji. Obie struktury są całkowicie równoważne.

Załóżmy, że chcesz zrobić "jakąś mądrą rzecz" Git jest "lepszy". A twoja sprawa jest sprawdzana w SVN.

Przekonwertuj swój SVN do równoważnej postaci Git, wykonaj to w Git, a następnie sprawdź wynik w kilku dodatkowych gałęziach. Jeśli możesz sobie wyobrazić zautomatyzowany sposób na przekształcenie problemu SVN w problem Git, to Git nie ma żadnej fundamentalnej przewagi.

Na koniec dnia, każdy system kontroli wersji pozwoli mi

1. Generate a set of objects at a given branch/revision.
2. Provide the difference between a parent child branch/revisions.

Dodatkowo, dla połączenie jest również przydatne (lub krytyczne), aby wiedzieć

3. The set of changes have been merged into a given branch/revision.

Mercurial , Git i Subversion (teraz natywnie, wcześniej używając svnmerge.py) może dostarczyć wszystkie trzy informacje. Aby zademonstrować coś zasadniczo lepszego z DVC, zwróć uwagę na czwartą część informacji, która jest dostępna w Git/Mercurial / DVC niedostępna w SVN / scentralizowanym VC.

To nie znaczy, że nie są lepszymi narzędziami!
 12
Author: Peter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-08 22:19:22

Jedna rzecz, o której nie wspomniano w innych odpowiedziach, a to naprawdę jest dużą zaletą DVCS, jest to, że możesz zatwierdzić lokalnie, zanim wypchniesz swoje zmiany. W SVN, kiedy miałem jakąś zmianę chciałem sprawdzić, a ktoś już zrobił commit na tej samej gałęzi w międzyczasie, oznaczało to, że musiałem zrobić svn update zanim mogłem commit. Oznacza to, że moje zmiany i zmiany od drugiej osoby są teraz mieszane ze sobą i nie ma sposobu, aby przerwać połączenie (jak z git reset lub hg update -C), ponieważ nie ma commit wrócić do. Jeśli scalanie nie jest trywialne, oznacza to, że nie możesz kontynuować pracy nad swoją funkcją przed wyczyszczeniem wyniku scalania.

Ale może to tylko zaleta dla ludzi, którzy są zbyt głupi, aby używać oddzielnych gałęzi (jeśli dobrze pamiętam, mieliśmy tylko jedną gałąź, która była używana do rozwoju w firmie, w której używałem SVN).

 10
Author: daniel kullmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-23 07:36:50

SVN śledzi pliki, podczas gdy Git śledzi Zawartość zmienia się. Jest wystarczająco sprytny, aby śledzić blok kodu, który został zrefakturowany z jednej klasy / pliku do drugiej. Wykorzystują dwa zupełnie różne podejścia do śledzenia Twojego źródła.

Nadal używam SVN mocno, ale jestem bardzo zadowolony z kilku razy używałem Git.

A nice read if you have the time: dlaczego wybrałem Git

 8
Author: used2could,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-08 22:16:47

Wystarczy przeczytać artykuł na blogu Joela (niestety jego ostatni). Ten jest o Mercurial, ale tak naprawdę mówi o zaletach rozproszonych systemów VC, takich jak Git.

Z rozproszoną kontrolą wersji, rozproszona część nie jest w rzeczywistości najciekawsza część. Ciekawostką jest to, że systemy te myślą w kategoriach zmian, a NIE wersji.

Przeczytaj artykuł tutaj.

 6
Author: rubayeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-18 17:09:01