/ / Scalanie: Hg / Git vs. SVN - git, svn, mercurial, merge, dvcs

Scalanie: Hg / Git vs. SVN - git, svn, mercurial, merge, dvcs

Często czytam, że Hg (i Git i ...) są lepsze w łączeniu niż SVN, ale nigdy nie widziałem praktycznych przykładów tego, gdzie Hg / Git może łączyć coś, co SVN zawodzi (lub gdzie SVN potrzebuje ręcznej interwencji). Czy mógłbyś opublikować listę krok po kroku operacji branch / modify / commit /...-, które pokazują, gdzie SVN mógłby się zawieść, podczas gdy Hg / Git z przyjemnością się porusza? Praktyczne, niezbyt wyjątkowe sprawy proszę ...

Niektóre tła: mamy kilkadziesiąt programistów pracujących nad projektami wykorzystującymi SVN, z każdym projektem (lub grupą podobnych projektów) we własnym repozytorium. Wiemy, jak stosować gałęzie wydań i funkcji, więc często nie napotykamy problemów (tj. Byliśmy tam, ale nauczyliśmy się przezwyciężyć Problemy Joela "jednego programisty powodującego traumę dla całościzespół "lub" potrzebuje sześciu programistów na dwa tygodnie, aby ponownie zintegrować oddział ") Mamy gałęzie dystrybucji, które są bardzo stabilne i używane tylko do poprawiania poprawek. Mamy pakiety, które powinny być wystarczająco stabilne, aby móc utworzyć wydanie w jednym Tydzień i mamy gałęzie funkcjonalności, na których mogą pracować pojedynczy programiści lub grupy programistów. Tak, są one usuwane po reintegracji, więc nie zagracają repozytorium. ;)

Więc nadal próbuję znaleźć zaletyHg / Git przez SVN. Bardzo chciałbym zdobyć praktyczne doświadczenie, ale nie ma większych projektów, które moglibyśmy przenieść do gry Hg / Git, więc utknąłem w grze z małymi sztucznymi projektami, które zawierają tylko kilka zmyślonych plików. Szukam kilku przypadków, w których można poczuć imponującą moc Hg / Git, ponieważ do tej pory często czytałem o nich, ale nie udało mi się ich znaleźć.

Odpowiedzi:

89 dla odpowiedzi № 1

