Home / Community / Blog

I've accidentally commit corrupted file (long story... ;-) into subversion repository, so I would like to revert to a previous version - but only this one particular file.

My file (l6208.lib) had been corrupted in version 309 (revision 308 was the last good one), so I type:

svn merge -c -309 l6208.lib

please note minus sign before 309. This mean apply differences between 309 and 308. You can also type:

svn merge -c -r 309:308 l6208.lib

Now it's time to commit recovered file.

svn ci l6208.lib -m "Reverted last revision"

I don't know if it is best method, but it works for me.

Miałem ciekawy przypadek błędu w repozytorium. Od razu powiem, że bez kopi zapasowej nie udało by mi się go rozwiązać.

Kopia robocza na jednym komputerze - wszystko działa (latami). Po jakimś czasie zachciało mi się (akurat - po prostu musiałem) w nim grzebnąć, ale już na nowym sprzęcie i ... klops. Okazało się, że repozytorium jest uszkodzone, co gorsza, jak się później okazało, już na wersji 2 (błąd dysku ???).

Komunikaty błędu miałem różne. Komenda svn pokazywała:

svn: REPORT of '/svnprv/reponame/!svn/vcc/default': Could not read \
 chunk size: connection was closed by server (http://localhost)

w error_log apache'a było:

[error] [client 127.0.0.1] Provider encountered an error while \
 streaming a REPORT response.  [500, #0]
[error] [client 127.0.0.1] A failure occurred while driving \
 the update report editor  [500, #160004]

a polecenie svnadmin verify reponame zwracało:

svnadmin: Checksum mismatch while reading representation:
   expected:  10b6f87d95a8283b4dc96607a662c4de
     actual:  1865015a5837bc21ea73397ee68e1c44

Po przejrzeniu backup'ów (makaronizm, ale "kopia zapasowa" jest taka długa...) znalazłem taki który miał poprawnie zapisaną wersję drugą - z przed 2 lat! W ten sposób posunąłem się o 400% (z wersji 1 do 4 ;-) do pełni szczęścia trochę brakuje...

Niby coś-tam działało, bo WebSVN ładnie pokazywał historie i doszedłem do tego, że mogę ściągać diff'y między różnymi wersjami, np.:

svn di http://localhost/svn/reponame@7 \
       http://localhost/svn/reponame@15

W ten sposób dowiedziałem się, że tylko wersja 2 jest uszkodzona. Niestety próby odzyskania reszty danych z bieżącego repozytorium nie dawały pozytywnych rezultatów (testowałem na różnych wersjach):

svnadmin dump -r3 reponame
svnadmin: Checksum mismatch while reading representation:
   expected:  959148e046aafa5f51db215c9ee0796d
     actual:  98824bfc23034f920d603d42a0810843

Oczywiście działało dla wersji 0 i 1. Kluczem okazał się przełącznik --incremental !

svnadmin dump -r3 --incremental reponame
* Dumped revision 3.

Teraz już poszło gładko. Trzeba założyć nowe repozytorium i załadować do niego dane z kopii (była spakowana bzip2):

svnadmin create reponame-new
bzcat reponame-2011-09-25.dump.bz2 | svnadmin load reponame-new
...
------- Committed revision 4 >>>

ustalić numer ostatniej wersji, powiedzmy 45 (żeby nie było równo) i wtedy (jak widać wersję 4 już miałem):

for x in `seq 5 45` ; do
    svnadmin dump --incremental -r $x reponame \
   | svnadmin load reponame-new;
done

... no trochę to trwało, ale za to pełen sukces.

Teraz wystarczy skasować stare i zmienić nazwę "nowego":

rm -rf reponam
mv reponame-new reponame

Ładowane dane były do pustego repozytorium, więc UUID się nie zmienił.

Teraz trzeba poprawić skrypt, żeby po pierwsze wychwytywał błędy przy robieniu zrzutu (dump) i może dodatkowo wykonywał jeszcze zrzut ostatnich np. 10 wersji...

Przetestowałem zabezpieczenie przed przypadkowy zrobieniem commit'a do repozytorium bez podania numeru zgłoszenia (ticket). Zrobiłem to za pomocą hook'a pre-commit. Wszystkie polecenia podaję względem katalogu repozytorium, u mnie jest to /var/lib/svn/test. Skopiowałem szablon skryptu (parametr -p jest ważny, bo inaczej można popsuć uprawnienia)

cp -p hooks/pre-commit.tpl hooks/pre-commit
chmod u+x hooks/pre-commit

i wykomentowałem linię:

commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1

na koniec po innych testach, tuż przed linią

exit 0

dopisałem taki kawałek kodu

blad=0
$SVNLOOK log -t "$TXN" "$REPOS" \
| grep "#[1-9][0-9]*" > /dev/null || blad=1
if [ $blad -eq 1 ] ; then
    echo -e "Dodaj numer ticket'a,\nnp.: Zgloszenie #123" 1>&2
    exit 2
fi

który sprawdza czy w treści komentarza zawiera #N (gdzie N to dowolna liczba naturalna większa od 0) i zwraca komunikat błędu - użytkownik zobaczy to co wysyłane jest na wyjście błędu. Właściwy test to:

grep "#[1-9][0-9]*"

Dla Trac'a to wystarczy. Można go rozbudować, np. o sprawdzenie czy zawiera refs #N (N to znowu ciąg cyfr) - tak, żeby w Redmine ładnie pokazywał. Wtedy warunek będzie wyglądał tak (ja używam tylko refs):

grep "refs #[1-9][0-9]*"

Oczywiście można jeszcze dodać sprawdzanie pozostałych Referencing keywords oraz Fixing keywords.

Wypadało by też zmienić wyświetlany komunikat na np. Podaj numer zgłoszenia poprzedzony słowem refs, np:\nrefs #123, ale by wstawić tam znak końca wiersza (\n) trzeba dodać parametr -e do polecenia echo. Razem wygląda tak:

echo -e "Podaj numer zgłoszenia poprzedzony słowem refs, \
np:\nrefs #123" 1>&2

Mechanizm uchwytów (hook) do zdarzeń można wykorzystać dowolnie, także do bardziej zaawansowanych testów: np. by zabezpieczyć przed commit'em kodu który się nie da skompilować (chociaż ja bym tego nie robił) lub automatycznego budowania po commit'cie za pomocą post-commit (tego też bym nie robił).

Jak już postawiłem usługę katalogową, to chciałem użyć jej do autoryzacji do repozytoriów SVN do których był dostęp przez Apache.

W sumie jest to proste:

<IfModule mod_dav.c>
    <Location /svn>
        DAV svn
        SVNParentPath /var/lib/svn/repositories
        SVNListParentPath on
        AuthType Basic
        AuthName "Dostep do repozytorium"
        AuthBasicProvider ldap
        AuthLDAPURL "ldap://localhost/dc=jaqb,dc=gda,dc=pl" NONE
        AuthLDAPBindDN "cn=Manager,dc=jaqb,dc=gda,dc=pl"
        AuthLDAPBindPassword jakiesmocnehaslo
        AuthLDAPGroupAttribute memberUid
        AuthLDAPGroupAttributeIsDN off
        Require ldap-group cn=svnowcy,ou=groups,dc=jaqb,dc=gda,dc=pl
    </Location>
</IfModule>

kluczowe są dwa wpisy:

AuthLDAPGroupAttribut memberUid
AuthLDAPGroupAttributeIsDN off

bez tego Apache zakładał, że użytkownicy są wpisani jako member lub uniquemember i na dodatek jako np.:

uid=jaqb,ou=People,dc=jaqb,dc=gda,dc=pl

a domyślnie LAM zakłada jako np.: jaqb w memberUid.

To find files using their properties I've prepared script havesvnproperty.bash:

#!/bin/bash

if [ $# -lt 1 ] ; then
    echo "Check if file has value in property \
                               'myown:tags'."
    echo " "
    echo "No enouh parameters"
    echo " "
    echo "Example:"
    echo "$0 path/to/file some_tag"
    echo "find /home/repository -path '*/.svn' -prune -o \
                                -exec $0 \\{\\} some_tag \\;"
    exit 1
fi

filename=$1
propval=$2

tags=`svn propget myown:tags "$filename"`

ma=1
for x in $tags ; do
    if [ $x == $propval ] ; then
        ma=0
    fi
done

if [ $ma -eq 0 ] ; then
    echo "File $filename is tagged $propval"
fi

exit $ma

later you can search using find command, ie:

find /home/jaqb/myrepo -path '*/.svn' -prune -o \
                       -exec havesvnproperty.bash \{\} some_value \;

Zdarzyło mi się modyfikować wersjonowane pliki z dwóch komputerów i jak to zwykle bywa wersje SVN były na nich różne. Po modyfikacji nowszym klientem gdy chciałem znowu pracować na starszej wersji dostałem komunikat:

This client is too old to work with working copy

rozwiązanie (inne niż aktualizacja) jest bardzo proste i opisane w FAQ. Wystarczy użyć przygotowanego skryptu change-svn-wc-format.py który jako parametr przyjmuje ścieżkę kopii roboczej i wersję którą ma ustawić. W moim przypadku popsułem sobie katalog galerie i chciałem wrócić do wersji 1.5 więc wywołanie było takie:

./change-svn-wc-format.py galerie 1.5 --verbose

parametr --verbose dodałem bo lubię widzieć co się dzieje.

Po zainstalowaniu Eclipse na Ubuntu 11.04 nie mogłem się doszukać Subclipse. Okazało się, że do poprawnej współpracy z Subversion (repozytoria SVN) teraz trzeba zainstalować Subversive SVN Team Provider, Subversive SVN Connectors oraz SVNKit (np: 1.3.0).

Just my blog...

Mon Tue Wed Thu Fri Sat Sun
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31