Kontrola wersji

System kontroli wersji (ang. version control system lub revision control system) to zestaw narzędzi do śledzenia i kontrolowania zmian w plikach projektu, w szczególności w kodzie źródłowym, dokumentacji i stronach WWW. Jeśli nigdy nie korzystałeś z kontroli wersji, powinieneś jak najprędzej znaleźć kogoś, kto korzystał, i przekonać go, aby dołączył do projektu. W dzisiejszych czasach każdy spodziewa się, że przynajmniej kod źródłowy projektu znajduje się pod kontrolą wersji, i nie traktuje poważnie projektów, które nie korzystają z niej przynajmniej minimalnie umiejętnie.

Kontrola wersji jest tak uniwersalna, ponieważ pomaga praktycznie w każdym aspekcie projektu: komunikacji między programistami, zarządzaniu wydaniami, śledzeniu błędów, stabilizacji kodu czy eksperymentowaniu, może także służyć do uwierzyteniania i przypisywania zasług poszczególnym programistom. System kontroli wersji stanowi centralny punkt we wszystkich wspomnianych obszarach. Sercem kontroli wersji jest zarządzanie zmianami, pozwalające odróżnić każdą atomową zmianę w plikach projektu, metadane z nią związane (jak data i autor danej zmiany) i przekazać ją do innych użytkowników w sposób, w jaki sobie tego zażyczą. To kanał komunikacyjny, w którym podstawową jednostką informacji jest pojedyncza zmiana.

Ten rozdział nie omawia wszystkich aspektów korzystania z systemu kontroli wersji. To tak rozległy temat, że przy okazji omawiania innych kwestii będziemy do niego wracali jeszcze nie raz w tej książce. Tutaj skupimy się na wybraniu i konfiguracji systemu kontroli wersji w taki sposób, który wspiera współpracę nad rozwojem projektu.

Słownictwo kontroli wersji

Ta książka nie może nauczyć cię kontroli wersji, jeśli nigdy wcześniej z niej nie korzystałeś. Z drugiej strony trudno byłoby rozmawiać o niej bez wprowadzenia kilku podstawowych pojęć. Te pojęcia są niezależne od konkretnego systemu kontroli wersji, to podstawowe rzeczowniki i przymiotniki gdy współpracujemy w sieci, i będą często stosowane w dalszej części książki. Nawet gdyby nie wymyślono systemów kontroli wersji, problem zarządzania zmianami ciągle by istniał—a więc potrzebujemy języka, pozwalającego mówić o problemie w zwięzły sposób.

commit

Dokonać zmiany w projekcie. Bardziej formalnie, zapisać zmianę w bazie danych systemu kontroli wersji w taki sposób, że może zostać włączona do przyszłych wydań projektu. "Commit" można stosować jako czasownik lub rzeczownik[14], jako rzeczownik to w zasadzie synonim "zmiany". Na przykład: "Właśnie zrobiłem commit poprawki dla błędu powodującego problemy ze stabilnością serwera, zgłaszanego przez użytkowników Mac OS X. Jay, mógłbyś przejrzeć commit i sprawdzić, czy dobrze używam w niej alokatora?"

komentarz do zmiany (ang. log message)

Krótka wiadomość dołączona do każdego commita, opisująca jego naturę i cel. Komentarze do zmian to jedne z najważniejszych dokumentów każdego projektu, stanowią most pomiędzy bardzo technicznym językiem samego kodu, a bardziej zorientowanym na użytkownika językiem mówiącym o zadaniach, poprawkach błędów i postępie projektu. Dalej w tym rozdziale omówimy sposoby dystrybucji komentarzy zmian do odpowiednich odbiorców. Co więcej, „Codifying Tradition” w Rozdział 6, Communications mówi o tym, jak zachęcić uczestników projektu do pisania zwięzłych i użytecznych komentarzy do zmian.

aktualizacja (ang. update)