Nie używam Subversion sam, ale z informacje o wersji dla Subversion 1.5: śledzenie łączenia (podstawowe) Wygląda na to, że istnieją następujące różnice w porównaniu do tego, jak działa śledzenie scalania w systemach kontroli wersji w pełnej wersji DAG, takich jak Git lub Mercurial.

  • Łączenie gałęzi z gałęzią różni się od łączenia gałęzi do linii głównej: z jakiegoś powodu połączenie gałęzi z gałęzią jest wymagane --reintegrate opcja do svn merge.

    W systemach kontroli wersji rozproszonych, takich jak Git lub Mercurial, nie ma techniczny różnica między gałęzią a gałęzią: wszystkie gałęzie są tworzone równe (może być społeczny różnica). Połączenie w dowolnym kierunku odbywa się w ten sam sposób.

  • Musisz podać nowe -g (--use-merge-history) opcja svn log i svn blame brać pod uwagę śledzenie łączenia.

    W Git i Mercurial śledzenie łączenia jest automatycznie brane pod uwagę podczas wyświetlania historii (dziennika) i obwiniania. W Git możesz poprosić o obserwowanie pierwszego rodzica tylko z --first-parent (Przypuszczam, że podobna opcja istnieje także w przypadku Mercurial), aby "odrzucić" informacje o śledzeniu scalania w git log.

  • Z tego co rozumiem svn:mergeinfo przechowuje informacje na temat konfliktów (Subversion opiera się na zestawie zmian), podczas gdy w Git i Mercurial jest to po prostu zatwierdzanie obiektów, które mogą mieć więcej niż jednego rodzica.

  • "Znane problemy" podsekcja do śledzenia łączenia w Subversionsugeruje, że wielokrotne / cykliczne / refleksyjne scalanie może nie działać poprawnie. Oznacza to, że przy następnych historiach scalanie drugie może nie działać właściwie ("A" może być drzewem lub gałęzią, a "B" może być odpowiednio gałęzią lub trunkem):

    * --- * --- x --- * --- y --- * --- * --- * --- M2 <- A                     / - * ---- M1 --- * --- * --- / <- B
    

    W przypadku zerwania powyższej grafiki ASCII: Rozgałęzienie "B" jest tworzone (rozwidlone) z oddziału "A" w wersji "x", a następnie gałąź "A" jest scalana w wersji "y" w odgałęzieniu "B" jako połączenie "M1", a na końcu w oddziale "B" jest scalony w gałąź "A" jako połączenie "M2".

    * --- * --- x --- * ----- M1 - * --- * --- M2 <- A        / / - * --- y --- * --- * --- / <- B
    

    W przypadku zerwania powyższej grafiki ASCII: Rozgałęzienie "B" jest tworzone (rozwidlone) z gałęzi "A" w wersji "x", jest połączone w gałąź "A" w "y" jako "M1", a później ponownie scalone w gałąź "A" jako "M2" .

  • Subversion może nie obsługiwać zaawansowanego przypadku krzyżowanie się.

    * --- b ----- B1 - M1 - * --- M3       / /      X /    / / --B2 - M2 - *
    

    Git radzi sobie z tą sytuacją w praktyce, stosując "rekursywną" strategię scalania. Nie jestem pewien co do Mercurial.

  • W "Znane problemy" istnieje ostrzeżenie, że funkcja śledzenia łączenia nie działa z nazwami plików, np. gdy jedna strona zmienia nazwę pliku (i prawdopodobnie modyfikuje go), a druga strona modyfikuje plik bez zmiany nazwy (pod starą nazwą).

    Zarówno Git, jak i Mercurial radzą sobie z takim przypadkiem w praktyce: Git za pomocą zmień nazwę, Używanie Mercurial zmień nazwę śledzenia.

HTH


119 dla odpowiedzi nr 2

Ja też szukałem przypadku, w którym, powiedzmy, Subversion nie połączy się z odgałęzieniem, a Mercurial (i Git, Bazaar, ...) robi to, co trzeba.

Księga SVN opisuje, jak pliki o zmienionej nazwie są niepoprawnie scalane. Dotyczy to Subversion 1.5, 1.6, 1.7, i 1.8! Próbowałem odtworzyć poniższą sytuację:

cd / tmp rm -rf svn-repo svn-checkout svnadminutwórz svn-repo Plik svn checkout: /// tmp / svn-repo svn-checkout cd svn-checkout gałęzie pnia mkdir echo "Żegnaj, Świat!" > trunk / hello.txt svn dodaje gałęzie pnia svn commit -m "Początkowy import". svn copy "^ / trunk" "^ / branches / rename" -m "Utwórz gałąź." Przełącznik svn "^ / trunk". echo "Hello, World!" > hello.txt svn commit -m "Aktualizacja na magistrali". przełącznik svn "^ / branches / rename". svn zmień nazwę hello.txt hello.en.txt svn commit -m "Zmień nazwę w oddziale." Przełącznik svn "^ / trunk". svn merge --reintegrate "^ / branches / rename"

Zgodnie z książką scalanie powinno kończyć się czysto, ale z błędnymi danymi w pliku o zmienionej nazwie od czasu aktualizacji trunk jest zapomniane. Zamiast tego pojawia się konflikt drzewa (jest to z Subversion 1.6.17, najnowszą wersją Debiana w momencie pisania):

--- Łączenie różnic między adresami URL repozytoriów na ".": A hello.en.txt C hello.txt Podsumowanie konfliktów: Konflikty drzew: 1

Nie powinno być żadnego konfliktu - aktualizacja powinna zostać scalona z nową nazwą pliku, podczas gdy Subversion się nie powiedzie, Mercurial obsługuje to poprawnie:

rm -rf /tmp/hg-repo
hg init /tmp/hg-repo
cd /tmp/hg-repo
echo "Goodbye, World!" > hello.txt
hg add hello.txt
hg commit -m "Initial import."
echo "Hello, World!" > hello.txt
hg commit -m "Update."
hg update 0
hg rename hello.txt hello.en.txt
hg commit -m "Rename."
hg merge

