Visualizzazione post con etichetta svn. Mostra tutti i post
Visualizzazione post con etichetta svn. Mostra tutti i post

lunedì 14 febbraio 2011

SVN: recuperare dalle versioni precedenti i file rimossi

A volte capita che in un sistema centralizzato come SVN venga "perso" un file, cancellato/rinominato da uno dei committers senza che gli altri se ne rendano conto. Passando il tempo, e accumulandosi le revisioni, diventa sempre piu' difficile trovare un file e visualizzare tutte le modifiche che lo riguardano. Ora, il problema di fatto è uno di quelli che mi ha spinto a lavorare su sistemi decentralizzati (git principalmente e mercurial per alcuni progetti), ma questa è un'altra storia...
Tornando ad SVN, come è possibile risalire all'ultima versione del repository che contiene un determinato file? Ebbene il comando da eseguire è semplice:

svn log -vq | tr -d '\n'| sed -r 's/-{3,}/\n/

Il comando di cui sopra visualizza i numeri di revisione e i percorsi modificati (opzioni -vq) senza il contenuto del testo di messaggio stesso, dopodiché rimuove tutti i fine linea e nuovamente li aggiunge ogni volta che trova una sequenza di piu' di due trattini (le linee di separazione usate nell'output del comando log stesso). I trattini rimossi sono da 3 in su per evitare di essere confusi da firme di mail (solitamente due trattini) o simili, volendo si potrebbe anche aumentare tale valore.
Ebbene tale comando, su un semplice repository di prova, produce un output simile al seguente:

r4 | luca | 2011-02-03 11:46:09 +0000 (Thu, 03 Feb 2011)Changed paths:   A /miao.txt
r3 | luca | 2011-02-03 11:44:09 +0000 (Thu, 03 Feb 2011)Changed paths:   D /ciao.txt
r2 | luca | 2011-02-03 11:43:54 +0000 (Thu, 03 Feb 2011)Changed paths:   M /ciao.txt
r1 | luca | 2011-02-03 11:43:38 +0000 (Thu, 03 Feb 2011)Changed paths:   A /ciao.txt


A questo punto si supponga di voler cercare il file ciao.txt, che esisteva nel repository e che poi è stato cancellato. Diventa immediato eseguire:

svn log -vq | tr -d '\n'| sed -r 's/-{3,}/\n/ | grep ciao.txt

e ottenere di conseguenza:

r3 | luca | 2011-02-03 11:44:09 +0000 (Thu, 03 Feb 2011)Changed paths:   D /ciao.txt
r2 | luca | 2011-02-03 11:43:54 +0000 (Thu, 03 Feb 2011)Changed paths:   M /ciao.txt
r1 | luca | 2011-02-03 11:43:38 +0000 (Thu, 03 Feb 2011)Changed paths:   A /ciao.txt


che corrisponde alla storia del file stesso, con in cima alla lista la cancellazione (D - delete) del file alla revisione r3. E' quindi possibile recuperare il file con:

svn update -r 2 ciao.txt

Da notare che la revisione dalla quale si fa il checkout del file è una in meno rispetto a quella della cancellazione, poiché se si eseguisse l'update alla versione della cancellazione il file risulterebbe ancora cancellato.
In realtà così facendo non si ha la possibilità di reinserire il file nel repository, a meno che non si usino trucchi di ridenominazione a aggiunta. Se quello che si vuole è annullare la cancellazione della revione si deve procedere come segue:

svn merge -c -3  file:///home/luca/tmp/test/

Questa volta il numero di revisione specificato è quello della revisione di cancellazione. Il segno negativo davanti al numero forza il sistema a confrontare le revisioni x e x-1, quindi la 3 e la 2 e ad annullare le modifiche della revisione 3. Occorre specificare il percorso del file del repository dal quale estrarre le versioni, poiché specificando il file stesso si otterrà un errore di file non sotto controllo di versione. Il sistema risponderà con quello che chiama "reverse merge" e il file ricomparirà nella versione corrente pronto per essere nuovamente committato:


--- Reverse-merging r3 into '.':
A    ciao.txt

mercoledì 19 novembre 2008

Importare un progetto SVN in Eclipse gestendolo con Git

Al fine di meglio apprendere Git, il sistema di gestione dei sorgenti appositamente sviluppato per il Kernel di Linux (ma usato anche in altri progetti), ho deciso di convertire un progetto Java che gestisco con un repository SVN in un repository Git.
Su questo progetto lavoro pressocché da solo, usando Eclipse e i tools SVN sviluppati dal gruppo di SVN stesso, quindi non dovrei molti problemi nella migrazione. Per sicurezza ho deciso di mantenere per alcuni giorni entrambi i progetti in Eclipse, così da poter tornare abbastanza semplicemente al sistema SVN in caso di problemi.
Di seguito i passi necessari all'installazione di EGit, il plug-in per Eclipse/Git e per l'importazione del progetto.

