Se siete abituati a sviluppare software, conoscerete sicuramente i vari strumenti di controllo della versione, RVS, CVS, SVN eccetera.
Un sistema di controllo della versione consente di lavorare in gruppo sullo stesso progetto, gestendo le versioni e mantenendo l'intero storico degli aggiornamenti.
Questi strumenti sono concepiti per programmatori e con scopi precisi, pertanto lavorano sulle modifiche ai sorgenti testuali e sono anche piuttosto difficili da gestire, visto che di solito si basano su strutture client/server, richiedono un setup eccetera.
Mi sono chiesto se è possibile implementare un sistema un po`più semplice che serva a scopi più generali.
Per esempio, se si mantiene ordinato il proprio lavoro, suddividendo i vari progetti in cartelle, aggiungendo documenti di varia natura, può essere interessante poter mantenere uno storico di ogni precedente situazione, gestire dei trunk o dei branch in modo intelligente e portabile.
Sia su MAC che nelle ultime versioni di Windows vi sono strumenti per recuperare vecchie versioni dello stesso documento e sono più che altro connesse al sistema di backup.
La mia idea però è più modesta, a me serve una specie di archiviatore, in grado di memorizzare in archivi standard, tutta la storia di una cartella, meglio se multi-piattaforma, in modo da poter trasferire facilmente l'intero storico copiandolo come un normale archivio, su una chiavetta o un disco.
Così ho scritto uno scriptino di bash che ho chiamato timerachive e che fa più o meno quello che ho detto.
Oltre che la bash, timearchive usa alcuni comandi tipici dei sistemi Unix like, oltre a tre componenti fondamentali, il tar, gzip e xdelta3.
Mentre dei primi due avrete probabilmente sentito parlare da qualche parte, xdelta3 è abbastanza sconosciuto ma in realtà è un moderno sistema di Patch binaria, ossia un programma in grado di eseguire la "delta encoding" cioè ricavare le differenze da due files binari A e B , e generare un file C, in modo che avendo B e C si possa riottenere A.
La caratteristica interessante di xdelta3 è quello di generare files in formato vcdiff che pur non essendo anch'esso molto noto, è uno standard descritto in rfc3284 .
Il comando timearchive
Lo script timearchive l'ho inserito in /usr/local/bin del mio sistema Linux , giusto perché è il posto più logico dove metterlo, così è a disposizione direttamente come ogni altro comando, lanciato da solo dà :
timearchive
Bad paramenter, please use U,T,R or X.
Bad paramenter, please use U,T,R or X.
e quindi ci presenta 4 diverse modalità d'uso :
- Update
- Test
- Rollback
- eXtract
Update
Immaginiamo di avere una cartella planning e di volerla archiviare con timearchive, possiamo lanciare il comando :
timearchive U planning.tar planning "Prima versione di test"
U è il comando, planning.tar, l'archivio da creare che è un tar appunto, planning è la cartella da memorizzare e poi segue una riga di testo per indicare il contenuto che ci può essere utile per fare i famosi branch o trunk. Con un tar tvf , vediamo il contenuto:
tar tvf planning.tar
-rw-r--r-- user/user 2020 2012-06-07 04:27 20120607042743.tar.gz
-rw-r--r-- user/user 23 2012-06-07 04:27 20120607042743.revision
Ebbene nel tar abbiamo due files, il primo è contenuto compresso che nel nostro caso è solo 2K (è una cartella molto piccola) e l'altra è la descrizione.
Entrambi i files sono registrati con quella strana sequenza di numeri, che in realtà è la data in formato ordinabile YYYYMMDDhhmmss e che è il nostro codice automatico di versione.
Interessante è aggiungere un piccolo file all'interno della cartella e rilanciare il comando identico :
timearchive U planning.tar planning "Aggiunto piccolo file"
tar tvf planning.tar
-rw-r--r-- user/user 23 2012-06-07 04:27 20120607042743.revision
-rw-r--r-- user/user 89 2012-06-07 04:32 20120607042743.vcdiff
-rw-r--r-- user/user 2193 2012-06-07 04:32 20120607043221.tar.gz
-rw-r--r-- user/user 22 2012-06-07 04:32 20120607043221.revision
In questo caso è partito xdelta3 che è stato applicato sulla versione decompressa (ma non scompattata) del file tar.gz e sul nuovo tar prodotto per l'occasione. Vediamo che il file che prima era tar.gz è divenuto vcdiff ed è diventato solo di 89 bytes, il vecchio tar.gz è stato rimosso ed è stato inserito al suo posto il nuovo contenuto.
Rimuovendo il file appena aggiunto ed eseguendo ancora lo stesso comando, eseguiamo lo stesso identico algoritmo dell'ultima volta e otteniamo :
timearchive U planning.tar planning "Tolto piccolo file"
tar tvf planning.tar
-rw-r--r-- user/user 23 2012-06-07 04:27 20120607042743.revision
-rw-r--r-- user/user 89 2012-06-07 04:32 20120607042743.vcdiff
-rw-r--r-- user/user 22 2012-06-07 04:32 20120607043221.revision
-rw-r--r-- user/user 58 2012-06-07 04:39 20120607043221.vcdiff
-rw-r--r-- user/user 2193 2012-06-07 04:39 20120607043911.tar.gz
-rw-r--r-- user/user 19 2012-06-07 04:39 20120607043911.revision
e così via.
Test
Il tar tvf è stato solo un sistema per farvi capire come funziona l'archiviazione e quanto pulito sia il risultato, tuttavia noi da quell'insieme di files inutili facciamo fatica a ricavarci qualcosa, per questo usiamo il semplicissimo comando di test che mette le cose sotto una luce completamente differente :
timearchive T planning.tar
20120607042743 Prima versione di test
20120607043221 Aggiunto piccolo file
20120607043911 Tolto piccolo file
Il codice della versione è quindi la data e in fianco la descrizione. Quel codice è comodo per il copia e incolla e lo useremo sia per eXtract che per Rollback.
eXtract
Vogliamo ricavare la versione a cui avevamo aggiunto quel famoso piccolo file, il comando di test ci dice che è la numero 20120607043221 , creiamo una cartella temporanea, giusto per non usare il . e quindi aggiungere i files alla planning corrente e diamo il comando :
timearchive X planning.tar temp 20120607043221
dove temp è ovviamente la cartella.
il risultato è che nella cartella temp, troveremo una cartella planning corredata anche del famoso piccolo file, nonostante l'avessimo cancellato, ma cosa è accaduto ?
Semplicemente è stato estratto il tar.gz , cioè 20120607043911.tar.gz e sono state applicate in sequenza tutte le patch fino ad arrivare a quella col codice corretto, risalendo quindi alla corretta versione. In questo l'unica patch era 20120607043221.vcdiff che ci ha riportato direttamente alla penultima versione, se fossimo stati alla decima versione e avessimo chiesto la seconda, sarebbero state applicate 8 patch.
In questo modo è possibile ripristinare qualsiasi documento intermedio.
Ovviamente l'archivio non è modificato durante l'estrazione, per farlo c'è Rollback.
Rollback
Rollback inizialmente non c'era e l'ho aggiunto in seguito perché in alcuni pacchetti avevo eseguito una serie di Update errati.Siccome eXtract, non modifica in alcun modo l'archivio, per tornare indietro si può usare Rollback, quindi se vogliamo tornare alla versione col file aggiunto possiamo applicare allo stesso modo di eXtract, il comando :
timearchive R planning.tar 20120607043221
Il risultato è un archivio così composto :
timearchive T planning.tar
20120607042743 Prima versione di test
20120607043221 Aggiunto piccolo file
In cui è scomparsa l'ultima versione.
Interessante è esaminarlo con tar tvf :
tar tvf planning.tar
-rw-r--r-- user/user 23 2012-06-07 04:27 20120607042743.revision
-rw-r--r-- user/user 89 2012-06-07 04:32 20120607042743.vcdiff
-rw-r--r-- user/user 22 2012-06-07 04:32 20120607043221.revision
-rw-r--r-- user/user 2174 2012-06-07 05:01 20120607043221.tar.gz
Il rollback lavora come l'extract cancellando di volta in volta i files e poi reinserisce il risultato patchato come ultimo update, sempre senza scompattare i tar ovviamente.
Lo script
Dopo avervi spiegato come funziona, e i comandi principali eccovi ovviamente l'intero script, se state usando un sistema tipo Posix, inseritelo in un file di testo di nome timearchive, e rendetelo eseguibile, altrimenti inventatevi qualcosa.Spero vi torni utile, divertitevi !
#!/bin/bash
function repatch
{
fil1=`tempfile`
fil2=`tempfile`
fil3=`tempfile`
while read l
do
t=${l#*.}
if [ "$t" != "revision" ]
then
if [ "$t" == "tar.gz" ]
then
tar xf $1 $l -O | gzip -d > $fil1
else
tar xf $1 $l -O >$fil2
xdelta3 -f -d -s $fil1 $fil2 $fil3
cp $fil3 $fil1
fi
tr=${l%%.*}
if [ "$tr" == "$3" ]
then
cd $2
tar xfps $fil1
break
fi
fi
done
rm $fil1
rm $fil2
rm $fil3
}
function rollback
{
fil1=`tempfile`
fil2=`tempfile`
fil3=`tempfile`
tmpdir=`mktemp -d`
while read l
do
t=${l#*.}
if [ "$t" != "revision" ]
then
if [ "$t" == "tar.gz" ]
then
tar xf $1 $l -O | gzip -d > $fil1
tar --delete --file $1 $l
else
tar xf $1 $l -O >$fil2
xdelta3 -f -d -s $fil1 $fil2 $fil3
cp $fil3 $fil1
tar --delete --file $1 $l
fi
tr=${l%%.*}
if [ "$tr" == "$3" ]
then
cd $tmpdir
gzip --best <$fil1 >$tr.tar.gz
tar rf $1 $tr.tar.gz
break
fi
else
tar --delete --file $1 $l
fi
done
cd $2
rm $tmpdir/*
rmdir $tmpdir
rm $fil1
rm $fil2
rm $fil3
}
function showcontent
{
while read l
do
if [ ${l##*\.} != "revision" ]
then
echo -n ${l%%\.*}" "
tar xf $1 ${l%%\.*}.revision -O
fi
done
}
function intcontent
{
while read l
do
if [ ${l##*\.} != "revision" ]
then
echo ${l%%\.*}
fi
done
}
case $1 in
U)
dirfull=$3
if [ "${dirfull:0:1}" != "/" ]
then
dirfull=`pwd`/$dirfull
fi
if [ ! -d $dirfull ]
then
echo "$3 must be an existing directory"
exit
fi
archfull=$2
if [ "${archfull:0:1}" != "/" ]
then
archfull=`pwd`/$archfull
fi
if [ -e $archfull ]
then
last=`tar -tf $archfull | grep "\.tar.gz"`
if [ -z "$last" ]
then
echo "The archive does not contain any tar.gz file"
exit
fi
# Creates Working Directory
tmpdir=`mktemp -d`
# Get Current Date
todate=`date +%Y%m%d%H%M%S`
tar xf $archfull $last -O | gzip -d >$tmpdir/oldimage
tar --delete --file $archfull $last
# Create the new archive
cd ${dirfull%/*}
tar cfps $tmpdir/$todate.tar ${dirfull##*/}
# Get Old DIFF name
olddiff=${last%%.tar.gz}.vcdiff
# Delta Analysis
cd $tmpdir
xdelta3 -S djw -s $todate.tar oldimage $olddiff
tar rf $archfull $olddiff
gzip --best $todate.tar
tar rf $archfull $todate.tar.gz
echo $4 >$tmpdir/$todate.revision
tar -C $tmpdir -rf $archfull $todate.revision
cd ..
rm $tmpdir/*
rmdir $tmpdir
else
# Creates Working Directory
tmpdir=`mktemp -d`
# Get Current Date
todate=`date +%Y%m%d%H%M%S`
cd ${dirfull%/*}
tar cfps $tmpdir/$todate.tar ${dirfull##*/}
cd $tmpdir
gzip --best $todate.tar
tar cf $archfull $todate.tar.gz
echo $4 >$tmpdir/$todate.revision
tar -C $tmpdir -rf $archfull $todate.revision
cd ..
rm $tmpdir/*
rmdir $tmpdir
fi
;;
X)
dirfull=$3
if [ "${dirfull:0:1}" != "/" ]
then
dirfull=`pwd`/$dirfull
fi
if [ ! -d $dirfull ]
then
echo "$3 must be an existing directory"
exit
fi
archfull=$2
if [ "${archfull:0:1}" != "/" ]
then
archfull=`pwd`/$archfull
fi
if [ ! -e $archfull ]
then
echo "Archive not found"
exit
fi
if [ -z "$4" ]
then
last=`tar tf $archfull | grep "\.tar.gz"`
fil=`tempfile`
tar xvf $archfull $last -O > $fil
cd $dirfull
tar xvzfps $fil
rm $fil
else
testtar=`tar tf $archfull| intcontent | grep "^$4$" `
if [ -z "$testtar" ]
then
echo "Record not found"
exit
fi
tar tf $archfull | sort -r | repatch "$archfull" "$dirfull" $4
fi
;;
R)
archfull=$2
dirfull=`pwd`
if [ "${archfull:0:1}" != "/" ]
then
archfull=`pwd`/$archfull
fi
if [ ! -e $archfull ]
then
echo "Archive not found"
exit
fi
if [ -z "$3" ]
then
echo "Missing rollback record"
exit
fi
testtar=`tar tf $archfull| intcontent | grep "^$3$" `
if [ -z "$testtar" ]
then
echo "Record not found"
exit
fi
tar tf $archfull | sort -r | rollback "$archfull" "$dirfull" $3
;;
T)
tar tf $2 | sort | showcontent $2
;;
*)
echo Bad paramenter, please use U,T,R or X.
;;
esac
Nessun commento:
Posta un commento