Przed scaleniem repozytorium wygląda tak (od hg glog):

@ zestaw zmian: 2: 6502899164cc | etykietka:         Wskazówka | rodzic: 0: d08bcebadd9e | użytkownik: Martin Geisler | data: czw. 01 12:29:19 2010 +0200 | podsumowanie: Zmień nazwę. | | o zestaw zmian: 1: 9d06fa155634 | / user: Martin Geisler | data: czw. 01 12:29:18 2010 +0200 | podsumowanie: aktualizacja. | o zestaw zmian: 0: d08bcebadd9e użytkownik: Martin Geisler data: czw. 01 12:29:18 2010 +0200 summary: Initial import.

Wynik połączenia to:

połączenie hello.en.txt i hello.txt z hello.en.txt 0 plików zaktualizowanych, 1 pliki połączone, 0 plików usuniętych, 0 plików nierozwiązanych (Oddział scalony, nie zapomnij o popełnieniu)

Innymi słowy: Mercurial podjął zmianę z wersji 1 i połączył ją z nową nazwą pliku z wersji 2 (hello.en.txt). Obsługa tego przypadku jest oczywiście niezbędna, aby wspierać refaktoryzację i refaktoryzację dokładnie rodzaj rzeczy, którą chcesz zrobić w oddziale.


17 dla odpowiedzi nr 3

Bez mówienia o zwykłych zaletach (zobowiązania offline, proces publikacji, ...) Oto przykład "scalania", który lubię:

Głównym scenariuszem, który widzę, jest gałąź, na której ... dwa niepowiązane zadania zostały faktycznie opracowane
(Zaczęło się od jednej funkcji, ale doprowadziło to do opracowania tej drugiej funkcji.
Lub zaczęło się od łatki, ale doprowadziło to do opracowania innej funkcji).

Jak połączyć tylko jedną z dwóch funkcji w głównej gałęzi?
Lub Jak wyodrębnić dwie funkcje w swoich oddziałach?

Możesz spróbować wygenerować jakieś łatki, problem z tym, że nie jesteś już pewien zależności funkcjonalne które mogło istnieć między:

  • zatwierdzenia (lub rewizja dla SVN) użyte w twoich łatach
  • inne zobowiązanie nie jest częścią łaty

Git (i także Mercurial, jak sądzę) proponują opcję rebase --onto do odświeżania (resetowania katalogu głównego oddziału) części oddziału:

Od Post Jefromi

- x - x - x (v2) - x - x - x (v2.1)

x - x - x (v2-only) - x - x - x (wss)

możesz rozplątać tę sytuację, w której masz łatki dla v2, a także nową funkcję wss na:

- x - x - x (v2) - x - x - x (v2.1)
|
|  x - x - x (v2-only)

x - x - x (wss)

, umożliwiając:

  • przetestuj każdą gałąź w izolacji, aby sprawdzić, czy wszystko się kompiluje / działa zgodnie z przeznaczeniem
  • scal tylko to, co chcesz.

Inną cechą, którą lubię (która wpływa na scalanie) jest zdolność squash zatwierdza (w oddziale, który nie został jeszcze przeniesiony na inny repozytorium), aby przedstawić:

  • czystsza historia
  • commits, które są bardziej spójne (zamiast commit1 dla funkcji 1, commit2 dla funkcji2, commit3 dla funkcji1 ...)

Zapewnia to scalenia, które są o wiele łatwiejsze, z mniejszymi konfliktami.


6 dla odpowiedzi № 4

Niedawno przeprowadziliśmy migrację z SVN do GIT i stanęliśmy w obliczu tej samej niepewności. Było wiele anegdotycznych dowodów na to, że GIT jest lepszy, ale trudno było znaleźć przykłady.

Mogę ci jednak powiedzieć GIT jest DUŻO LEPSZY przy scalaniu niż SVN. Jest to oczywiście anegdotyczne, ale istnieje tabela do naśladowania.

