W ostatnich latach rozproszone systemy kontroli wersji (w skrócie DVCS) stały się popularne, zwłaszcza w środowiskach związanych z open source. Warto je jednak znać nie tylko wtedy, gdy pracujemy nad projektami z otwartym źródłem. Jak bowiem pisałem wcześniej, mogą być one przydatne chociażby do małych jednoosobowych projektów. Ponadto bywają nierzadko używane w większych oraz mniejszych firmach. Ogólnie mamy więc duże szanse, aby prędzej lub później zetknąć się z takimi systemami.
Ten post jest adresowany przede wszystkim do osób, które nie miały wcześniej dłuższej styczności rozproszonymi systemami kontroli wersji. Ma on na celu wyjaśnienie wielu spośród tych tajemniczo brzmiących słówek, na jakie nieustannie można się natknąć, gdy pracujemy z DVCS-ami. Istnieje oczywiście szansa, że bardziej zaawansowani użytkownicy również wyciągną nowe wiadomości z lektury (albo chociaż przejrzenia) niniejszej notki. A jeśli nawet nie, to powtórzenie znanej sobie wiedzy na pewno im nie zaszkodzi ;)
Dobrym pojęciem na początek – bo prostym – jest repozytorium, czyli miejsce składowania wszystkich wersji wszystkich plików źródłowych naszego projektu. Ponieważ zawartości repozytorium zwykle nie edytuje się bezpośrednio, do pracy nad projektem wykorzystujemy kopię roboczą (working copy): lustrzane odbicie którejś z jego wersji (zwykle najnowszej), będące zwykłym zbiorem plików i katalogów. Do nich właśnie wprowadzamy zmiany, które potem ewentualnie będziemy chcieli zachować.
Zapisywanie zmian w repozytorium odbywa się operacją commit, dla której nie ma żadnego dobrego polskiego odpowiednika. Po jej zakończeniu powstaje w repozytorium nowa wersja projektu, a nasza kopia robocza staje się tym samym jej odbiciem. Możliwa jest też operacja odwrotna, gdy to z repozytorium pozyskujemy kopię roboczą, czyli wykonujemy uaktualnienie (update lub checkout). Dzieje się tak zwykle wtedy, gdy chcemy dalej edytować wersję utworzoną (“zcommitowaną“) przez kogoś innego,
Powyższe pojęcia (commit, update, itd.) występują w każdym systemie kontroli wersji, niekoniecznie rozproszonym. Jeśli jednak mówimy o systemach zdecentralizowanych – takich jak Git czy Mercurial – to pojawia się też kilka nowych terminów związanych z interakcją między repozytoriami. Trzeba powiem pamiętać, że istotą takich systemów jest właśnie fakt, że w grę wchodzi w nich wiele repozytoriów naraz; typowo na przykład każdy programista ma swoje własne, lokalne. Stąd też potrzebne są sposoby na wymianę danych (czyli wersji) między nimi.
Odbywa się to za pomocą operacji synchronizacji zmian między dwoma repozytoriami. Jest ona właściwie symetryczna, ale zwykle wyróżnia się dwa jej aspekty, zależne od punktu… ekhm… siedzenia :) Możemy mianowicie wprowadzać zmiany zapisane w “naszym” (np. lokalnym) repozytorium do innego (np. zdalnego). Taki efekt wywołuje użycie komendy push (“pchaj”) i właśnie pod tym mianem znany jest ten koniec procesu synchronizacji. Z drugiej strony możliwe jest pobranie zmian z innego repozytorium i wprowadzenie do naszego – używa się wtedy polecenia pull (“ciągnij”) lub fetch (“weź”). Dlaczego terminologia tych operacji kojarzy się raczej z próbą wydostania samochodu zakopanego w śniegu – to pewnie pozostanie tajemnicą twórcą DVCS-ów ;]
Po synchronizacji może okazać się, że w repozytorium powstały nam dodatkowe gałęzie (branches), czyli osobne ścieżki wprowadzania zmian do projektu. W przeciwieństwie np. do SVN-a, systemy rozproszone mają wbudowane wsparcie dla rozgałęziania procesu tworzenia, więc nie będzie to w żaden sposób sytuacją nadzwyczajną. Jeśli jednak rozgałęzienie nie było celowe, wtedy typowym wyjściem jest złączenie (merge) dwóch ścieżek z powrotem w jedną. Po tej operacji – do której wlicza się rozwiązanie ewentualnych konfliktów – liczbą gałęzi zmniejsza się o jeden, a czynność łączenia rejestrowana jest jako osobny commit.
Kończąc to krótkie wprowadzenie/powtórzenie warto jeszcze wspomnieć o… początku, czyli pozyskaniu własnego repozytorium, aby móc dołączyć się do całej tej rozproszonej zabawy. Jeśli sami zaczynamy projekt, to możemy utworzyć je jako puste (poleceniem init w katalogu projektu) i wypełnić pierwszym commitem.
Częściej jednak będziemy klonowali (clone) już istniejące repozytorium, tworząc jego własną (lokalną) kopię. Będzie ona identyczna, zawierać będzie pełną kopię projektu wraz z całą historią zmian i pozwoli tym samym na wprowadzanie (i testowanie!) własnych modyfikacji. Właśnie ta własność rozproszonych systemów kontroli wersji sprawia zresztą, że są one tak popularne w projektach z otwartym źródłem.
Jeśli chcemy dowiedzieć się więcej o wspomnianych tutaj operacjach, pojęciach, działaniu DVCS-ów i pracy z nimi, to polecam którąś z dwóch poniższych książek (dostępnym za darmo online), w zależności od systemu, który nas interesuje:
IMO trochę pomieszałeś w tej terminologii :) Przynajmniej w przypadku gita repozytorium = working copy, “samo” repozytorium to bare repository. Dalej: w gicie nie ma czegoś takiego jak checkout/update. Najbliższym odpowiednikiem byłby chyba switch. Pull to tylko skrót na fetch+merge (albo rebase — o tym też warto by wspomnieć). Ogólnie trzeba pamiętać, że git czy inny DVCS to nie “lepszy SVN” tylko coś zupełnie innego :)
W gicie IIRC jest “working tree”, do którego się checkoutuje. Pull i fetch są używane wymiennie zależnie od systemu; o ile pamiętam, w Gicie pull=fetch+merge, a w Hg fetch=pull+merge ;)
O rebase, tagowaniu, tworzeniu patchy, transplantacji changesetów, itp. itd. oczywiście też “warto by wspomnieć”, ale gdzieś trzeba postawić granicę między podstawami a bardziej zaawansowanymi kwestiami :)
nieudane merge do dzisiaj nie pozwalają mi spokojnie zasnąć