Musiałem utworzyć do firmy mały poradnik, jak korzystać z branchy i tagów w Subversion, a że pisałem to w miarę (jak mi się zdaje) łopatologicznie, to stwierdziłem że umieszczę to i tutaj dla szerszego grona :)

Nie jest to pozycja dla osoby nie mającej dotychczas styczności z Subversion, nie jest więc wytłumaczone co to jest commit i w ogóle Subversion czy też system kontroli wersji, są za to informacje jak ja i kilka osób na świecie widzą korzystanie z tak ważnych rzeczy jakimi są branche i tagi :)

Generalnie, podstawową strukturą katalogów jest układ:

/
|-trunk
|-branches
|-tags

W trunk przechowywana jest aktualna rozwojowa wersja systemu. Tutaj nanoszone są bieżące zmiany, poprawki etc. Branches służą do przechowywania oddzielnych gałęzi systemu, które niosą zbyt rewolucyjne zmiany, żeby umieszczać je w drzewku głównym, które musi być czasem gotowe do natychmiastowej synchronizacji z systemem głównym. W katalogu tags tworzone są aktualne snapshoty systemu produkcyjnego - każda wersja, która idzie na system produkcyjny, powinna najpierw zostać przeniesiona (zamrożona) do konkretnego taga, i dopiero później system powinien zostać zsynchronizowany.

Najpierw troszkę samej teorii:

Zarówno branche, jak i tagi, tworzone są poprzez skopiowanie dowolnej z gałęzi
rozwijanego systemu (może to być zarówno system trunk, jak i dowolna gałąź utworzona przez któregokolwiek użytkownika) poleceniem cp (copy):

svn cp svn://repo/trunk svn://repo/branches/moja_galaz

Powyższe polecenie tworzy nową gałąź o nazwie moja_galaz którą można dowolnie sobie rozwijać, testować, modyfikować według własnych potrzeb, a następnie, gdy przyjdzie odpowiedni czas/ochota, dołączyć do aktualnego systemu.

W podobny sposób tworzy się konkretne tagi (tagi zwyczajowo oznacza się wielkimi literami):

svn cp svn://repo/trunk svn://repo/tags/HOME_20071210_1

Utworzy to aktualny snapshot repozytorium w katalogu /tags/HOME_20071210_1 (ostatnia
cyfra oznacza rewizję w ciągu tego samego dnia - oczywiście schemat nazewnictwa jest umowny).

Po utworzeniu własnej gałęzi, powinniśmy się na nią przełączyć, ponieważ domyślnie ciągle pracujemy na poprzedniej wersji repozytorium. Do przełączania się między gałęziami służy polecenie sw (switch):

svn sw svn://repo/branches/moja_galaz

W tym momencie pracujemy już na naszej gałęzi, i wszelkie commity, zmiany etc będą się odnosić do naszej wersji.
Co dalej?

Podstawowe prace i rozwijanie systemu powinno odbywać się na gałęzi trunk, która w razie puszczania zmian na system produkcyjny, powinna zostać skopiowana do odpowiedniego tagu, a następnie zsynchronizowana. Pozostaje jeszcze jeden problem: jak połączyć zmiany dokonane na osobnej gałęzi z tym, co dzieje się w aktualnym drzewku?

Tutaj pomoże nam komenda merge. Załóżmy hipotetyczną sytuację:

W repozytorium, w gałęzi trunk, znajduje się plik index.php. Ma on zawartość:

<?php

include 'some_file.php';

?>

W tej chwili tworzymy osobnego brancha:

svn cp svn://repo/trunk svn://repo/branches/mysz1
svn sw svn://repo/branches/mysz1

Od tego momentu pracujemy już na branchu mysz1. Sprawdzić to możemy za pomocą
komendy info:

svn info
[...]
URL: svn://repo/branches/mysz1
[...]

Dokonujemy zmiany w pliku index.php, tak że zawiera on teraz:

<?php
include 'some_file.php';
echo date ('Ymdhis'); // branches/mysz1
?>

Wykonujemy commit pliku, po czym przenosimy się na trunka:

svn sw svn://repo/trunk
svn info
[...]
URL: svn://repo/trunk
[...]

Sprawdzamy zawartość pliku index.php:

svn cat index.php
<?php
include 'some_file.php';
?>

Dokonujemy zmian, żeby plik wyglądał tak:

<?php
include 'some_file.php';
print strftime ('%Y%m%d %H%M%S', time ()); // trunk
?>

Commit, i wracamy na mysz1:

svn sw svn://repo/branches/mysz1
svn info
[...]
URL: svn://repo/branches/mysz1
[...]
svn cat index.php
<?php
include 'some_file.php';
echo date ('Ymdhis'); // branches/mysz1
?>

No to teraz próbujemy nanieść zmiany z branches/mysz1 na główną gałąź systemu:

svn merge -r 6:HEAD svn://repo/branches/mysz1 .

(na końcu jest kropka).

Co znaczą poszczególne części, wyjaśniam poniżej:

svn merge
polecenie
-r 6:HEAD
wersje która mają zostać połączone: najpierw numer wersji na której aktualnie pracujemy (w tym wypadku 6), później wersji do której chcemy się uaktualnić (HEAD, oznaczający najbardziej aktualną wersję, ale może to również być liczba oznaczająca konkretną rewizję)
svn://repo/branches/mysz1
branch który łączymy z trunkiem
.
(kropka) aktualna ścieżka (równie dobrze mogło to być svn://repo/trunk)

W tym wypadku powstanie nam konflikt, który rozwiązujemy tradycyjnym sposobem (kasując, dodając i poprawiając konkretne linie), a na końcu, po rozwiązaniu wszystkich problemów, robimy tradycyjny commit.

Więcej informacji znaleźć można na:


Twórcy Blip!a wydali wersję 0.02 API, więc zrobiłem co w mojej mocy aby skończyć moją PeHaPową bilbiotekę do tegoż ;) Zmian dużo, do wersji API 0.01 była ona mocno testowa, i w sumie nie jest poprawnie zrobiona, są w niej błędy etc, których nie zamierzam już poprawiać. Wersja 0.02.4 niesie z sobą pełną obsługę protokołu, wykorzystując wszystko co producenci dali, szczegóły w oficjalnej dokumentacji Blip!a.