Oto niektóre z rzeczy, które znaleźliśmy:

  • SVN używał do wywoływania wielu konfliktów drzew w sytuacjach, w których wydawało się, że nie powinien. Nie doszliśmy do sedna tego, ale nie dzieje się to w GIT.
  • Choć lepiej, GIT jest znacznie bardziej skomplikowany. Poświęć trochę czasu na trening.
  • Byliśmy przyzwyczajeni do Tortoise SVN, co nam się podobało. GIT Tortoise nie jest tak dobry, a to może cię zniechęcić. Jednak teraz używam wiersza poleceń GIT, który bardzo wolę od SVN Tortoise lub dowolnego GUI GIT.

Kiedy ocenialiśmy GIT, wykonywaliśmy następujące czynnościtesty. Pokazują one GIT jako zwycięzcę, jeśli chodzi o łączenie, ale nie za dużo. W praktyce różnica jest znacznie większa, ale wydaje mi się, że nie udało nam się odtworzyć sytuacji, w których SVN źle sobie radzi.

GIT vs SVN Scalanie Ocena


5 dla odpowiedzi № 5

Inne dotyczyły bardziej teoretycznych aspektów tego. Może mogę pożyczyć bardziej praktyczną perspektywę.

Aktualnie pracuję dla firmy korzystającej z SVN w modelu rozwojowym "feature branch".

  • W bagażniku nie można wykonać żadnej pracy
  • Każdy programista może utworzyć własne gałęzie
  • Oddziały powinny trwać przez cały czas realizacji zadania
  • Każde zadanie powinno mieć własną gałąź
  • Scalanie z powrotem do bagażnika wymaga autoryzacji (zwykle poprzez bugzilla)
  • W czasach, gdy wymagany jest wysoki poziom kontroli, połączenia mogą być wykonywane przez strażnika

Ogólnie rzecz biorąc, to działa. SVN może być użyty do takiego przepływu, ale nie jest doskonały, istnieją pewne aspekty SVN, które przeszkadzają ludzkiemu zachowaniu, co daje mu pewne negatywne aspekty.

  • Mieliśmy sporo problemów z ludźmi rozgałęziającymi się z punktów poniżej ^/trunk. Te mioty łączą rekordy informacyjne w całym drzewie i ostatecznie przerywają śledzenie korespondencji seryjnej. Pojawiają się fałszywe konflikty i króluje zamieszanie.
  • Podnoszenie zmian z pnia do gałęzi jest stosunkowo proste. svn merge robi to, co chcesz. Scalanie zmian wymaga (powiedziano nam) --reintegrate w poleceniu scalania. Nigdy naprawdę nie rozumiałem tego przełącznika, ale oznacza to, że oddział nie może być ponownie połączony z pniem. Oznacza to, że jest to martwa gałąź i musisz utworzyć nową, aby kontynuować pracę. (Patrz uwaga)
  • Cała operacja wykonywania operacji na serwerze za pośrednictwem adresów URL podczas tworzenia i usuwania oddziałów naprawdę myli i przeraża ludzi. Więc unikają tego.
  • Przełączanie między gałęziami jest łatwe do uniknięcia, pozostawiając część drzewa patrząc na gałąź A, pozostawiając inną część patrząc na gałąź B. Dlatego ludzie wolą wykonywać całą swoją pracę w jednym oddziale.

Często zdarza się, że inżynier tworzyoddział w dniu 1. Rozpoczyna pracę i zapomina o tym. Jakiś czas później pojawia się szef i pyta, czy może zwolnić swoją pracę do bagażnika. Inżynier obawiał się tego dnia, ponieważ reintegracja oznacza:

  • Scalenie jego długiej gałęzi z powrotem do bagażnika i rozwiązanie wszystkich konfliktów oraz uwolnienie niepowiązanego kodu, który powinien znajdować się w osobnej gałęzi, ale nie był.
  • Usuwanie jego oddziału
  • Tworzenie nowego oddziału
  • Przełączam jego kopię roboczą do nowego oddziału

...a ponieważ inżynier robi to tak mało, jak tylko mogą, nie pamiętają "magicznej inkantacji", aby zrobić każdy krok. Złe przełączniki i adresy URL zdarzają się i nagle są "w bałaganie" i otrzymują "eksperta".