Aktualizacja to dołączenie zmian dokonanych przez innych do lokalnej kopii projektu, a więc sprawienie, aby kopia stała się aktualna. To bardzo częsta czynność, większość programistów aktualizuje swój kod wiele razy dziennie, aby mieć pewność, że pracują mniej-więcej na tej samej wersji kodu, co pozostali. Co więcej, gdy znajdą błąd, mogą być niemal pewni, że nie został on jeszcze naprawiony. Na przykład: "Hej, zauważyłem, że kod indeksujący zawsze wyrzuca ostatni bajt. Czy to nowy błąd?" "Tak, ale został już naprawiony tydzień temu—zrób aktualizację, powinien zniknąć."

repozytorium (ang. repository)

Baza danych, w której zapisywane są zmiany. Niektóre systemy wersji są zcentralizowane: istnieje jedno główne repozytorium, przechowujące wszystkie zmiany. Inne są zdecentralizowane: każdy programista ma swoje własne repozytorium, a zmiany mogą być dowolnie przenoszone pomiędzy repozytoriami. System kontroli wersji śledzi zależności pomiędzy zmianami, i gdy nadejdzie czas wydania, odpowiedni zestaw zmian zostaje zaakceptowany do nowej wersji. Pytanie czy lepsze są systemy zcentralizowane czy rozproszone to jedna z wciąż żywych programistycznych świętych wojen, postaraj się unikać kłótni na ten temat na listach dyskusyjnych projektu.

checkout

Proces pobierania kopii projektu z repozytorium. Checkout[15] zazwyczaj tworzy drzewo podkatalogów, nazywane "kopią roboczą" (patrz niżej), w której zmiany mogą być wysyłane do repozytorium (jako commit). W niektórych zdecentralizowanych systemach kontroli wersji każda kopia robocza jest zarazem repozytorium, a zmiany mogą być wypychane z (lub wciągane do) każdego repozytorium, które je zaakceptuje.

kopia robocza (ang. working copy)

Prywatne drzewo katalogów każdego programisty, zawierające kod źródłowy projektu i być może jego stronę WWW, czy inne dokumenty. Kopia robocza zawiera także pewną ilość metadanych, zarządzanych przez system kontroli wersji, określających z jakiego repozytorium pochodzi, jakie "wersje" (patrz niżej) plików są obecne, itd. Na ogół każdy programista ma swoją własną kopię roboczą, w której dokonuje zmian i je testuje, i z której wykonuje commit.

wersja (ang. revision), zmiana (ang. change), zbiór zmian (ang. changeset)

"Wersja" to na ogół konkretne wcielenie danego pliku lub katalogu. Na przykład plik F może być najpierw w wersji 6, następnie ktoś wykonuje commit zmieniający plik F, tworząc wersję 7 pliku F. Niektóre systemy stosują pojęcia "wersja", "zmiana" czy "zbiór zmian" aby opisać zbiór zmian, wysłanych w jednym commicie jako jedna abstrakcyjna operacja.

Niekiedy te pojęcia mają różne znaczenia w poszczególnych systemach kontroli wersji, ale sama idea pozostaje bez zmian: pozwalają one mówić precyzyjnie o pewnych punktach w czasie historii pliku lub zbioru plików (na przykład przed i po poprawce jakiegoś błędu). Na przykład: "Tak, naprawiła to w wersji 10" lub "Naprawiła to w wersji 10 pliku foo.c".

Gdy ktoś mówi o pliku lub grupie plików bez wspominania o konkretnej wersji, na ogół zakłada się, że ma na myśli jego (ich) najnowszą wersję dostępną w repozytorium.

diff

Tekstowa reprezentacja zmiany. Diff wskazuje, które linie uległy zmianie i w jaki sposób, pokazując przy okazji kilka linii kontekstu przed i po. Programista, który zna dany obszar kodu może na ogół przeczytać diff i zrozumieć, na czym polega zmiana, a być może nawet wyłapać błędy.

etykieta (ang. tag)