Installazione del plug-in per Eclipse: EGit
L'installazione del plugin non è un'operazione banale, poiché occorre importare i vari sotto progetti in Eclipse e compilarli a mano. In particolare occorre seguire i passi:
  • scaricare i progetti di EGit in una directory temporanea con il comando git clone git://repo.or.cz/egit.git
  • importare i progetti in Eclipse. Per fare questo selezionare da Eclipse il menù File->Import->existing projects into workspace e verificare che i progetti compaiano nel project explorer.

  • ora occorre compilare i progetti, per fare questo occorre darre il classi Build-All dal menù Project. Questo passo è opzionale se avete abilitato il build automatico dei progetti.
  • occorre ora esportare un plug-in compatibile per Eclipse, quindi dal menù File->Export si seleziona la voce Plug-in and fragments. E' necessario impostare come percorso la root dell'installazione corrente di Eclipse.


  • a questo punto è possibile riavviare Eclipse, cancellare la directory temporanea dove si sono scaricati i sorgenti di EGit, rimuovere i progetti dal project manager e verificare che il plugin sia installato. In particolare per questo è sufficiente controllare che nell'import ci sia una voce Git.

Conversione di un progetto SVN in repository Git
Nel mio caso il progetto SVN si chiama HRPM e risiede su una macchina remota. Dopo aver installato sulla mia macchina tutti i tool git, fra i quali git-svn, posso importare il progetto SVN in un repository locale git con la seguente procedura:
  • creare una directory dove inserire il repository, entrare nella directory e inizializzare il repository git: git-svn init svn+ssh://host/sviluppo/svn//hrpm
  • importare i sorgenti con il comando git-svn fetch -r900 (dove 900 rappresenta il numero di revisione a cui il repository SVN è arrivato)
  • dopo qualche secondo (o qualche minuto), il repository git sarà pronto al suo utilizzo. E' ora possibile usare questo repository o importarlo in Eclipse.
  • (opzionale) siccome il progetto importato nel repository contiene lo stesso nome del suo fratello su SVN, qualora si vogliano mantenere entrambi i progetti è necessario editare il file .project che si trova nella directory dei sorgenti cambiandone il nome, come ad esempio in HRPM-git.