W końcu wszystko ustaje i ludzie się ucząjak radzić sobie z niedociągnięciami, ale każdy nowy starter ma te same problemy. Ostateczna rzeczywistość (w przeciwieństwie do tego, co przedstawiłem na początku) to:

  • Żadna praca nie jest wykonywana na bagażniku
  • Każdy programista ma jeden główny oddział
  • Oddziały trwają do momentu zwolnienia pracy
  • Naprawione poprawki błędów mają tendencję do zdobywania własnego oddziału
  • Scalanie z powrotem do bagażnika jest wykonywane po uzyskaniu autoryzacji

...ale...

  • Czasami praca sprawia, że ​​do bagażnika, gdy nie powinien, ponieważ jest w tej samej gałęzi, co inne.
  • Ludzie unikają łączenia się (nawet łatwe rzeczy), więc ludzie często pracują we własnych małych bańkach
  • Wielkie zlepki mają tendencję do występowania i powodują ograniczoną ilość chaosu.

Na szczęście zespół jest na tyle mały, by sobie z tym poradzić, aleNie jest to żaden problem z CVCS, ale więcej, ponieważ połączenia nie są tak ważne jak w DVCS, nie są tak zręczne, że "scalanie tarcia" powoduje zachowanie, które oznacza, że ​​" Model Oddziału Cechy zaczyna się rozpadać, dobre połączenia muszą być cechą wszystkich VCS, a nie tylko DVCS.


Według to teraz jest --record-only przełącznik, który może być użyty do rozwiązania --reintegrate problem, i widocznie v1.8 wybiera, kiedy ma się automatycznie ponownie włączyć i nie spowoduje, że gałąź będzie martwa później


3 dla odpowiedzi № 6

Przed subwersją 1.5 (jeśli się nie mylę), subwersja miała znaczną wadę, ponieważ nie zapamięta historii łączenia.

Spójrzmy na sprawę opisaną przez VonC:

- x - x - x (v2) - x - x - x (v2.1)
|
|  x - A - x (v2-only)

x - B - x (wss)

Zwróć uwagę na wersje A i B. Załóżmy, że połączyłeś zmiany z wersji A w gałęzi "wss" z gałęzią "tylko v2" w wersji B (z dowolnego powodu), ale nadal używasz obu gałęzi. Jeśli próbowałbyś scalić dwie gałęzie ponownie używając mercurial, scaliłbyś tylko zmiany po wersjach A i B. Z subversion, musiałbyś scalić wszystko, tak jakbyś wcześniej nie robił scalenia.

Jest to przykład z własnego doświadczenia, w którym połączenie z B na A zajęło kilka godzin ze względu na objętość kodu: to byłby prawdziwy ból do przejścia jeszcze raz, co miałoby miejsce w przypadku subversion pre-1.5.

Kolejna, prawdopodobnie bardziej istotna różnica w porównaniu z zachowaniem scalania Hginit: Reversion:

Wyobraź sobie, że ty i ja pracujemy jakiś kod,i rozgałęziamy ten kod, i każdy z nas idzie do naszej oddzielnej przestrzenie robocze i rób dużo i dużo zmiany tego kodu osobno, więc trochę się rozeszły.

Kiedy musimy się połączyć, Subversion próbuje spojrzećw obu wersjach - mój zmodyfikowany kod i zmodyfikowany kod - i próbuje zgadywać, jak to zrobić rozbij ich razem w jednym wielkim przeklętym bałagan. Zwykle zawodzi, produkuje strony i strony "konfliktów seryjnych" to nie są tak naprawdę konflikty, po prostu miejsca, w których nie działa Subversion dowiedzieć się, co zrobiliśmy.

Natomiast podczas gdy pracowaliśmy osobno wMercurial, Mercurial był zajęte prowadzenie serii zestawów zmian. A więc, kiedy chcemy scalić nasz kod razem, Mercurial faktycznie ma dużo więcej informacji: wie co każdy z nas zmienił i może zastosuj ponownie te zmiany, a nie tylko patrząc na końcowy produkt i próbując zgadnąć, jak to ująć razem.

W skrócie, sposób analizy różnic Mercurial jest (był?) Lepszy od subwersji.