Określa pewien zbiór plików w ściśle określonych wersjach. Etykiety sa na ogół używane do oznaczania ważnych momentów w życiu projektu. Na przykład etykieta jest na ogół tworzona dla każdego publicznego wydania, tak, aby każdy mógł pobrać bezpośrednio z repozytorium dokładnie te same wersje plików, które zostały wydane. Popularnymi nazwami etykiet są na przykład Release_1_0, Delivery_00456, itd.

gałąź (ang. branch)

Kopia projektu znajdująca się w repozytorium, ale izolowana w taki sposób, że zmiany w gałęzi nie wpływają na resztę projektu, i odwrotnie, do czasu gdy zmiany nie zostaną przez kogoś "scalone" (patrz niżej). Gałęzie bywają także nazywane "liniami rozwoju". Nawet gdy projekt nie posiada wielu gałęzi, mówi się że rozwój odbywa się na "głównej gałęzi", także nazywanej "pniem" (ang. trunk).

Gałęzie dają możliwość odizolowania od siebie różnych linii rozwoju. Na przykład gałąź może powstać na potrzeby eksperymentalnego kodu, który mógłby zdestabilizować główną gałąź. Albo odwrotnie, gałąź może być użyta jako miejsce, w którym nowe wydanie będzie stabilizowane. Podczas procesu wydania normalny rozwój może odbywać się w głównej gałęzi repozytorium, a w tym samym czasie na gałęzi utworzonej dla wydania nie będą akceptowane żadne zmiany oprócz tych, które zostały dozwolone przez zarządzających wydaniem. W ten sposób wydanie nowej wersji nie musi kolidować z normalnym rozwojem. Patrz „Use branches to avoid bottlenecks” dalej w tym rozdziale aby dowiedzieć się więcej o gałęziach.

scalanie lub portowanie (ang. merge lub port)

Przeniesienie zmiany z jednej gałęzi na inną. Chodzi zarówno o scalanie zmian z głównej gałęzi do innej gałęzi, jak i w drugą stronę. To najczęściej stosowane w praktyce rodzaje scaleń—rzadko przenosi się zmiany pomiędzy dwoma gałęziami, z których żadna nie jest główną. Patrz „Singularity of information” oby dowiedzieć się więcej o takich scalaniach.

"Scalanie" ma jeszcze drugie, podobne znaczenie: to zachowanie systemu kontroli wersji gdy stwierdzi, że dwóch użytkowników dokonało zmian w tym samym pliku, które nie zachodzą na siebie. Takie zmiany na ogół nie mają na siebie wpływu, więc u tego użytkownika, który dokona aktualizacji pliku (w którym sam dokonał zmian), zmiany innych zostaną automatycznie scalone. To bardzo częsta sytuacja, szczególnie w projektach, w których wiele osób pracuje nad tym samym kodem. Gdy dwie różne zmiany zachodzą na siebie, pojawia się "konflikt" (patrz niżej).

konflikt (ang. conflict)

Konflikt zachodzi, gdy dwie osoby próbują dokonać zmian dokładnie w tym samym miejscu kodu. Wszystkie systemy kontroli wersji automatycznie wykrywają konflikty i powiadamiają jednego z użytkowników o ich istnieniu. Reszta jest w rękach człowieka, musi on ręcznie rozwiązać (ang. resolve) konflikt i poinformować o tym system kontroli wersji.

blokada (ang. lock)

Sposób na zadeklarowanie wyłączności na zmiany w danym pliku lub katalogu. Na przykład: "Nie mogę zrobić commitu moich zmian w stronach WWW. Wygląda na to, że Alfred założył blokadę na czas poprawek w obrazkach tła." Nie wszystkie systemy kontroli wersji pozwalają na stosowanie blokad, a z tych które pozwalają, nie wszystkie wymagają korzystania z nich. To dlatego, że równoległy wspólny rozwój jest standardem, a blokowanie plików przed zmianami innych jest (na ogół) sprzeczne z tą ideą.

