Ricerca tra la vecchia roba

Git

Posted: Giugno 3rd, 2007 | Author: | Filed under: Guide, Programmazione | 9 Comments »

 [ATTENZIONE: pagina in via di completamento]

Logo del progetto Git.

Git è un programma distribuito per il controllo di "revisione" (maledetti termini inglesi[1][2]) di progetti software creato da Linus Torvalds per gestire lo sviluppo del kernel ormai giunto ad un numero di righe di codice probabilmente insostenibile per una persona normale. Il progetto partì il 3 Aprile 2005 e già nel giugno dello stesso anno il kernel incominciò ad essere gestito attraverso questo fantastico strumento. Visto l’odio viscerale che il caro nostro amico (?) nutre per il sistema alternativo chiamato CVS (vedere video) e altri sistemi analoghi, ha deciso di scriverne uno lui stesso.  

Qui di seguito presenterò Git nella maniera in cui l’ho imparato: non-linearmente, per attività pratiche; i comandi non direttamente sperimentati avranno un link ad una possibile documentazione specifica.

Caratteristiche

  • Supporto per lo sviluppo non lineare attraverso "branching" e "merging" e supporto per la visualizzazione della loro storia.
  • Sviluppo distribuito: ognuno ha la propria copia locale sulla quale può fare tutte le modifiche di cui sente il bisogno per poi poterle pubblicare ed eventualmente gli amministratori del progetto potranno fare il merge da quel repository.
  • Facilità di pubblicazione remota attraverso un protocollo apposito (git:// appunto) anche se accessibile anche rispetto ai più tradizionali http, ftp, ssh etc…
  • Utilizzo della crittografia per la gestione della storia e dei "commit".

Installazione

 Per installarlo su un sistema debian-like ai necessitano dei seguenti pacchetti

  • git-core
  • git-doc (se desideri avere la documentazione in file:///usr/share/doc/git-doc/index.html)
  • gitk (se si desidera visualizzare graficamente gli sviluppi)
  • git-daemon-run (se si desidera rendere disponibile tramite protocollo git gli sviluppi)
  • git-completion (se si desidera avere il completamento dei comandi da linea di comando)
  • git-email (per le funzioni legatate al mandare patch via email)
  • git-gui (interfaccia spartana per le operazioni di base)
  • git-buildpackage (permette di importare pacchetti sorgenti come repository di git)

Fare attenzione che la versione nei pacchetti differisce molto da una distribuzione ad un’altra causa l’incessante attività degli hacker che lavorano sopra Git (sono iscritto alla ml e mi arrivano circa 50 mail al giorno quando stanno tranquilli).

Concetti

 Git basa il suo design sull’idea che non c’è uno sviluppatore migliore degli altri: non vi è la necessità che esista un repository principale a cui tutti debbano riferirsi, ma permette anzi uno sviluppo distribuito, dove ognuno può, una volta che ha scaricato il codice, apporre delle modifiche e ripubblicare a sua volta il codice permettendo modifiche ulteriori;

Al contrario di altri programmi di questo tipo, lui non elabora file, ma i contenuti dei file, Linus stesso lo descrive come content-addressable; contiene due strutture dati

  1. Un indice mutevole che raccoglie le info sulla directory di lavoro corrente e sulla versione successiva che verrà importata.
  2. Database di oggetti (immutabile).

In particolare esistono varie tipologie di oggetti:

  • Blob: semplicemente un insieme binario di dati che non si riferisce a niente altro. A basso livello viene creato tramite git-update-index, mentre il contenuto può essere visualizzato tramite git-cat-file.
  • Tree: è un oggetto che lega assieme blob e/o tree; viene creato tramite git-write-tree e visualizzato attraverso git-ls-tree
  • Commit: introduce il concetto di "storia": è rappresentato dal tree risultante, dal commit effettuato/i per arrivare a quel punto e dal commento a questi.
  • Tag:identifica simbolicamente altri oggetti, tramite l’identificazione ed il tipo dell’oggetto

Si può riconoscere in questo schema un grafico ciclico diretto.

Ognuno di essi viene denominato attraverso un Hash SHA-1 di 40 cifre esadecimali creato con il contenuto proprio dell’oggetto. Questo codice può essere utilizzato per riferirsi a ben determinati punti (temporali) del progetto.

Capito come funziona a basso livello questo astuto programma, potete immaginare che la grandezza più utilizzata è il tree: siccome un commit punta ad un tree ed una tag punta ad un commit, entrambi possono essere usati indistamente. In maniera analoga una tag è simil commit quindi può essere usata quando si richiede un commit.

Specificando il nome di una referenza, git la cerca in una di queste directory

.git/                        (or $GIT_DIR)
.git/refs/                   (or $GIT_DIR/refs/)
.git/refs/heads/             (or $GIT_DIR/refs/heads/)
.git/refs/tags/              (or $GIT_DIR/refs/tags/)

 Esistono inoltre una grammatica ben precisa per riferirsi alle referenze: la testa del ramo corrente di sviluppo viene indicato con HEAD (si trova in .git/HEAD) ed è un riferimento ad un elemento in .git/refs/heads/; è possibile usare l’operatore postfisso ^ per indicare il genitore di una data referenza. Siccome è possibile usarlo più volte esiste la regola che l’applicazione ripetuta N volte di ^ può essere sostituita con l’operatore postfisso ~ seguito da N: cioé

HEAD^^^ è equivalente a HEAD~3 

Siccome questi riferimenti sono ovviamente legati  alla situazione attuale del ramo di sviluppo, è possibile conoscere la referenza assoluta usando 

git rev-parse REFERENZA

quindi nel caso precedente basta dare git rev-parse HEAD~3 per sapere a quale commit si riferisce. Una descrizione più approfondita la si può trovare tramite git rev-parse –help alla sezione SPECIFYING REVISIONS da cui potrete capire figure quali

                  G   H   I   J
            /     /
             D   E   F
                |  /
               | /   |
                |/    |
                 B     C
                     /
                   /
                    A

           A =      = A^0
           B = A^   = A^1     = A~1
           C = A^2  = A^2
           D = A^^  = A^1^1   = A~2
           E = B^2  = A^^2
           F = B^3  = A^^3
           G = A^^^ = A^1^1^1 = A~3
           H = D^2  = B^^2    = A^^^2  = A~2^2
           I = F^   = B^3^    = A^^3^
           J = F^2  = B^3^2   = A^^3^2

Getting started

 Oltre a voler capire come sono implementate le funzionalità di git, magari vorrete usarlo no? no problema, per questo esiste questa parte: cerchiamo di esplicare i comandi fondamentali e/o più utili, anche se, per un approfondimento, rimando alla documentazione ufficiale.

Supponiamo che abbiamo appena iniziato un nuovo progetto e che ci troviamo nella directory in cui il progetto stesso risiede, per inizializzare il repository bisogna eseguire[3]

git init-db

e di seguito

git add . se si vuole importare tutto, oppure git add file_1 … file_n se interessano solo certi file 

Visto che lo scopo principale di questo programma è il tracciare i cambiamenti, una volta che si è lavorato su un albero di sorgenti si possono controllare i cambiamenti attraverso

git status

che rende noto i file modificati ma non "committati", i file che non vengono inseriti etc..lo schema di utilizzo è il seguente: via via che si fanno le modifiche e si interessa salvarle nel prossimo commit, si aggiungono i file utili tramite git add; per effettuare effettivamente il commit

git commit

che indicizza tutti i cambiamenti memorizzati nell’index (bisogna scrivere il commento ed è consigliabile dividerlo in uno riassuntivo ed uno più esplicativo separati da una linea vuota). Per ricordarci quali sono i cambiamenti che sono stati apportati fra indice, repository e working tree viene in aiuto il comando git diff: normalmente questo comando restituisce la differenza fra il working tree ed il repository senza tenere conto delle modifiche già inserite nell’indice (in pratica fa un diff fra working tree e indice per farla breve); per avere info a riguardo al codice inserito nell’indice rispetto a quello del repo si aggiunge l’opzione –cached al comando.Altra simpatica opzione che aiuta la visualizzazione è –color che rende di colore rosso o verde le righe rispettivamente tolte o aggiunte.

È anche possibile configurare git in maniera tale che inserisca in automatico i vostri dati nei commit: nel mio caso nel file .git/config ho inserito le righe

[user]
    name = "packz"
    email = "packz at autistici dot org"
[ovviamente tu che non sei un bot fai i cambiamenti del caso]

È evidente che è possibile inserire nuovi file in qualunque momento nel progetto usando sempre git add ed è inoltre importante tenere conto del fatto che è possibile usare wildcard per scegliere file che rientrano in un ben determinato schema: per esempio

git add directory_generica/*.txt 

inserirà nell’indice tutti i file che hanno estensione txt (la barra davanti all’asterisco preserva quel carattere dall’interpretazione fatta dalla shell e nel caso di git inserisce file txt anche di sottodirectory). Nel caso abbiate bisogno di eliminare un file dall’indice potete usare

git rm 

che appunto, si occupa dell’operazione inversa di git add (non è proprio così). Nel caso necessitate di un messaggio standard nei commit, è possibile impostare nella configurazione un file contenente il template di questo messaggio tramite il suo path relativo alla working dir:

[commit]
           template = .git/commit-template

dove ovviamente dopo template potete inserire il percorso che volete.

Comandi utili

 Git dispone di varie funzionalità che richiamano le utility da riga da comando tipiche del mondo *nix e che permettono la gestione e la lettura del codice nella sua evoluzione temporale e non; uno dei comandi è git blame che permette di vedere ad ogni linea di un dato file chi lo ha modificato e quando, potendo limitare ad un dato range di questo tramite anche espressioni regolari: per esempio

🙂 $ git blame -L ‘/^function generate_script_js(/,/^}$/’ private/Php/routine.php
96c53bd8 (packz 2007-06-09 16:11:57 +0200 46) function generate_script_js($src){
96c53bd8 (packz 2007-06-09 16:11:57 +0200 47)                   $js = "<script type="text/javascript" src="$src" rel="javascript">";
96c53bd8 (packz 2007-06-09 16:11:57 +0200 48)                   $js .= "</script>n";
96c53bd8 (packz 2007-06-09 16:11:57 +0200 49)                   return $js;
96c53bd8 (packz 2007-06-09 16:11:57 +0200 50) }

restituisce le righe delle definizione di una funzione Php. Inoltre è capace di tracciare linee di codice spostate da un file all’altro passandogli l’opzione -C (seguendo questo consiglio di Torvalds, è meglio copiare, fare il commit e poi modificare quelle linee).

Altro comando è git grep che come l’omonimo da shell cerca l’occorrenza di una determinata stringa nel file passato come argomento (nice l’opzione –color che permette una lettura più chiara).

È possibile anche conoscere in quali commit un dato file è stato cambiato tramite

git whatchanged path/to/file

(senza il nome del file restituisce un log con tutti i file modificati). 

Forse la vera potenzialità sta proprio in questa caratteristica che permette di "montare" opportunamente i singoli comandi per ottenerne di nuovi: supponiamo di voler conoscere il numero di linee a cui ammonta ogni singolo file del progetto, usando le funzionalità della shell bash e i comandi di git questo è possibile tramite il comando

for i in $(git ls-files); do wc -l $i; done | awk ‘//{VAR+=$1; print $0}END{print "totale: "VAR}’

oppure è possibile conoscere  il numero di persone diverse che hanno contribuito al codice presente nel repository (fonte)

git log –pretty=short | sed -n ‘s/^Author: ([^<]*)<.*$/1/p’ | sort | uniq | wc -l

Branch&Tag

 Esistono due cose fondamentali nello sviluppo del software: la possibilità di provare nuove soluzioni e la possibilità di impostare a determinate configurazioni del codice dei nomi/versioni da poter porre come punti fermi nello sviluppo. Questa possibilità sono espresse da Git attraverso i branch e le tag.

 Per branch si intendono delle "linee concettuali" di sviluppo in cui suddividere il progetto, possono essere derivate da progetti esterni (tracking branches) oppure provenire da cambiamenti minimi dal ramo principale (topic branches). All’inizio esiste solo il branch chiamato master ma è semplicissimo crearne di nuovi e utilizzarli acrobaticamente passando da uno all’altro!!!

Un semplice

git branch

restituisce l’elenco dei branch disponibili, mentre per crearne uno di nome ‘hazardous’ si abbisogna del magico comando

git branch hazardous

a cui sarà possibile accedere attraverso

git checkout hazardous

e fare le modifiche senza modificare il ramo master; riutilizzando git branch si otterrà

master
 * hazardous

con l’asterisco ad indicare il branch in cui si stanno effettuando le modifiche. Nel momento in cui ci ritroviamo con del codice in un branch "experimental" che riteniamo oramai stabile, possiamo unirlo al branch principale tramite il merging:

git merge ["qui un commento" master] hazardous

oppure spostarci nel branch voluto e poi eseguire semplicemente

git merge hazardous 

se non ci saranno problemi i due rami saranno uniti in uno solo e quello master conterrà i cambiamenti; git possiede degli algoritmi per gestire la fusione di codice nello stesso file, ma non è detto che il tutto sia possibile, in questo caso nel file (o nei file) interessato/i saranno presenti delle linee in stile diff con evidenziati i problemi (appena sarò più pratico vi farò sapere). Se non pensate di effettuare più cambiamenti da quel branch lo potete cancellare tramite

git branch -d hazardous 

In una sola mossa è possibile anche creare e spostarsi nel nuovo branch tramite

git checkout -b hazardous

inoltre è anche possibile fare il checkout di una versione più vecchia usando il commit come argomento per poi aprire un nuovo branch da quel punto usando la regola appena insegnata. È anche possibile creare dei branch "vuoti" cioé dei rami di sviluppo senza parents: il metodo da usare è un po’ tricky e ve lo espongo nel seguito

$ git-symbolic-ref HEAD refs/heads/mybranch
$ git rm –cached -r .
$ rm * # questo serve quando si cambierà branch per evitare errori
$ git commit –allow-empty

è utile per creare del codice parallelo al progetto di sviluppo ma che può non interessare i normali developer, per esempio la web page del progetto etc…

Si può utilizzare il checkout per invertire i cambi locali al proprio archivio locale tramite

git checkout -f

che in pratica riporta all’attuale ramo il contenuto della directory. 

Per quanto riguarda i tag invece è ugualmente semplice la loro gestione:

git tag -l <pattern>

visualizza i tag con un nome che rispecchia il pattern passato, mentre

git tag <label>

imposta <label> come tag della attuale versione.

Rebasing

 È anche possibile che vi troviate in questa situazione

					 A---B---C topic
	/
	D---E---F---G master
	
e vogliate aggiornare il branch topic con master in modo da ottenere la situazione 
					              A'--B'--C' topic
	/
	D---E---F---G master
	

senza dover fare un branch temporaneo e spostare le patch per ricreare la situazione: vi viene in aiuto git rebase! basta in questo caso dare

git rebase master topic
che passa al branch topic ed effettua appunto il rebasing; nel caso foste già in topic so potrebbe togliere l’ultimo argomento.Questo comando è molto potente in quanto permette di modificare i singoli commit fondendoli o dividendoli all’occorrenza per sopperire a qualsiasi necessità. Nel caso ci siano dei problemi è possibile annullare l’operazione tramite
git rebase –abort
Esiste anche una versione interattiva di questo comando nelle ultimissime versioni a cui si accede (sorpresa) con

git rebase –interactive 
Errare humanum est

 
Può capitare di sbagliare ed infatti ecco qualche comando utile per invertire l’errore

  • git checkout FILE
  • git reset – dimentica nuovi file aggiunti
  • git reset –hard – tralascia i cambiamenti
  • git reset –soft HEAD^ – cancella l’ultimo commit ma lascia i cambiamenti
  • git checkout -m branch_corretto nel caso si sia iniziato a fare cambiamenti nel branch errato

Bisecare i bug

Succede appunto di sbagliare durante la produzione del codice, ma magari ce ne accorgiamo (molto) in seguito,magari qualcun’altro che il risultato del nostro lavoro lo usa in maniera differente e nel frattempo sono stati pushati svariati commit nel repository e non possiamo sapere direttamente quale commit in particolare ha introdotto un baco: a questo viene aiuto il comando git bisect che si preoccupa di creare un branch temporaneo e di effettuare una operazione di dicotomia sulle parti del codice

Io personalmente non l’ho provato ancora, ma potete trovare dalle parole del sommo il suo utilizzo.

Importare un repository remoto

 È importante anche poter importare da una locazione remota un progetto che ci interessa, in maniera da apportare le modifiche da eventualmente rispedire al mittente (w l’open source); per mettere in pratica questo il comando è

git clone <uri> 

che crea nella directory corrente il contenuto del repository e due branch: master e origin, di cui il primo, per convenzione, è preposto alle modifiche. Per aggiornare il contenuto locale rispetto a quello remoto è usato il comando

git pull

(l’indirizzo da cui proviene è nel file .git/remotes/origin). 

Di default viene scaricato solo il ramo master di un repository, per ottenere tutti i rami presenti eseguire

git branch -r

che restituirà qualcosa come

   origin/HEAD
  origin/label_placing
  origin/master
  origin/menu_on_client
  origin/pdf_profile
  origin/xform
  origin/xslt

a questo punto con un bel

git checkout –track -b local_branch_name origin/remote_branch

creerà un branch locale chiamato local_branch_name a partire dalla referenza origin/remote_branch e lo sincronizzerà con quello remoto. 

 Patching

 Una ulteriore chicca di questo programma è la possibilità di generare delle patch da spedire per posta tramite il comando (con l’opzione -n e più patch specificate vi ritroverete con l’intestazione della mail nella forma "[PATCH 2/5]")

git format-patch  <since>..[<until>]

A meno che non specifichiate –stdout vi ritroverete tutti i diff dei vari commit per arrivare da <since> ad <until> (se quest’ultimo non è specificato, pone di default il ramo attuale); si può indicare per questi sia il loro codice hash che il nome del branch che una tag. Ovviamente come si possono spedire le patch, si possono anche "leggere" ed è qui che la potenzialità svetta:

git am <mail box>

apre la mail, scarica le patch e le applica. Per <mail box> si intende il percorso alla cartella locale della mail (nella documentazione parlano di "Berkeley mail" che dovrebbe corrispondere alla posta letta attraverso il pacchetto mailx in un sistema Debian); nel mio caso è /var/log/packz se qualcuno ne sa di più batta una mail 😉

Consiglio di usare l’opzione –interactive così da poter scegliere le patch da applicare (una alla volta!) altrimenti vi dà errore (se non ci sono solo patch!!!), al massimo scarivatele a parte e poi applicatelo. Comunque a meno che non siate dei guru installatevi pine (su ubuntu c’è alpine) altrimenti diventate rincoglioniti ad usare mail.

Nel caso non vogliate sclerare è possibile applicare la patch manualmente tramite il comando

git-applymbox <file della patch> 

e vi ritroverete il commit già inserito nel tree con il commento preceduto da "[PATCH]" per la gioia di grandi e piccini. 

 Nel caso non vi fidiate del vostro client di posta è possibile utilizzare git send-mail per avere con sicurezza l’invio della patch: quindi

git send-mail –to packz@porco.lo.spam.org –subject "vattela a prendere nel culo" files 

Probabilmente non avete un servet SMTP quindi vi conviene installarvene uno affinché il tutto funzioni per il meglio: dalla documentazione di git si consiglia msmtp, un semplice programma che permette di inviare patch anche attraverso connessioni che utilizzano SSL (tipo gmail oppure autistici ;-)): per installarlo

# apt-get install msmtp 

poi creare il file ~/.msmtprc con per chi usa un account su autistici

# Example for a user configuration file
# Set default values for all following accounts.
defaults
tls on
tls_starttls off
#tls_trust_file /etc/ssl/certs/ca-certificates.crt
tls_certcheck off
logfile ~/.msmtp.log
# My email service
account packz
host smtp.autistici.org
port 465
from packz@un.mondo.senza.spam.org
auth on
user packz@un.mondo.senza.spam.org
# if not set below, msmtp ask for a password
password
# Set a default account
account default : packz

e poi inviare la mail tramite

 
git send-email –smtp-server /usr/bin/msmtp file_contenente_la_patch

 a quel punto vi verranno chieste interattivamente gli estremi della mail e le manderà in automatico.

Troubleshooting 
A me personalmente non ha funzionato subito: ecco i possibili scenari
  • msmtp: /home/packz/.msmtprc: must have no more than user read/write permissions
    Soluzione: chmod 0600 ~/.msmtprc
  • msmtp: the server sent an empty reply
    Soluzione: tls_starttls off (forse non la migliore…)
  • msmtp: TLS certificate verification failed: the certificate hasn’t got a known issuer
    Succede siccome il server usato (come per esempio autistici), possiede un certificato "self signed".
    Soluzione: tls_certcheck off (sicurezza portami via)[per altre info]

Per usarlo con Gmail guardate nelle GitTips.
Pubblicare il codice

Magari siete proprio voi ad avere la necessità di rendere pubblico il codice e di rendere disponibile quindi agli altri di poter clonare il vostro repository; esistono vari metodi per poterlo fare

  • HTTP
    Di seguito le azioni da compiere
    1) Spostarsi nella directory padre rispetto al vostro albero dei sorgenti (che deve essere già "gittato");
        il progetto presuppongo sia in una directory chiamata "project"
    2) eseguire git clone –bare project project.git
    3) copiare la directory project.git nello spazio del web server
    4) spostarsi nella directory appena copiata
    5) Eseguire git –bare update-server-info
    6) Rendere eseguibile lo script post-update tramite chmod a+x hooks/post-update nella directory project.git.

Nel caso poi vogliate pubblicare su questo server le nuove modifiche del codice, basta eseguire tramite ssh il comando

git push ssh://mio.domino/absolute/path/to/git/project.git master:master

 

Git by Git

 È possibile ottenere git attraverso git stesso con il comando

 git clone git://git.kernel.org/pub/scm/git/git.git

Consiglio caldamente di installare la versione più "moderna", magari tenendola parallelamente a quella della vostra distro ufficiale: i passaggi da compiere sono i seguenti (tutti da eseguire come utente normale)

git pull      # aggiorna il tutto
git tag       # per vedere l’ultima versione stabile (oppure gitk)
git checkout vx.x.x      # pone il working tree a quella versione (no -rc please)
./configure –prefix=/opt/git-vx.x.x # imposta il path di installazione in /opt/ (createla eventualmente)
make
make install
cd /opt/
unlink git      # se avete già una precedente installazione manuale
ln -s git-vx.x.x git    # crea il link simbolico

Aggiornate il PATH in ~/.bashrc nella seguente maniera

PATH=/opt/git/bin/:$PATH

e verificate che git –version vi restituisca il numerillo giusto… nel caso interessi, la documentazione va creata a parte con make doc, installata con make install-doc ricordandosi di impostare il percorso di man correttamente tramite la variabile MANPATH.

Configurazione

 Un programma dalle così vaste potenzialità e comandi accessori, necessità di poter essere configurato a livello utente come meglio si crede: a questo scopo sono possibili due alternative

  1. editare a mano il file .git/config
  2. usare il comando git config (usando assieme l’opzione –global viene impostato per tutti i repository locali contenuti nel computer)

Una possibilità di vasta portata è la capacità di creare degli alias, cioé dei nuovi comandi che eseguano comandi di git e non: per impostarli basta creare una sezione alias nel file di configurazione (ulteriori informazioni).

Cleanup&Optimize

Dopo aver lavorato per un certo periodo di tempo su un dato repository è possibile eseguire delle operazioni di "pulizia" per tenere ordinato il nostro archivio e diminuire lo spazio occupato su disco dallo stesso. Il primo comando e git repack che riscrive tutti gli oggetti presenti nella directory (.git)/objects/ in una sottodirectory (.git)/objects/pack/ in un formato particolare (in pratica in una forma deltificata, per ulteriori info guardate Documentation/technical/pack-format.txt nell’albero dei sorgenti di git). Il sommo consiglia di effettuare il repacking per non avere performance scadenti sul lungo periodo. Appena creati i file *.pack comunque restano in giro gli oggetti originali (che a questo punto sono rindondanti) e servirebbe dare un bel git-prune-packed, tuttavia pare un comando di basso livello ed è quindi preferibile usare i comndi più general purpouse per la pulizia.

Per la pulizia del repository è consigliabile l’uso di git gc che si preoccupa di chiamare a sua volta git prune, il quale è adibito ad eliminare ogni elemento non più accessibile (tramite git fsck –unreachable), oltre che anche git-prune-packed come accennato sopra.

Anche l’occhio vuole la sua parte

 Siccome è difficile seguire gli sviluppi del software facendo solo affidamento ai commenti dei vari commit, è disponibile il progetto gitk, attualmente distribuito assieme a git stesso tramite cui è possibile visualizzare l’evoluzione del progetto che abbiamo a cuore: tramite le parole dello stesso Torvalds possiamo dire

gitk is really quite incredibly cool, and is great for visualizing what is going on in a git repository.  It’s especially useful when you are looking at what has changed since a particular version, since it gracefully handles partial trees (and this also avoids the expense of looking at _all_ changes in a big project).

(commit 5569bf9bbedd63a00780fc5c110e0cfab3aa97b9 di git ;-)), ma una immagine vale più di mille parole

 

Mostra una parte di una schermata di gitk relativa a git stesso.

 

questo è come si presenta visivamente il progetto git a circa metà del suo sviluppo: i rami di diverso colore sono i vari branch… 

Esistono tuttavia altri programmi analoghi che tuttavia non hanno la stessa resa grafica: tig, qgit… se approfondirò il loro utilizzo lo saprete.

Quando la rete non è available 

Succede che non si ha a disposizione un accesso alla rete e quindi i comandi fetch/pull/push non possono essere usati  nella loro maniera convenzionale, nonostante questo git è ancora capace di essere pienamente funzionale attraverso il comando git bundle.

 La sintassi del comando in questione è una delle seguenti

git-bundle create <file> <git-rev-list args>
git-bundle verify <file>
git-bundle list-heads <file> [refname…]
git-bundle unbundle <file> [refname…]

La prima versione è quella utile per creare il bundle: <file> corrisponderà al nome del file risultante e <git-rev-list args> è un qualunque argomento accettabile da git-rev-list (quindi oltre sha1 si possono usare tag e identificativi temporali come "–since=10.days.ago"). Per creare semplicemente un bundle con tutto l’archivio si usa

git-bundle create my-project.bundle master

Nel frattempo che imparo meglio leggetevi questo thread.

Packaging

In una certa misura è importante poter esportare il proprio lavoro sotto forma di archivi da poter inviare o da scaricare comodamente anche a chi non è un developer (developer developer) e proprio per questo è stato inventato il comando 

git archive –format=<tar|zip> [–prefix=<prefix>/] <tree-ish> [path]

 
Poi per chi è un amante delle distribuzioni debian-based, esiste anche un insieme di comandi pensati per interagire con i rilasci tramite pacchetti, inglobati nella suite git buildpackage

  • git import-dsc: crea un repository git da un pacchetto debian pre-esistente.
  • git import-orig: crea un repository git da sorgenti.
  • git buildpackage: compila e crea il pacchetto.
  • git dch: aggiorna il Changelog a partire dai commit di git.

Esistono principalmente due modi per iniziare ad usarlo

  1. Importare un pacchetto pre-esistente: facciamo un esempio con netcat
    $ apt-get source netcat
    $ git import-dsc
  2. Importare un progetto non avente un pacchetto debian:supponiamo che il progetto sia archiviato in un file project-0.2.tar.gz (attenzione che l’archivio deve contenere un prefisso altrimenti il comando git import-orig restituirà un errore del tipo Cannot copy files: [Errno 20] Not a directory: ‘../tmpnsjU34/NOME_DI_QUALCHE_FILE’)
    $ mkdir project-0.2
    $ cd project-0.2
    $ git init
    $ git import-orig -u 0.2 /path/to/project-0.2.tar.gz

In generale vengono usati due branch particolari

  • debian-branch: contiene il ramo di lavoro dove vengono effettuati i lavori nel codice
  • upstream-branch: contiene l codice proveniente dalla upstream release (come tradurlo in italiano?)

tuttavi nulla vieta di cambiare la topologia del repository come meglio vi aggrada.

Per ulteriori informazioni andate sulla home page:  http://honk.sigxcpu.org/projects/git-buildpackage/manual-html/gbp.html.

Contributi

Esistono tutto un insieme di piccoli programmini accessori a git che si trovano sotto la directory /usr/share/doc/git-core/contrib/; fra questi vi segnalo nel bash_completion la possibilità di avere il prompt con indicato il branch in cui ci si trova (nel caso ovviamente la directory corrente corrisponda ad un repo git)tramite __git_ps1: il suo utilizzo è tramite

PS1=’$(__git_ps1 "%s branch")’

ovviamente potete aggiungere sia prima che dopo robe utili a voi…

Linkografia&Documentazione


Footnote 

1 – In inglese viene chiamato distributed revision control software configuration management project.
2 – Git in inglese è  usato come insulto per riferirsi ad uno stupido.
3 – La versione nel sistema da me usato è la 1.4.4.2, mentre nella documentazione ufficiale ci si riferisce alla 1.5 o successive, quindi i comandi potrebbero essere diversi; si può scaricare la versione unstable per debian a questo indirizzo.


9 Comments on “Git”

  1. 1 ToxicWind said at 5:11 pm on Luglio 11th, 2007:

    Ciao, complimenti per la pagina (una delle poche che ho trovato in italiano).
    Sto implementando un server git/cogito per un cliente, ma non riesco a capire una cosa, magari puoi/vuoi chiarirmi le idee.
    Se all’interno del repo ho dei file binari (nel mio caso immagini gif), che vengono modificate, mi è capitato di incappare in un problema :
    se quando faccio il push del mio repo sul server sono state apportate delle modifiche il sistema mi dice che prima di poter fare un push mi devo allienare alla versione attuale, quindi vado di cg-fetch, cg-diff -r origin (per vedere quali sono le differenze), cg-merge. E qui esce il problema :
    se la gif che ho modificato è stata a sua volta modificata da un altro utente e pushata, il merge mi avvisa, e va a scrivere nel codice dell’immagine dei tag che dovrebbero aiutarmi a capire dove sono state fatte le modifiche. Ora, questa cosa mi sta bene coi file di testo, ma coi binari si risolve tutto in un gran casino, e ovviamente alla fine ci si ritrova con la gif illeggibile.
    L’unica scappatoia che ho trovato al momento consiste nel togliere la mia immagine dal repository (cg-rm ), fare il merge, e poi ricacciare dentro a mano l’immagine modificata. A questo punto posso fare la mia push senza problemi.
    Credo però ci siano metodi più umani per gestire questo tipo di dati…tu ci hai già avuto a che fare ? Come mi dovrei muovere ? (pensavo ai blob object, ma non ho ancora capito come si generano e come si gestiscono).

    Grazie per l’attenzione !! 🙂

    Ciax…
    Tox…

  2. 2 packz said at 12:07 am on Luglio 13th, 2007:

    Ma penso che questa situazione sia difficile da gestire in quanto il programma è stato pensato appositamente per i sorgenti del kernel e quindi file di testo; io non sono un gran esperto e sto scrivendo a poco a poco le cose che imparo (tu mi sopravvaluti… ma grazie lo stesso per i complimenti) e nel mio caso, quando sono presenti file binari (immagini per esempio) li tengo in directory separate e poi le importo a parte: è meno frequente cambiare un immagine e di solito se ne occupa una persona sola, io non so bene come sia il tuo caso… se risolvi il tuo problema o hai qualche consiglio sull’uso di git non ti fare problemi a postarli che li aggiungo…

  3. 3 ToxicWind said at 5:04 pm on Luglio 13th, 2007:

    Grazie per la risposta, in effetti ho trovato anche un post del sommo Torvald a proposito della gestione di file binari, ti segno il link :
    http://www.gelato.unsw.edu.au/archives/git/0511/11494.html

    a quanto pare non è una cosa implementata direttamente.

    Sto facendo un po di prove per trovare un qualche giro di comando da lanciare per gestire più o meno manualmente la cosa.

    Se trovo una soluzione degna di nota ti aggiorno !! 🙂

    Grazie per la risposta 🙂

    Ciax…
    Tox… 🙂

  4. 4 packz said at 5:23 pm on Settembre 27th, 2007:

    Ho trovato questo delizioso link su del.icio.us su di un tipo che presenta una soluzione al tuo problema

    http://www.bluishcoder.co.nz/2007/09/git-binary-files-and-cherry-picking.html

    fecendo uso di gitattributes (vedi http://www.kernel.org/pub/software/scm/git/docs/gitattributes.html) appena ho tempo scrivo qualcosa, magari la traduzione della pagina di manuale… se qualcun’altro ha soluzioni si faccia avanti.

  5. 5 d said at 5:08 pm on Luglio 26th, 2008:

    ciao,

    per usare msmtp su autistici puoi :

    wget http://ca.autistici.org/ca.pem -O ~/ca.autistici.org-ca.pem

    account packz-tls
    port 587
    from packz@un.mondo.senza.spam.org
    user packz@un.mondo.senza.spam.org
    auth on
    password
    tls on
    host smtp.autistici.org
    tls_certcheck on
    tls_trust_file ~/ca.autistici.org-ca.pem

    p.s.: non so se sia un errore o sia tutto voluto da antani, ma il .pem indicato da autistici semplicemente non va con msmtp.

  6. 6 Alessandro said at 9:54 pm on Agosto 27th, 2010:

    Ciao.
    Ho una domanda probabilmente stupida ma non so come risolvere. Sto cercando di utilizzare GIT per un mio progetto web.

    Quindi ho creato un repository git sul server, che chiamo origin
    ed un suo clone sul mio desktop, che mi serve per fare le modifiche.

    Riesco a fare il push delle mie modifiche verso il server, ma non ho capito come posso fare per aggiornare i file sul server.
    Se guardo il log con git-log infatti vedo correttamente lo storico delle modifiche, ma i file rimangono quelli precedenti.

    Lo stesso vale al contrario, quando faccio il fetch dal server nel mio locale le modifiche sono visibili sono nel branch origin/master, e non so come applicarle anche al mio master locale.

    Riesco ad applicarle solo con un reset con l’id del commit che voglio, oppure con il git checkout -f, ma non mi paiono la soluzione giusta.

    Certamente devo aver capito male qualche passaggio…

    Mi sai aiutare?

  7. 7 packz said at 9:31 am on Agosto 28th, 2010:

    Allora Alessandro, devi abilitare gli hooks (http://www.kernel.org/pub/software/scm/git/docs/githooks.html) nel repository lato server, in particolare quello post-receive dicendogli di fare un checkout quando una particolare ref è aggiornata altrimenti senza un esplicito checkout il repository rimarrà allo stato precedente.

    Inoltre ti consiglio di separare il database degli object dal working tree (che sarebbe poi la tua webroot se ho ben capito) facendo diventare il repository bare (cioé far diventare la directory .git/ quella principale) e poi usare nell’hook le opzioni –git-dir e –work-tree altrimenti mi pare che in caso di push con storie di merge potresti avere qualche problema… oltre che per il fatto che se nella webroot ci sta la directory .git/ la gente potrebbe accedervi, non che sia tutto sto problema, ma meglio evitare…

  8. 8 Alessandro said at 2:30 pm on Agosto 28th, 2010:

    Intanto ti ringrazio, guardo il link che mi hai mandato.

    Per quanto riguarda il webroot, non dovrei avere problemi perché il mio è un progetto django, ed il progetto è in una sottodirectory, che non contiene .git

    Il webserver prende il codice python dal path interno, ed ignora semplicemente la directory git.

    Il tuo consiglio invece mi torna utile per i file statici (css, js, per intenderci) sui quali vale il tuo ragionamento.

    Questo però lo guardo in un secondo step, perché ho iniziato da poco ad utilizzare git ed ho ancora parecchia confusione nella testa.

    Mi sai consigliare un manuale decente? la documentazione in rete c’è, ma è troppo sparsa e mi pare più utile come reference per uno che già usa questo sistema piuttosto che per chi inizia da zero…

  9. 9 packz said at 1:02 pm on Agosto 29th, 2010:

    Ti capisco, ma purtroppo è un tool sviluppato da programmatori poco avvezzi alla narrativa, tuttavia cercando in rete qualcosa si trova, avere la fortuna di azzeccare volta per volta i termini di ricerca nel caso di problemi può aiutare; ti consiglio se hai twitter di cercare #git spesse volte qualche chicca ne esce.

    I link in mio possesso su git li puoi trovare qui

    http://link.autistici.org/bookmarks/packz/git

    ma ti consiglio questi in particolare

    – http://marklodato.github.com/visual-git-guide/
    – http://www.eecs.harvard.edu/~cduan/technical/git/
    – http://book.git-scm.com/
    – http://progit.org/

    Un altro consiglio è di seguire la mailing list, che benché contenga molte questioni tecniche (utile ai veri programmatori™), spesse volte dà spazio a mail di richiesta magari un po’ particolari utili alla comprensione di tutto l’ambaradan. Inoltre essendo un progetto in continuo sviluppo hai subito l’anteprima delle novità (git notes FTW).

    P.S: usi django per lavoro? ma in italia? io sono un fans di quel framework…