Szczegóły numeracji biblioteki: pierwszą część stanowi wersja API (w tym wyapdku 0.02), drugą - odsłona samej biblioteki (w tym wypadku 4).

BlipApi.php można używać na dwa sposoby:

  1. wywołując metodę BlipApi::execute(), gdzie pierwszym parametrem jest nazwa komendy do wykonania (spis komend w oficjalnej dokumentacji, jedyną różnicą jest dirmsg zamiast directed_messages), a następnie dostępne parametry metody (szczegóły w pliku klasy i w oficjalnej dokumentacji Blip!a), np.:

    $bapi = new BlipApi ('login', 'haslo');
    $bapi->connect ();
    $bapi->execute ('update_read', null, 'mysz');

    Pobierze ostatnie 10 statusów użytkownika mysz.

  2. wywołując komendę jako konkretną metodą obiektu BlipApi:

    $bapi->update_read (null, 'mysz');

    Powyższe dwa wywołania są sobie równoważne.

Jeśli ktoś znajdzie jakieś błędy, lub ma uwagi co do samej biblioteki, proszę o komentarze tutaj, lub kontakt mailowy: urzenia.net/email. Z góry dziękuję za feedback ;)

Bibliotekę można pobrać/obejrzeć z: repo.urzenia.net/files/blipapi-0.02.phps. W tej chwili nie ma skąd pobrać samej biblioteki, jako że usunąłem repo.urzenia.net. Dołączona jest za to do pakietu WP Blip!, skąd można ją “ręcznie” wyciągnąć :) Przepraszam za kłopot :)


Od kilku tygodni bawię się po trochu CURLem, w ramach tworzenia PeHaPowej biblioteki do Blip!a. Poniżej kilka zagwozdek, na które ciężko znaleźć sensowną odpowiedź w google, albo ja nie wiem jak pytać…

  1. Jak poprawnie wykonać zapytanie PUT? (aka: metoda PUT i “select/poll returned error”)

    Nad tym spędziłem najwięcej czasu. Kombinowałem na mnóstwo sposobów. Kluczem do sukcesu okazało się podejrzenie wersji konsolowej CURLa z dokładnie takim samym zapytaniem, jakie wysyłałem za pomocą skryptu PHP. W skrócie: należy wymusić na CURLu HTTP 1.0, za pomocą ustawienia opcji:

    curl_setopt ($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); # gdzie $ch jest obiektem utworzonym za pomocą curl_init ()

    Po tej operacji zapytania śmigają jak należy (dodam tylko, że czasem zamiast “select/poll returned error” dostawałem “connection reset by peer”).

  2. Przy rozwiązywaniu powyższego problemu, pomogła mi mała sztuczka. Mianowicie musiałem podejrzeć jakie nagłówki wysyła CURL. Normalnie PHPowy CURL nie zamierza pokazać co jest wysyłane i odbierane (Rozpisałem się - oczywiście nagłówki odbierane bez problemu można zobaczyć. Dzięki, Arek ;) ), bo i po co… Ja posłużyłem się tutaj specjalnie na tą okazję utworzonym skryptem PHP z dokładnie wyizolowanym ‘testcase’. Tutaj, po zaimplementowaniu opcji:

    curl_setopt ($ch, CURLOPT_VERBOSE, 1);

    i odpaleniu skryptu z konsoli, grzecznie została mi pokazana komunikacja wychodząca z serwerem, co było kluczem do rozwiązania problemu (okazało się że był wysyłany dodatkowy nagłówek Expect należący do HTTP 1.1, a po wrzuceniu tego w google znalazłem informacje które mnie nakierowały na właściwy trop).

  3. Jak wysłać plik POSTem?

    Ech, w sumie rozwiązanie jest banalne, jeśli ktoś dokładniej przeczyta manual na stronach PHP dotyczący CURLa. Ale skoro już opisuję zagwozdki…

    PHPowy CURL pozwala wysyłać dane na dwa sposoby (akceptowane i sprecyzowane, oczywiście, w odpowiednim RFC). Jeden to x-www-form-urlencoded (tak są wysyłane dane za pomocą webowych formularzy), drugi to multi-part form-data (tak są wysyłane pliki, także z formularzy). Normalnie dla opcji CURLOPT_POSTFIELDS podaje się string który wygląda dokładnie tak samo jak ten widoczny w URLach skryptów pehapowych (mówiąc po laicku), np. a=1&b=2 - wtedy dane są wysyłane metodą x-www-form-urlencoded. Aby zmusić CURLa do wysłania danych metodą multi-part form-data, jako parametr opcji CURLOPT_POSTFIELDS musimy podać tablicę. Tak, właśnie tablicę, gdzie kluczem powinne być nazwa pola (odpowiadająca temu z prawdziwego formularza), a wartością ścieżka do pliku, ale uwaga: ścieżka powinna być poprzedzona znakiem “małpy”: @. Czyli powinno wyglądać to tak:

    curl_setopt ($ch, CURLOPT_POSTFIELDS, array ('pole' => '@/sciezka/do/pliku.jpg'));

    Po tej operacji wysłanie pliku nie powinno być już problemem :)