Mówi się, że systemy kontroli wersji, które wymagają stosowania blokad aby wykonać commit, pracują w modelu blokada-modyfikacja-odblokowanie. Te, które ich nie wymagają stosują model kopia-modyfikacja-scalanie. Doskonałe wyjaśnienie i porównanie obu modeli można znaleźć w http://svnbook.red-bean.com/svnbook-1.0/ch02s02.html. Na ogół model kopia-modyfikacja-scalanie jest lepszy dla rozwoju otwartego oprogramowania, i wszystkie systemy kontroli wersji omówione w tym rozdziale wspierają taki model.

Wybór systemu kontroli wersji

W chwili pisania tych słów dwoma najpopularniejszymi systemami kontroli wersji w świecie wolnego oprogramowania sią CVS (Concurrent Versions System, http://www.cvshome.org/) i SVN (Subversion, http://subversion.tigris.org/).

CVS istnieje od bardzo dawna. Większość doświadczonych programistów jest już przyzwyczajona do korzystania z niego. Mniej więcej spełania swoje zadanie i jest popularny od bardzo dawna, więc wybierając CVS raczej nie doprowadzisz do długiej dyskusji o tym, czy to dobry wybór. Jednak CVS ma pewne wady. Nie pozwala na łatwe odróżnianie tego, że dana zmiana dotyczyła wielu plików, nie pozwala na zmianę nazwy czy kopiowanie plików pod kontrolą wersji (jeśli więc potrzebujesz przeorganizować strukturę kodu w trakcie projektu, masz problem), ma słabe wsparcie dla scalania, nie obsługuje dobrze dużych plików ani plików binarnych, a niektóre operacje bywają bardzo powolne, gdy dotyczą wielu plików.

Żadna z tych wad nie jest dyskwalifikująca, więc CVS jest wciąż dość popularny. Jednak w ostatnich kilku latach system kontroli wersji Subversion zaczął zyskiwać zwolenników, szczególnie w nowszych projektach.[17] Jeśli rozpoczynasz nowy projekt, polecam Subversion.

Z drugiej strony jestem zaangażowany w projekt Subversion, więc moja obiektywność może być kwestionowana. Co więcej, w ostatnich latach pojawiło się wiele nowych systemów kontroli wersji na wolnych licencjach. Dodatek A, Wolne Systemy Kontroli Wersji (VCS) zawiera listę wszystkich, które znam, posortowanych według (przybliżonej) popularności. Uświadamia ona, że wybór systemu kontroli wersji może stać się projektem badawczym na całe życie. Być może dylematów pozbawi Cię dostawca hostingu, jeśli jednak musisz wybierać, porozmawiaj z innymi programistami, zapytaj znajomych o ich doświadczenia, a następnie wybierz jeden system i przy nim pozostań. Każdy stabilny i gotowy do zastnowań produkcyjnych system kontroli wersji powinien wystarczyć, nie musisz martwić się za bardzo skutkami swojej decyzji. Jeśli jednak nie możesz się zdecydować, skorzystaj z Subversion. Jest dość łatwy w nauce, i najprawdopodobniej pozostanie standardem przez przynajmniej kilka late.

Korzystanie z systemu kontroli wersji

Rady w tym rozdziale nie dotyczą jakiegoś konkretnego systemu kontroli wersji, ich zastosowanie powinno być łatwe w każdym z nich. Sprawdź dokumentację Twojego systemu kontroli wersji aby dowiedzieć się wiecej.

Wersjonuj wszystko

Kontrolę wersji stosuj nie tylko dla kodu źródłowego projeku, ale także strony internetowej, dokumentacji, FAQ, dokumentów projektowych i wszystkiego innego, co ludzie mogliby edytować. Przechowuj je zaraz obok kodu, w tym samym repozytorium. Każdą informację, którą warto zapisać, warto też wersjonować—a więc każdą informację, która może ulec zmienie. Rzeczy które nie ulegają zmianie powinny być archiwizowane, nie wersjonowane. Na przykład email po wysłaniu nie może już zmienić treści, a więc kontrola wersji w tym przypadku nie miałaby sensu (do czasu, gdy nie stanie się częścią jakiegoś większego, ewolującego dokumentu).

Wersjonowanie wszystkiego razem, w jednym miejscu, jest tak ważne, gdyż wtedy ludzi muszą nauczyć się tylko jednego mechanizmu nadsyłania zmian. Często uczestnik projektu zaczyna od edycji strony internetowej czy dokumentacji, a następnie stopniowo przechodzi do trudniejszych zadań. Gdy projekt korzysta z tego samego systemu dla wszystkich zmian, ludzi muszą nauczyć się korzystać z niego jedynie raz. Wersjonowanie wszystkiego razem oznacza też, że commit nowego kodu może zawierać też aktualizację dokumentacji, utworzenie gałęzi kodu tworzy także gałąź dokumentacji, i tak dalej.

W przechowuj w repozytorium plików generowanych. Nie są one edytowalnymi danymi, gdyż są generowane automatycznie na podstawie innych plików. Na przykład niektóre systemy budowania tworzą plik configure w oparciu o szablon configure.in. Aby dokonać zmian w pliku configure należy wyedytować configure.in i wygenerować plik wynikowy, a więc jedynie szablon jest "plikiem edytowalnym". Wersjonuj jedynie szablony—jeśli będziesz wersjonować także pliki wynikowe, ludzi będą zapominać o tym, aby je ponownie wygenerować po dokonaniu zmian w szablonie, a wynikające z tego niespójności będą prowadzić do sporego zamieszania.[18]

Od zasady mówiącej, że wszystkie edytowalne dane powinny być przechowywane w systemie kontroli wersji, jest niestety jeden wyjątek: system śledzenia błędów. Bazy błędów zawierają wiele edytowalnych danych, ale z powodów technicznych nie przechowują ich w głównym systemie kontroli wersji. Niektóre z nich mają własne prymitywne możliwości wersjonowania, są one jednak zupełnie niezależne od głównego repozytorium projektu.

Możliwość przeglądania

Repozytorium projektu powinno dać się przeglądać przy użyciu przeglądarki internetowej. Nie chodzi tu tylko o możliwość dostępu do najnowszych wersji plików, ale także cofanie się w czasie i podgląd starych wersji, porównywanie różnic pomiędzy wersjami, czytania komentarzy do zmian, i tak dalej.

Możliwość przeglądania jest istotna, ponieważ daje możliwość łatwego dostępu do danych projektu. Gdyby repozytorium nie było dostępne za pomocą przeglądarki, wtedy każdy kto chciałby sprawdzić zawartość jakiegoś pliku (na przykład aby sprawdzić, czy pewna poprawka została dołączona do kodu) musiałby najpierw zainstalować u siebie klienta systemu kontroli wersji, co mogłoby zmienić kilkuminutowe zapytanie w pół godzinne zadanie.

Możliwość przeglądania zakłada też istnienie standardowych adresów do przeglądania wybranych wersji plików, włączając w to tę najnowszą. To może być bardzo przydatne w dyskusjach technicznych lub w odsyłaczach do dokumentacji. Na przykład zamiast mówić "Aby znaleźć porady dotyczące debugowania serwera sprawdź plik www/hacking.html w swojej kopii roboczej" można powiedzieć "Aby znaleźć porady dotyczące debugowania serwera sprawdź http://subversion.apache.org/docs/community-guide/", podając adres wskazujący na najbardziej aktualną wersję pliku hacking.html. Adres URL jest lepszy, ponieważ jest jednoznaczny i niezależny od tego, na ile aktualną kopię roboczą posiada adresat.

Niektóre systemy kontroli wersji posiadają wbudowane mechanizmy do przeglądania repozytorium, inne polegają na zewnętrznych narzędziach. Takimi narzędziami są ViewCVS (http://viewcvs.sourceforge.net/), CVSWeb (http://www.freebsd.org/projects/cvsweb.html), i WebSVN (http://websvn.tigris.org/). Pierwszy z nich współpracuje zarówno z CVS jak i Subversion, drugi jedynie z CVS, a trzeci tylko i wyłącznie z Subversion.

Powiadomienia o commitach

Każdy commit do repozytorim powinien powodować wysłanie emaila zawierającego informacje o autorze zmian, czasie ich wykonania, zmodyfikowanych plikach i różnicach w stosunku do wersji pierwotnej. Ten email powinien zostać wysłany na specjalną listę dyskusyjną, odrębną od tej, na której dyskutują ludzie. Programiści (i inne zainteresowane osoby) powinni być zachęcani do subskrybowania tej listy, gdyż jest to najefektywniejszy sposób na to, aby śledzić zmiany w projekcie na poziomie kodu. Oprócz oczywistych zalet takiego przeglądu kodu (patrz „Practice Conspicuous Code Review”), takie powiadomienia wzmacniają społeczność, gdyż tworzą wspólne forum na którym ludzie mogą reagować na zdarzenia (commity) widoczne dla wszystkich innych.

Szczegóły konfiguracji powiadomień są zależne od konkretnego systemu kontroli wersji, ale na ogół istnieje gotowy skrypt, który można w tym celu wykorzystać. Jeśli masz problemy z jego odnalezieniem, spróbuj poszukać w dokumentacji pod hasłem hooks, a w szczególności post-commit hook (w terminologii CVS loginfo hook). Nie ma przyjętego polskiego odpowiednika, więc na potrzeby tego rozdziału użyjemy bezpośredniego tłumaczenia "hak". Haki post-commit pozwalają na automatyczne wykonywanie pewnych zadań w odpowiedzi na commi. Hak zostaje wykonany dla każdego commita, otrzymując wszystkie konieczne informacje o nim—których może użyć na przykład do wysłania emaila.

Czasem warto zmodyfikować domyślne zachowanie gotowego skryptu wysyłającego powiadomienia:

  1. Some commit mailers don't include the actual diffs in the email, but instead provide a URL to view the change on the web using the repository browsing system. While it's good to provide the URL, so the change can be referred to later, it is also very important that the commit email include the diffs themselves. Reading email is already part of people's routine, so if the content of the change is visible right there in the commit email, developers will review the commit on the spot, without leaving their mail reader. If they have to click on a URL to review the change, most won't do it, because that requires a new action instead of a continuation of what they were already doing. Furthermore, if the reviewer wants to ask something about the change, it's vastly easier to hit reply-with-text and simply annotate the quoted diff than it is to visit a web page and laboriously cut-and-paste parts of the diff from web browser to email client.

    (Of course, if the diff is huge, such as when a large body of new code has been added to the repository, then it makes sense to omit the diff and offer only the URL. Most commit mailers can do this kind of limiting automatically. If yours can't, then it's still better to include diffs, and live with the occasional huge email, than to leave the diffs off entirely. Convenient reviewing and commenting is a cornerstone of cooperative development, much too important to do without.)

  2. The commit emails should set their Reply-to header to the regular development list, not the commit email list. That is, when someone reviews a commit and writes a response, their response should be automatically directed toward the human development list, where technical issues are normally discussed. There are a few reasons for this. First, you want to keep all technical discussion on one list, because that's where people expect it to happen, and because that way there's only one archive to search. Second, there might be interested parties not subscribed to the commit email list. Third, the commit email list advertises itself as a service for watching commits, not for watching commits and occasional technical discussions. Those who subscribed to the commit email list did not sign up for anything but commit emails; sending them other material via that list would violate an implicit contract. Fourth, people often write programs that read the commit email list and process the results (for display on a web page, for example). Those programs are prepared to handle consistently-formatted commit emails, but not inconsistent human-written mails.

    Note that this advice to set Reply-to does not contradict the recommendations in „Wielka debata o nagłówek Reply-to” earlier in this chapter. It's always okay for the sender of a message to set Reply-to. In this case, the sender is the version control system itself, and it sets Reply-to in order to indicate that the appropriate place for replies is the development mailing list, not the commit list.

Use branches to avoid bottlenecks

Non-expert version control users are sometimes a bit afraid of branching and merging. This is probably a side effect of CVS's popularity: CVS's interface for branching and merging is somewhat counterintuitive, so many people have learned to avoid those operations entirely.

If you are among those people, resolve right now to conquer any fears you may have and take the time to learn how to do branching and merging. They are not difficult operations, once you get used to them, and they become increasingly important as a project acquires more developers.

Branches are valuable because they turn a scarce resource—working room in the project's code—into an abundant one. Normally, all developers work together in the same sandbox, constructing the same castle. When someone wants to add a new drawbridge, but can't convince everyone else that it would be an improvement, branching makes it possible for her to go to an isolated corner and try it out. If the effort succeeds, she can invite the other developers to examine the result. If everyone agrees that the result is good, they can tell the version control system to move ("merge") the drawbridge from the branch castle over to the main castle.

It's easy to see how this ability helps collaborative development. People need the freedom to try new things without feeling like they're interfering with others' work. Equally importantly, there are times when code needs to be isolated from the usual development churn, in order to get a bug fixed or a release stabilized (see „Stabilizing a Release” and „Maintaining Multiple Release Lines” in Rozdział 7, Packaging, Releasing, and Daily Development) without worrying about tracking a moving target.

Use branches liberally, and encourage others to use them. But also make sure that a given branch is only active for exactly as long as needed. Every active branch is a slight drain on the community's attention. Even those who are not working in a branch still maintain a peripheral awareness of what's going on in it. Such awareness is desirable, of course, and commit emails should be sent out for branch commits just as for any other commit. But branches should not become a mechanism for dividing the development community. With rare exceptions, the eventual goal of most branches should be to merge their changes back into the main line and disappear.

Singularity of information

Merging has an important corollary: never commit the same change twice. That is, a given change should enter the version control system exactly once. The revision (or set of revisions) in which the change entered is its unique identifier from then on. If it needs to be applied to branches other than the one on which it entered, then it should be merged from its original entry point to those other destinations—as opposed to committing a textually identical change, which would have the same effect in the code, but would make accurate bookkeeping and release management impossible.

The practical effects of this advice differ from one version control system to another. In some systems, merges are special events, fundamentally distinct from commits, and carry their own metadata with them. In others, the results of merges are committed the same way other changes are committed, so the primary means of distinguishing a "merge commit" from a "new change commit" is in the log message. In a merge's log message, don't repeat the log message of the original change. Instead, just indicate that this is a merge, and give the identifying revision of the original change, with at most a one-sentence summary of its effect. If someone wants to see the full log message, she should consult the original revision.

The reason it's important to avoid repeating the log message is that log messages are sometimes edited after they've been committed. If a change's log message were repeated at each merge destination, then even if someone edited the original message, she'd still leave all the repeats uncorrected—which would only cause confusion down the road.

The same principle applies to reverting a change. If a change is withdrawn from the code, then the log message for the reversion should merely state that some specific revision(s) is being reverted, not describe the actual code change that results from the reversion, since the semantics of the change can be derived by reading the original log message and change. Of course, the reversion's log message should also state the reason why the change is being reverted, but it should not duplicate anything from the original change's log message. If possible, go back and edit the original change's log message to point out that it was reverted.

All of the above implies that you should use a consistent syntax for referring to revisions. This is helpful not only in log messages, but in emails, the bug tracker, and elsewhere. If you're using CVS, I suggest "path/to/file/in/project/tree:REV", where REV is a CVS revision number such as "1.76". If you're using Subversion, the standard syntax for revision 1729 is "r1729" (file paths are not needed because Subversion uses global revision numbers). In other systems, there is usually a standard syntax for expressing the changeset name. Whatever the appropriate syntax is for your system, encourage people to use it when referring to changes. Consistent expression of change names makes project bookkeeping much easier (as we will see in Rozdział 6, Communications and Rozdział 7, Packaging, Releasing, and Daily Development), and since a lot of the bookkeeping will be done by volunteers, it needs to be as easy as possible.

See also „Releases and Daily Development” in Rozdział 7, Packaging, Releasing, and Daily Development.

Authorization

Most version control systems offer a feature whereby certain people can be allowed or disallowed from committing in specific sub-areas of the repository. Following the principle that when handed a hammer, people start looking around for nails, many projects use this feature with abandon, carefully granting people access to just those areas where they have been approved to commit, and making sure they can't commit anywhere else. (See „Prawo zapisu” in Rozdział 8, Zarządzanie ochotnikami for how projects decide who can commit where.)

There is probably little harm done by exercising such tight control, but a more relaxed policy is fine too. Some projects simply use an honor system: when a person is granted commit access, even for a sub-area of the repository, what they actually receive is a password that allows them to commit anywhere in the project. They're just asked to keep their commits in their area. Remember that there is no real risk here: in an active project, all commits are reviewed anyway. If someone commits where they're not supposed to, others will notice it and say something. If a change needs to be undone, that's simple enough—everything's under version control anyway, so just revert.

There are several advantages to the relaxed approach. First, as developers expand into other areas (which they usually will if they stay with the project), there is no administrative overhead to granting them wider privileges. Once the decision is made, the person can just start committing in the new area right away.

Second, expansion can be done in a more fine-grained manner. Generally, a committer in area X who wants to expand to area Y will start posting patches against Y and asking for review. If someone who already has commit access to area Y sees such a patch and approves of it, they can just tell the submitter to commit the change directly (mentioning the reviewer/approver's name in the log message, of course). That way, the commit will come from the person who actually wrote the change, which is preferable from both an information management standpoint and from a crediting standpoint.

Last, and perhaps most important, using the honor system encourages an atmosphere of trust and mutual respect. Giving someone commit access to a subdomain is a statement about their technical preparedness—it says: "We see you have expertise to make commits in a certain domain, so go for it." But imposing strict authorization controls says: "Not only are we asserting a limit on your expertise, we're also a bit suspicious about your intentions." That's not the sort of statement you want to make if you can avoid it. Bringing someone into the project as a committer is an opportunity to initiate them into a circle of mutual trust. A good way to do that is to give them more power than they're supposed to use, then inform them that it's up to them to stay within the stated limits.

The Subversion project has operated on the honor system way for more than four years, with 33 full and 43 partial committers as of this writing. The only distinction the system actually enforces is between committers and non-committers; further subdivisions are maintained solely by humans. Yet we've never had a problem with someone deliberately committing outside their domain. Once or twice there's been an innocent misunderstanding about the extent of someone's commit privileges, but it's always been resolved quickly and amiably.

Obviously, in situations where self-policing is impractical, you must rely on hard authorization controls. But such situations are rare. Even when there are millions of lines of code and hundreds or thousands of developers, a commit to any given code module should still be reviewed by those who work on that module, and they can recognize if someone committed there who wasn't supposed to. If regular commit review isn't happening, then the project has bigger problems to deal with than the authorization system anyway.

In summary, don't spend too much time fiddling with the version control authorization system, unless you have a specific reason to. It usually won't bring much tangible benefit, and there are advantages to relying on human controls instead.

None of this should be taken to mean that the restrictions themselves are unimportant, of course. It would be bad for a project to encourage people to commit in areas where they're not qualified. Furthermore, in many projects, full (unrestricted) commit access has a special status: it implies voting rights on project-wide questions. This political aspect of commit access is discussed more in „Who Votes?” in Rozdział 4, Social and Political Infrastructure.



[18] Aby poznać inną opinię na temat wersjonowania plików configure, przeczytaj wpis na blogu Alexey'a Makhotkina zatytułowany "configure.in and version control", dostępny pod adresem http://versioncontrolblog.com/2007/01/08/configurein-and-version-control/.