Importare il progetto in Eclipse
Avendo ora il progetto su un repository Git è possibile importare il progetto all'interno di Eclipse:

  • dal menù File->Import->Git selezionare la voce Git-Repository;
  • selezionare nella dialog che si apre la voce file e specificare il percorso dove risiede il repository git;

  • la dialog successiva chiede quale branch si vuole importare. Siccome il repository è appena stato creato, il branch master è quello selezionato per default.

  • a questo punto il progetto è importato, ma non ancora visibile in Eclipse: esiste la cartella corrispondente nel workspace, ma non il progetto nel project explorer. Per far comparire il progetto, è necessario procedere ad una successiva importazione di un progetto esistente: File->Import->Existing Project into Workspace come mostrato di seguito

  • si specifica la directory che contiene il progetto (ATTENZIONE: questa volta si deve specificare la directory all'interno del workspace) e si vedrà comparire il progetto fra quelli selezionabili. Qualora esista già un progetto con lo stesso nome Eclipse non mostrerà nessun progetto selezionabile.

  • e il progetto verrà visualizzato nel project explorer. Non è ancora finita, perché il progetto è locale, e non verrà gestito da Git. Occorre quindi fare click destro sul progetto, selezionare Team->Share Project e usare Git come metodo di condivisione.

  • da ultimo si informa Eclipse di usare un repository esistente, e il gioco è fatto.

La cosa interessante è che il procedimento ha funzionato perfettamente nonostante il mio progetto fosse non un semplice progetto Java, bensì un progetto AspectJ. L'unica pecca del plug-in, almeno ad una prima vista, è l'utilizzo dei caratteri in stile CVS per indicare che il contenuto necessita di commit/aggiornamenti, mentre l'uso delle icone del plug-in SVN è sicuramente più gradevole.

Link utili:
Eclipse Git Plugin Installation
A dummy guide to use Egit

martedì 16 settembre 2008

Pubblicazione di un progetto Web gestito tramite SVN

SVN è un ottimo software per il versioning dei sorgenti di qualunque tipo. Quando si utilizza però per progetti Web, può essere necessario avere un qualche tipo di deploy automatico. In altre parole si vuole che il ramo dei sorgenti resi pubblici dal server Web sia automaticamente aggiornato ad ogni commit sul repository SVN. La FAQ di SVN presenta già una soluzione al problema, ma io ne ho messa a punto una leggermente differente che non fa uso di programmi compilati e di suid, ma semplicemente di script shell e di sudo.
L'idea è quella di creare un post-commit-hook, ossia un comando da eseguire a seguito di un commit, che effettui l'aggiornamento dell'albero dei sorgenti web. Nello specifico andrà a fare un update dell'albero dei sorgenti web prendendo solo gli ultimi file modificati in fase di commit (ovviamente si ipotizza che il server web sia locale al repository SVN). Il file post-commit della directory hooks del repository SVN deve quindi contenere il seguente codice shell:

REPOSITORY=$1
REVISION=$2

GREP_CMD=`which grep`
SED_CMD=`which sed`
ECHO_CMD=`which echo`
SU_CMD=`which su`
WWW_USER="www-data"
SUDO_CMD=`which sudo`
DEPLOY_CMD="/usr/local/bin/svnWeb.sh"
SVNLOOK_CMD=`which svnlook`
TMP_FILE=/tmp/svn.changed
WEB_REPOSITORY="myProject"

# controllo se il repository include la parte web
$ECHO_CMD $REPOSITORY | $GREP_CMD $WEB_REPOSITORY > /dev/null 2>&1
if [ $? -eq 0 ]
then
# e' il repository web, faccio la pubblicazione automatica

echo "Pubblicazione web automatica " > $TMP_FILE
echo "per il repository $WEB_REPOSITORY " >> $TMP_FILE
echo " (vedere il file post-commit del server svn) " >> $TMP_FILE
echo "\nSeguono i dettagli dell'aggiornamento web\n" >> $TMP_FILE

# mi faccio dire i file che sono cambiati
$SVNLOOK_CMD changed $REPOSITORY | $AWK_CMD '{print $2;}' >> $TMP_FILE

# faccio la pubblicazione diventando un utente apposito tramite
# il comando su, invocato a sua volta tramite sudo. E' importante
# che l'utente con il quale si esegue il comando sudo abbia
# una impostazione di /etc/sudoers che non richiede password, altrimenti
# questo script non verra' eseguito correttamente.
$SUDO_CMD $SU_CMD -c $DEPLOY_CMD $WWW_USER >> $TMP_FILE

fi

Come si può notare, oltre all'inizializzazione di alcune variabili (si presti attenzione poiché post-commit potrebbe eseguire con un PATH modificato o nullo) si effettua un primo controllo sul tipo di commit, affinché si riferisca al repository/progetto di cui si vuole fare il deployment. Dopodiché si scrive un semplice file di log per tenere traccia delle ultime modifiche effettuate (questo può essere utile da inviare come allegato in una e-mail di notifica del commit). Infine, si effettua un cambio utente (tramite su) all'utente cui appartiene la directory del server web e si esegue il comando di deploy (DEPLOY_CMD). Tutto questo viene fatto tramite sudo, per evitare che venga richiesta una password per diventare l'utente del server web. Infatti, se nel file di configurazione di sudo (/etc/sudoers) l'utente che effettua il commit è configurato perché non venga richiesta nessuna password, allora egli può diventare prima superutente (con sudo) e da li un utente qualunque (tramite su) senza che nessuna password sia richiesta.
Il comando di deploy (DEPLOY_CMD) è uno script che effettua l'update tramite SVN e sistema i permessi della directory del server Web:

#!/bin/bash

SRC_DIR=/var/www/myProject
SVN_REPOSITORY=file:///sviluppo/svn/myProject/trunk
SVN_CMD=`which svn`
CHMOD_CMD=`which chmod`
CHOWN_CMD=`which chown`
WWW_USER="www-data"
WWW_GROUP="www-data"



# entro nella directory in cui aggiornare l'albero dei sorgenti
cd $SRC_DIR || exit 1

# effettuo l'aggiornamento dei sorgenti
echo "Aggiornamento dell'albero dei sorgenti..."

$SVN_CMD update $SVN_REPOSITORY `pwd`
echo "fatto <$?>"

# cambio i permessi dei file copiati
$CHOWN_CMD $WWW_USER:$WWWGROUP `pwd` -Rf
$CHMOD_CMD 755 `pwd` -Rf

L'utilizzo di un post-commit hook per la pubblicazione del server Web ha comunque alcuni difetti:
  • occorre più tempo per ogni commit, poiché il server Web deve essere aggiornato;
  • per testare le modifiche occorre fare dei commit, e quindi si va a fare il commit di codice non proprio stabile. A tal fine è bene inserire uno schema di branching o tagging.

mercoledì 20 febbraio 2008

SVN: notifica via e-mail dei commit

Come già anticipato in un precedente articolo SVN consente di personalizzare le fasi pre e post commit mediante opportuni hooks. E' quindi possibile creare uno script che, nella fase successiva al commit (post-commit) effettui l'invio per e-mail delle informazioni di commit stesse.

Esistono diversi modi (e utility) per inviare per e-mail informazioni su un commit, ma uno semplice ed efficace è svnnotify (pacchetto ubuntu libsvn-notify-perl), uno script perl che consulta il repository SVN per reperire le informazioni da mandare via e-mail.

E' sufficiente creare uno script denominato post-commit nella directory hooks del repository SVN contenente i seguenti comandi:

EPOS="$1"
REV="$2"

# commands
SVNLOOK=`which svnlook`
SVNNOTIFY=`which svnnotify`
GREP=`which grep`

# from and to e-mail address, substitue with something appropriate
MAIL_TO="commit@myDomain.com"
MAIL_FROM=$MAIL_TO

# check projects to send e-mail from
MYREPO=javaRepo # the name of the repo, substitute with something appropriate
$SVNLOOK dirs-changed "$REPOS" | $GREP $MYREPO > /dev/null

# is this a project I'd like to send an-email for?
if [ $? -eq 0 ]
then
$SVNNOTIFY --repos-path "$REPOS" --revision "$REV" --svnlook "$SVNLOOK" --to "$MAIL_TO" --from "$MAIL_FROM" --attach-diff --subject-cx -i 50 -P "[commit]"
fi

Si noti che viene definito l'indirizzo di posta elettronica al quale inviare il resoconto, questo potrebbe essere una mailing list (tipicamente -commit) o un alias di posta per gli sviluppatori. Si noti inoltre che viene fatta una selezione sul repository per il quale inviare delle e-mail, tale controllo può essere omesso se si vuole ricevere notifica per ogni repository, o può essere esteso per includere altri repository.

Il comando svnnotify viene utilizzato cone le informazioni precedentemente definite, ed in particolare:
  • attach-diff che manda per allegato il diff relativo al nuovo commit;
  • subject-cx imposta il subject dell'e-mail in base al contesto (ad esempio viene visualizzato il percorso dei file modificati). Si noti l'utilizzo del flag -i per limitare la lunghezza della linea di soggetto;
  • -P per impostare un prefisso della linea di soggetto, così da facilitare il riconoscimento a colpo d'occhio (e mediante filtri automatici) del contenuto dell'e-mail stessa.

SVN: impedire commit senza commento

SVN presenta una serie di hooks per gestire le fasi di pre and post commit, cosa che risulta molto comoda ad esempio per verificare determinati requisiti prima del commit vero e proprio o fornire segnalazioni (ad es. e-mail) a commit avvenuto.

Per impedire che gli utenti inseriscano dei commit senza commento (o con commento troppo corto) è possibile inserire il seguente controllo nel file hooks/pre-commit:

REPOS="$1"
TXN="$2"

SVNLOOK=`which svnlook`

# check how many characters the user has placed in the comment
MESSAGE_TEXT=`$SVNLOOK log -t "$TXN" "$REPOS" `
COMMENT_LENGTH=`echo $MESSAGE_TEXT | grep "[a-zA-Z0-9]" | wc -c `
MIN_COMMENT_LENGHT_ALLOWED=12

if [ $COMMENT_LENGTH -le $MIN_COMMENT_LENGHT_ALLOWED ]
then
echo "*** ATTENZIONE ***" >&2
echo "Per ovvie ragioni non e' piu' possibile effettuare un commit con messaggio nullo o troppo corto." >&2
echo "Per favore, prenditi il tempo che occorre e scrivi un buon commento, servira' a comprendere meglio" >&2
echo "le modifiche che hai apportato." >&2
echo "Inoltre, se stai correggendo un errore/bug, includi alcune informazioni di test " >&2
echo "(ad esempio un lotto di test) che possano essere utili per controllare l'errore " >&2
echo "e per testare eventuali altre modifiche. " >&2
echo "" >&2
echo "Il testo da te inserito { $MESSAGE_TEXT } ha una lunghezza insufficiente: $COMMENT_LENGTH" >&2
echo "" >&2
exit 1
fi

Quello che avviene è piuttosto semplice:
  1. con svnlook si ottiene il messaggio di commit inserito dall'utente (si noti che il commit non è ancora stato confermato);
  2. si ottiene la lunghezza del messaggio di commit;
  3. se la lunghezza del messaggio è inferiore ad una lunghezza specificata, si provvede a notificare l'utente del problema e si termina in modo anormale lo script.
Si noti che l'avviso all'utente viene notificato attraverso lo standard error, e che lo script termina con un codice di errore non zero, ad indicare ad svn che il commit non puo' essere accettato. Per accettare il commit occorre infatti che lo script pre-commit termini con un codice di uscita pari a zero.