Wiki Ubuntu-it

Indice
Partecipa
FAQ
Wiki Blog
------------------
Ubuntu-it.org
Forum
Chiedi
Chat
Cerca
Planet
  • Pagina non alterabile
  • Informazioni
  • Allegati
  • Differenze per "Programmazione/Git"
Differenze tra le versioni 3 e 6 (in 3 versioni)
Versione 3 del 05/12/2014 13.17.57
Dimensione: 37345
Autore: jeremie2
Commento: Importati i contenuti dalla pagina jepessen/Prove1
Versione 6 del 05/12/2014 16.58.20
Dimensione: 36966
Autore: jeremie2
Commento:
Le cancellazioni sono segnalate in questo modo. Le aggiunte sono segnalate in questo modo.
Linea 3: Linea 3:
<<Indice()>> <<Indice(depth=2)>>
Linea 8: Linea 8:
'''Git''' è un [[http://it.wikipedia.org/wiki/Controllo_versione|CVS]] ('''C'''ontrol '''V'''ersion '''S'''ystem). Gli aspetti importanti di un CSV sono:

 * Tracciare la storia di un progetto e poter lavorare con le versioni precedenti.
 * Permettere la collaborazione di piu' persone allo stesso progetto.
 * Organizzare e semplificare l'andamento del progetto.

La maggior differenza con Git ed altri programmi simili ([[Programmazione/Cvs|CVS]], [[Programmazione/Subversion|Subversion]] ecc) e che lo accumuna ad altri programmi della sua stessa generazione ([[http://mercurial.selenic.com|Mercurial]], [[Programmazione/Bazaar|Baazar]] ecc), è che è un sistema di controllo di versione localizzato, cioe' che funziona con repository locali invece di remoti.

Con programmi come '''Subversion''' si ha un repository centrale, che materialmente e' il posto (un server, un servizio web) dove risiede il progetto. I vari utenti sincronizzano il lavoro con questo repository, e quando devono salvare delle modifiche al progetto (commit) le salvano direttamente sul repository centralizzato.

'''Git''' invece lavora in locale: non esiste un repository centralizzato (nel modo che si intende in '''Subversion''' et similia): ogni cartella di lavoro e' un repository autosufficiente. Commit, ricerca log e molte altre azioni vengono eseguite in locale, senza l'appoggio di un server esterno. Quando e' necessario e' possibile sincronizzarsi con altri repository, che puo' essere un server centrale, il repository del pc accanto al tuo e cosi' via. Questa flessibilita' ha numerosi vantaggi.

= Installazione =

Git e' presente nei repository di Ubuntu, per cui basta digitare da terminale

{{{
$ sudo apt-get install git
}}}

= Configurazione base =

Prima di lavorare con un repository, e' importante (ma non necessario a dire il vero) configurare git dando alcune informazioni.

E' importante immettere almeno il nome utente e l'email che si vogliono utilizzare per identificarsi nei progetti:

{{{
$ git config --global user.name "user"
$ git config --global user.email user@mail.com
}}}

In questo modo quando si comincia un nuovo progetto, o se ne clona uno pre-esistente, verranno utilizzati questi dati per identificarvi (anche se e' possibile modificare questi dati per un singolo repository omettendo il flag '''--global''').

E' possibile anche creare dei comandi personalizzati, chiamati '''alias''', che permettono di eseguire con un semplice nome mnemonico dei semplici comandi. Io consiglio di digitare il seguente comando, per la visualizzazione dei log (in seguito faro' riferimento a questo comando per mostrare i log di esempio, perche' e' molto descrittivo e significativo nel contenuto), che aggiunge l'alias {{{lg}}}:

{{{
$ git config --global alias.lg 'log --pretty=format:'%C(yellow)%h%Cred%d%Creset - %C(cyan)%an %Creset: %s %Cgreen(%cr)' --decorate --graph --all --abbrev-commit'
}}}

Vedremo fra poco come utilizzarlo.
'''Git''' è un [[http://it.wikipedia.org/wiki/Controllo_versione|CVS]] ('''C'''ontrol '''V'''ersion '''S'''ystem). Gli aspetti importanti di un CSV si possono riassumere nel:

 * tracciare la storia di un progetto e poter lavorare con le versioni precedenti;
 * permettere la collaborazione di più persone allo stesso progetto;
 * organizzare e semplificare l'andamento del progetto.

Rispetto a programmi come [[Programmazione/Cvs|CVS]], [[Programmazione/Subversion|Subversion]] ma più similmente a [[http://mercurial.selenic.com|Mercurial]], [[Programmazione/Bazaar|Baazar]], '''Git''' è caratterizzato da un sistema di controllo di versione localizzato, cioè utilizzabile con repository locali invece che remoti. Programmi come '''Subversion''' adottano un repository centrale, che materialmente è il posto (server, servizio web) in cui risiede il progetto. Gli utenti sincronizzano il lavoro con questo repository e quando devono salvare delle modifiche al progetto (''commit''), le salvano direttamente sul repository centralizzato.

'''Git''' invece lavora in locale. Non esiste un repository centralizzato nel modo che si intende in '''Subversion''' et similia. Ogni cartella di lavoro è un repository autosufficiente. Commit, ricerca log e molte altre azioni vengono eseguite in locale, senza l'appoggio di un server esterno. Quando si rende necessario è possibile sincronizzarsi con altri repository (server centrale, pc in rete, ecc..). Questo comporta ovvi vantaggi in termini di flessibilità.

<<Anchor(alias)>>
= Installazione/Configurazione base =

 0. Per installare '''Git''' digitare in un [[AmministrazioneSistema/RigaDiComando|terminale]]:{{{
sudo apt-get install git
}}}
 0. Impostare '''nome utente''' e '''indirizzo e-mail''' che si vogliono utilizzare per identificarsi nei progetti:{{{
git config --global user.name "NOME_UTENTE"
git config --global user.email INDIRIZZO_E-MAIL
}}}avendo cura di soistituire le diciture NOME_UTENTE e INDIRIZZO_E-MAIL col proprio nome utente e indirizzo e-mail.
 ||<tablestyle="text-align: justify; width:100%;" style="border:none;" 5%><<Immagine(Icone/Piccole/note.png,,center)>> ||<style="padding:0.5em; border:none;">''È possibile modificare questi dati per un singolo repository omettendo il flag '''--global.''''' ||
 0. Consigliato ma non obbligatorio, impostare l'alias '''lg''' per una versione personalizzata del comando '''log''' al fine di migliorarne la leggibilità:{{{
git config --global alias.lg "log --pretty=format:'%C(yellow)%h%Cred%d%Creset - %C(cyan)%an %Creset: %s %Cgreen(%cr)' --decorate --graph --all --abbrev-commit"
}}}
Linea 51: Linea 35:
Quando si comincia a lavorare su un progetto, si puo' iniziare un progetto vuoto, oppure si puo' iniziare a lavorare su un progetto gia' esistente. Viene qui mostrato come creare un '''nuovo progetto''' o come importare (clonare) un '''progetto esistente'''.
Linea 55: Linea 39:
Se si vuole cominciare a lavorare ad un nuovo progetto, basta andare nella cartella apposita (che puo' essere vuota o contenente un progetto gia' avviato) e digitare

{{{
$ git init
}}}

Questo inizializza un repository vuoto, su cui potete cominciare a lavorare da zero.
<<Anchor(creare)>>
 0. Creare la cartella `~/gitproject` nella propria '''Home''' che conterrà il progetto e spostarsi al suo interno:{{{
mkdir gitproject
cd gitproject
}}}
 0. Per inizializzare un repository vuoto su cui poter incominciare a lavorare, digitare:{{{
git init
}}}verrà restituito un messaggio del tipo{{{
Initialized empty Git repository in /home/USER_NAME/gitproject/.git/
}}}All'interno di `~/gitproject` sarà ora presente la cartella `.git` che contiene le informazioni del repository. Questa cartella è molto importante perché è l'unica che contiene tutti i dati necessari. (In '''Subversion''' ad es. viene creata una cartella .svn in ogni sottocartella del progetto).
Linea 65: Linea 52:
Se il progetto e' gia' esistente su un server, e voi dovete scaricarlo per lavorarci, il comando da eseguire e'

{{{
$ git clone percorso/progetto
}}}

Il comando creera' una cartella col nome del progetto e vi copiera' dentro il contenuto.

Se invece si vuole clonare il repository in una cartella specifica, basta aggiungere il path al comando:

{{{
$ git clone percorso/progetto percorsolocale
 * Per scaricare un progetto già esistente su di un server, il comando da eseguire è:{{{
git clone PERCORSO/PROGETTO
}}}Verrà creata nella propria '''Home''' una cartella col nome del progetto in cui saranno copiati i contenuti.

 * Se invece si preferisce clonare il repository in una cartella specifica, basta aggiungere il percorso al comando:{{{
git clone PERCORSO/PROGETTO PERCORSO/LOCALE
Linea 81: Linea 62:
Il commit e' l'azione che permette di aggiungere, rimuovere e modificare file del repository.

Supponiamo di avere un repository appena creato:

{{{
$ mkdir gitproject
$ cd gitproject
$ git init
Initialized empty Git repository in /home/user/gitproject/.git/
}}}

Nella cartella precedentemente vuota, e' presente adesso la cartella {{{.git}}} che contiene le informazioni del repository. Questa cartella e' molto importante perche' e' l'unica che contiene tutti i dati necessari (in [[Subversion]] invece viene creata una cartella .svn in ogni sottocartella del progetto).

Possiamo vedere lo stato del repository, che ovviamente non dira' niente di utile:

{{{
$ git status
# On branch master
#
# Initial commit
#
Il commit è l'azione che permette di aggiungere, rimuovere e modificare file del repository.<<BR>>
Supponiamo di aver creato il progetto '''gitproject''' come [[#creare|mostrato]] in precedenza e di essersi spostati al suo interno:{{{
cd gitproject
}}}

Possiamo vedere lo stato del repository, che ovviamente non dirà niente di utile:{{{
git status
}}}che restituirà:{{{
Sul branch master

Commit iniziale
Linea 105: Linea 77:
Possiamo anche analizzarne i log (utilizzando l'alias precedentente creato):

{{{
$ git lg
Possiamo anche analizzarne i log (utilizzando l'alias [[#alias|precedentente]] creato):

{{{
git lg
Linea 117: Linea 89:
$ echo Inizio progetto > README
$ git status
echo Inizio progetto > README
git status
Linea 126: Linea 98:
# README #   README
Linea 132: Linea 104:
$ git add README
$ git status
git add README
git status
Linea 141: Linea 113:
# new file: README #   new file: README
Linea 150: Linea 122:
$ git commit -a -m "README aggiunto" git commit -a -m "README aggiunto"
Linea 161: Linea 133:
$ git status git status
Linea 169: Linea 141:
$ git lg git lg
Linea 184: Linea 156:
$ git log git log
Linea 196: Linea 168:
$ mkdir src
$ printf '#include <iostream>\nint main() {\n\tstd::cout << "Benvenuti su Git!" << std::endl;\n\treturn 0;\n}\n' > src/main.cpp
$ echo 'vpath %.cpp ./src'$'\n'$'\n''src = main.cpp'$'\n\n''objects = $(patsubst %.cpp,obj/%.o,$(src))'$'\n\n''$(objects): | obj'$'\n\n''obj:'$'\n\t''@mkdir -p $@'$'\n\n''obj/%.o : %.cpp'$'\n\t''@echo $<'$'\n\t''@$(CXX) $(CXXFLAGS) -c $< -o $@'$'\n\t''mkdir bin'$'\n\t''@$(CXX) $(LDFLAGS) -o bin/main $(objects)'$'\n\n''clean:'$'\n\t''rm -fr bin/ obj/'$'\n' > Makefile
mkdir src
printf '#include <iostream>\nint main() {\n\tstd::cout << "Benvenuti su Git!" << std::endl;\n\treturn 0;\n}\n' > src/main.cpp
echo 'vpath %.cpp ./src'$'\n'$'\n''src = main.cpp'$'\n\n''objects = $(patsubst %.cpp,obj/%.o,$(src))'$'\n\n''$(objects): | obj'$'\n\n''obj:'$'\n\t''@mkdir -p $@'$'\n\n''obj/%.o : %.cpp'$'\n\t''@echo $<'$'\n\t''@$(CXX) $(CXXFLAGS) -c $< -o $@'$'\n\t''mkdir bin'$'\n\t''@$(CXX) $(LDFLAGS) -o bin/main $(objects)'$'\n\n''clean:'$'\n\t''rm -fr bin/ obj/'$'\n' > Makefile
Linea 204: Linea 176:
$ cat src/main.cpp cat src/main.cpp
Linea 207: Linea 179:
 std::cout << "Benvenuti su Git!" << std::endl;
 return 0;
    std::cout << "Benvenuti su Git!" << std::endl;
    return 0;
Linea 211: Linea 183:
$ cat Makefile cat Makefile
Linea 221: Linea 193:
 @mkdir -p $@     @mkdir -p $@
Linea 224: Linea 196:
 @echo $<
 @$(CXX) $(CXXFLAGS) -c $< -o $@
 mkdir bin
 @$(CXX) $(LDFLAGS) -o bin/main $(objects)
    @echo $<
    @$(CXX) $(CXXFLAGS) -c $< -o $@
    mkdir bin
    @$(CXX) $(LDFLAGS) -o bin/main $(objects)
Linea 230: Linea 202:
 rm -fr bin/ obj/     rm -fr bin/ obj/
Linea 236: Linea 208:
$ ls -altr ls -altr
Linea 249: Linea 221:
$ make make
Linea 252: Linea 224:
$ bin/main bin/main
Linea 259: Linea 231:
$ git status git status
Linea 264: Linea 236:
# Makefile
# bin/
# obj/
# src/
#   Makefile
#   bin/
#   obj/
#   src/
Linea 281: Linea 253:
$ echo 'bin/'$'\n''*.o'$'\n' > .gitignore
$ cat .gitignore
echo 'bin/'$'\n''*.o'$'\n' > .gitignore
cat .gitignore
Linea 292: Linea 264:
$ git status git status
Linea 297: Linea 269:
# .gitignore
# Makefile
# src/
#   .gitignore
#   Makefile
#   src/
Linea 307: Linea 279:
$ git add .
$ git commit -a -m "Aggiunti sorgenti e Makefile"
git add .
 git commit -a -m "Aggiunti sorgenti e Makefile"
Linea 319: Linea 291:
$ git status git status
Linea 322: Linea 294:
$ git lg git lg
Linea 332: Linea 304:
$ sed -i "s/Benvenuti/`date`: Benvenuti/" src/main.cpp
$ cat src/main
sed -i "s/Benvenuti/`date`: Benvenuti/" src/main.cpp
cat src/main
Linea 336: Linea 308:
 std::cout << "Thu Dec 4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
 return 0;
    std::cout << "Thu Dec 4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
    return 0;
Linea 339: Linea 311:
$ make clean make clean
Linea 341: Linea 313:
$ make make
Linea 344: Linea 316:
$ bin/main bin/main
Linea 351: Linea 323:
$ git status git status
Linea 357: Linea 329:
# modified: src/main.cpp #   modified: src/main.cpp
Linea 365: Linea 337:
$ git commit -a -m "Aggiunta data in stampa." git commit -a -m "Aggiunta data in stampa."
Linea 373: Linea 345:
$ git status git status
Linea 376: Linea 348:
$ git lg git lg
Linea 391: Linea 363:
$ git branch git branch
Linea 398: Linea 370:
$ git branch gp-1 git branch gp-1
Linea 404: Linea 376:
$ git branch git branch
Linea 412: Linea 384:
$ git checkout gp-1 git checkout gp-1
Linea 419: Linea 391:
$ git branch git branch
Linea 427: Linea 399:
$ git lg git lg
Linea 438: Linea 410:
$ sed -i '3 a \\tstd::cout << "Issue gp-1 implementata" << std::endl;' src/main.cpp
$ cat src/main.cpp
sed -i '3 a \\tstd::cout << "Issue gp-1 implementata" << std::endl;' src/main.cpp
cat src/main.cpp
Linea 442: Linea 414:
 std::cout << "Thu Dec 4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
 std::cout << "Issue gp-1 implementata" << std::endl;
 return 0;
    std::cout << "Thu Dec 4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
    std::cout << "Issue gp-1 implementata" << std::endl;
    return 0;
Linea 446: Linea 418:
$ git status git status
Linea 452: Linea 424:
# modified: src/main.cpp #   modified: src/main.cpp
Linea 462: Linea 434:
$ git commit -a -m "Modificato codice per issue gp-1." git commit -a -m "Modificato codice per issue gp-1."
Linea 465: Linea 437:
$ git lg git lg
Linea 475: Linea 447:
$ make clean make clean
Linea 477: Linea 449:
$ make make
Linea 480: Linea 452:
$ bin/main bin/main
Linea 488: Linea 460:
$ git checkout master git checkout master
Linea 490: Linea 462:
$ make clean && make && bin/main make clean && make && bin/main
Linea 500: Linea 472:
$ git lg git lg
Linea 510: Linea 482:
$ git checkout SHA1 git checkout SHA1
Linea 518: Linea 490:
$ mkdir doc
$ echo '== Changelog =='$'\n'$'\n''First release' > doc/Changelog
$ cat doc/Changelog
mkdir doc
echo '== Changelog =='$'\n'$'\n''First release' > doc/Changelog
cat doc/Changelog
Linea 529: Linea 501:
$ git add doc/
$ git commit -a -m "Aggiunta cartella documentazione."
git add doc/
git commit -a -m "Aggiunta cartella documentazione."
Linea 534: Linea 506:
$ git lg git lg
Linea 559: Linea 531:
$ git merge altro_branch git merge altro_branch
Linea 565: Linea 537:
$ git branch git branch
Linea 573: Linea 545:
$ git merge gp-1 git merge gp-1
Linea 582: Linea 554:
$ git lg git lg
Linea 596: Linea 568:
$ git branch git branch
Linea 604: Linea 576:
$ cat src/main.cpp cat src/main.cpp
Linea 607: Linea 579:
 std::cout << "Thu Dec 4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
 std::cout << "Issue gp-1 implementata" << std::endl;
 return 0;
    std::cout << "Thu Dec 4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
    std::cout << "Issue gp-1 implementata" << std::endl;
    return 0;
Linea 611: Linea 583:
$ make clean && make && bin/main make clean && make && bin/main
Linea 639: Linea 611:
$ mkdir /home/user/gitproject_2
$ cd /home/user/gitproject_2
mkdir /home/user/gitproject_2
cd /home/user/gitproject_2
Linea 650: Linea 622:
$ git clone /home/user/gitproject . git clone /home/user/gitproject .
Linea 658: Linea 630:
$ git lg git lg
Linea 679: Linea 651:
$ git remote git remote
Linea 691: Linea 663:
$ git branch git branch
Linea 702: Linea 674:
$ mkdir /home/user/gitproject_3
$ cd /home/user/gitproject_3
$ git init
mkdir /home/user/gitproject_3
cd /home/user/gitproject_3
git init
Linea 711: Linea 683:
$ git remote git remote
Linea 717: Linea 689:
$ git remote add origin /home/user/gitproject
$ git remote
git remote add origin /home/user/gitproject
git remote
Linea 725: Linea 697:
$ git lg git lg
Linea 732: Linea 704:
$ ls -altr ls -altr
Linea 745: Linea 717:
$ git fetch origin git fetch origin
Linea 758: Linea 730:
$ ls -altr ls -altr
Linea 767: Linea 739:
$ git lg git lg
Linea 783: Linea 755:
$ git merge origin/master
$ git lg
git merge origin/master
git lg
Linea 798: Linea 770:
$ ls -altr ls -altr
Linea 813: Linea 785:
$ mkdir /home/user/gitproject_4
$ cd /home/user/gitproject_4
$ git init
mkdir /home/user/gitproject_4
cd /home/user/gitproject_4
git init
Linea 817: Linea 789:
$ git remote add origin /home/user/gitproject
$ git pull origin master
git remote add origin /home/user/gitproject
git pull origin master
Linea 825: Linea 797:
$ git lg git lg
Linea 843: Linea 815:
$ sed -i '4 a \\tstd::cout << "Modifica da un altro repository" << std::endl;' src/main.cpp
$ cat src/main.cpp
sed -i '4 a \\tstd::cout << "Modifica da un altro repository" << std::endl;' src/main.cpp
cat src/main.cpp
Linea 847: Linea 819:
 std::cout << "Thu Dec 4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
 std::cout << "Issue gp-1 implementata" << std::endl;
 std::cout << "Modifica da un altro repository" << std::endl;
 return 0;
    std::cout << "Thu Dec 4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
    std::cout << "Issue gp-1 implementata" << std::endl;
    std::cout << "Modifica da un altro repository" << std::endl;
    return 0;
Linea 852: Linea 824:
$ git commit -a -m "Aggiunta riga di output." git commit -a -m "Aggiunta riga di output."
Linea 855: Linea 827:
$ git lg git lg
Linea 870: Linea 842:
$ git push origin master git push origin master
Linea 912: Linea 884:
$ git config --global receive.denyCurrentBranch ignore git config --global receive.denyCurrentBranch ignore
Linea 918: Linea 890:
$ git config --global receive.denyCurrentBranch refuse git config --global receive.denyCurrentBranch refuse
Linea 927: Linea 899:
$ cd /home/user/gitproject_3 cd /home/user/gitproject_3
Linea 933: Linea 905:
$ git pull origin master git pull origin master
Linea 940: Linea 912:
$ git lg git lg


Problemi in questa pagina? Segnalali in questa discussione

Introduzione

Git è un CVS (Control Version System). Gli aspetti importanti di un CSV si possono riassumere nel:

  • tracciare la storia di un progetto e poter lavorare con le versioni precedenti;
  • permettere la collaborazione di più persone allo stesso progetto;
  • organizzare e semplificare l'andamento del progetto.

Rispetto a programmi come CVS, Subversion ma più similmente a Mercurial, Baazar, Git è caratterizzato da un sistema di controllo di versione localizzato, cioè utilizzabile con repository locali invece che remoti. Programmi come Subversion adottano un repository centrale, che materialmente è il posto (server, servizio web) in cui risiede il progetto. Gli utenti sincronizzano il lavoro con questo repository e quando devono salvare delle modifiche al progetto (commit), le salvano direttamente sul repository centralizzato.

Git invece lavora in locale. Non esiste un repository centralizzato nel modo che si intende in Subversion et similia. Ogni cartella di lavoro è un repository autosufficiente. Commit, ricerca log e molte altre azioni vengono eseguite in locale, senza l'appoggio di un server esterno. Quando si rende necessario è possibile sincronizzarsi con altri repository (server centrale, pc in rete, ecc..). Questo comporta ovvi vantaggi in termini di flessibilità.

Installazione/Configurazione base

  1. Per installare Git digitare in un terminale:

    sudo apt-get install git
  2. Impostare nome utente e indirizzo e-mail che si vogliono utilizzare per identificarsi nei progetti:

    git config --global user.name "NOME_UTENTE"
    git config --global user.email INDIRIZZO_E-MAIL
    avendo cura di soistituire le diciture NOME_UTENTE e INDIRIZZO_E-MAIL col proprio nome utente e indirizzo e-mail.

    È possibile modificare questi dati per un singolo repository omettendo il flag --global.

  3. Consigliato ma non obbligatorio, impostare l'alias lg per una versione personalizzata del comando log al fine di migliorarne la leggibilità:

    git config --global alias.lg "log --pretty=format:'%C(yellow)%h%Cred%d%Creset - %C(cyan)%an %Creset: %s %Cgreen(%cr)' --decorate --graph --all --abbrev-commit"

Creazione repository

Viene qui mostrato come creare un nuovo progetto o come importare (clonare) un progetto esistente.

Creazione nuovo repository

  1. Creare la cartella ~/gitproject nella propria Home che conterrà il progetto e spostarsi al suo interno:

    mkdir gitproject
    cd gitproject
  2. Per inizializzare un repository vuoto su cui poter incominciare a lavorare, digitare:

    git init

    verrà restituito un messaggio del tipo

    Initialized empty Git repository in /home/USER_NAME/gitproject/.git/

    All'interno di ~/gitproject sarà ora presente la cartella .git che contiene le informazioni del repository. Questa cartella è molto importante perché è l'unica che contiene tutti i dati necessari. (In Subversion ad es. viene creata una cartella .svn in ogni sottocartella del progetto).

Clonazione repository

  • Per scaricare un progetto già esistente su di un server, il comando da eseguire è:

    git clone PERCORSO/PROGETTO

    Verrà creata nella propria Home una cartella col nome del progetto in cui saranno copiati i contenuti.

  • Se invece si preferisce clonare il repository in una cartella specifica, basta aggiungere il percorso al comando:

    git clone PERCORSO/PROGETTO PERCORSO/LOCALE

Commit

Il commit è l'azione che permette di aggiungere, rimuovere e modificare file del repository.
Supponiamo di aver creato il progetto gitproject come mostrato in precedenza e di essersi spostati al suo interno:

cd gitproject

Possiamo vedere lo stato del repository, che ovviamente non dirà niente di utile:

git status

che restituirà:

Sul branch master

Commit iniziale

nothing to commit (create/copy files and use "git add" to track)

Possiamo anche analizzarne i log (utilizzando l'alias precedentente creato):

git lg
fatal: bad default revision 'HEAD'

Il primo comando ci dice che non c'e' niente da committare (perche' non abbiamo eseguito nessuna modifica essendo il progetto ancora completamente vuoto) mentre il secondo comando ci da errore (perche' essendo il repository appena creato non esiste ancora un log)

Proviamo a creare il primo file del progetto:

echo Inizio progetto > README
git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   README

Abbiamo creato un file nel progetto. Git ci avvisa che e' presente un nuovo file nella cartella, ma che e' nello stato untracked, ovvero che non viene tenuto in conto dal repository. I nuovi file devono essere aggiunti manualmente al repository. Per aggiungere uno o piu' file si utilizza il comando add:

git add README
git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#   new file:   README
#

Adesso il file si trova nello stato di stage, ovvero e' pronto per essere committato all'interno del repository. In breve i file in stato stage sono quelli pronti per essere committati, le cui modifiche ovvero verranno salvate nel repository.

Per applicare le modifiche fatte, si utilizza il comando commit, indicando i file da committare (o inserendo il flag -a se si vogliono committare tutti i files in stato stage).

git commit -a -m "README aggiunto"
[master (root-commit) 4510db0] README aggiunto
 1 file changed, 1 insertion(+)
 create mode 100644 README

Il flag -m serve per inserire il messaggio associato al commit. Il messaggio e' obbligatorio, ma se si omette il flag Git aprira' l'editor di default per l'inserimento del messaggio (noi nel wiki evitiamo finestre interattive quando possiamo).

Analizziamo adesso lo status ed il log:

git status
# On branch master
nothing to commit (working directory clean)

Lo stato e' in clean, ovvero la nostra cartella adesso e' allineata col repository (dato che si sono appena sincronizzati).

git lg
* e9ea39b (HEAD, master) - user : README aggiunto (30 seconds ago)

Il log contiene la lista dei commit. Avendo fatto solamente un commit abbiamo un'unica riga dove sono rappresentati:

  • SHAID: Ogni commit e' identificato da un checksum SHA. Questa e' una particolarita' di Git che lo differenzia da moltri altri prodotti simili. Semplificando si puo' dire che e' un ID univoco per ogni commit, che permette di distinguerlo dagli altri. Questo ID e' molto lungo, ma Git in genere ne mostra solo una piccola parte.
  • Branch: quando si crea un repository, Git crea un branch di default, chiamato master. Qua viene scritto il nome del branch cui appartiene il commit. HEAD e' un segnaposto di Git che indica in che punto ci troviamo nel repository. Dato che abbiamo appena committato, HEAD coincide col nostro stato (vedremo piu' avanti meglio a cosa serve).

  • Nome utente: Il nome dell'utente che ha eseguito il commit. Quando si collabora con altri qua comparira' il loro identificativo.
  • Messaggio: Questo e' il messaggio che abbiamo scritto nel commit. In particolare e' solo la prima riga nel caso in cui il messaggio sia composto da piu' righe.
  • Timestamp: mostra a quando risale il commit.

Tenete conto che questo output e' dipendente dal comando lg definito in precedenza. Git di default utilizza, per avere queste informazioni, il comando log:

git log
commit e9ea39bb9e5d93584eb8af4cbe037622ff123ef8
Author: user <user@mail.com>
Date:   Thu Dec 4 14:39:46 2014 +0100

    README aggiunto

Come potete immaginare, quando si hanno lunghi log il comando customizzato si rivela molto utile (oltre al fatto, come vedremo, che rappresenta graficamente il branching).

Aggiungiamo un altro paio di file, giusto per avere qualcosa di utile (da echo 'vpath in poi e' un unico comando):

mkdir src
printf '#include <iostream>\nint main() {\n\tstd::cout << "Benvenuti su Git!" << std::endl;\n\treturn 0;\n}\n' > src/main.cpp
echo 'vpath %.cpp ./src'$'\n'$'\n''src = main.cpp'$'\n\n''objects = $(patsubst %.cpp,obj/%.o,$(src))'$'\n\n''$(objects): | obj'$'\n\n''obj:'$'\n\t''@mkdir -p $@'$'\n\n''obj/%.o : %.cpp'$'\n\t''@echo $<'$'\n\t''@$(CXX) $(CXXFLAGS) -c $< -o $@'$'\n\t''mkdir bin'$'\n\t''@$(CXX) $(LDFLAGS) -o bin/main $(objects)'$'\n\n''clean:'$'\n\t''rm -fr bin/ obj/'$'\n' > Makefile

Abbiamo scritto un file sorgente minimale ed un makefile. Dovrebbero essere cosi':

cat src/main.cpp
#include <iostream>
int main() {
    std::cout << "Benvenuti su Git!" << std::endl;
    return 0;
}

cat Makefile
vpath %.cpp ./src

src = main.cpp

objects = $(patsubst %.cpp,obj/%.o,$(src))

$(objects): | obj

obj:
    @mkdir -p $@

obj/%.o : %.cpp
    @echo $<
    @$(CXX) $(CXXFLAGS) -c $< -o $@
    mkdir bin
    @$(CXX) $(LDFLAGS) -o bin/main $(objects)

clean:
    rm -fr bin/ obj/

A questo punto dovremmo avere la seguente situazione

ls -altr
total 24
drwxrwxr-x 20 user group 4096 Dec  4 14:37 ..
-rw-rw-r--  1 user group   16 Dec  4 14:38 README
drwxrwxr-x  8 user group 4096 Dec  4 14:40 .git
drwxrwxr-x  2 user group 4096 Dec  4 14:42 src
-rw-rw-r--  1 user group  258 Dec  4 14:42 Makefile
drwxrwxr-x  4 user group 4096 Dec  4 14:42 .

Proviamo a creare l'eseguibile ed a vedere cosa fa

make
./src/main.cpp
mkdir bin
bin/main
Benvenuti su Git!

Vediamo adesso lo stato del progetto:

git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   Makefile
#   bin/
#   obj/
#   src/
nothing added to commit but untracked files present (use "git add" to track)

Dato che non abbiamo modificato file pre-esistenti, ma creati di nuovi (Git sta mostrando le cartelle create), dobbiamo come nel caso precedente aggiungere a mano i file che ci servono. In questo caso pero', per i fini del progetto non serve aggiungere tutti i file: i file object infatti non servono, dato che vengono creati ogni volta dalla compilazione, cosi' come l'eseguibile. Non ha senso metterli sotto configurazione, ed e' anche dannoso (a lungo andare i file binari tengono ad ingrossare il repository). Possiamo allora cancellare a mano questi file, ma dovremmo farlo ogni volta. Possiamo non aggiungerli al repository, ma rimarrebbero sempre in mezzo.

Git ci permette di filtrare i file che non vogliamo, utilizzando .gitignore.

.gitignore

Questo file permette di specificare quali file devono essere esclusi dal repository. E' un semplice file di testo con un pattern per riga. Deve essere creato nella root del repository, dove si trova .git. I pattern possono essere file o cartelle. Nel nostro caso escluderemo la cartella bin e i file oggetto .o. Creiamo quindi il file:

echo 'bin/'$'\n''*.o'$'\n' > .gitignore
cat .gitignore
bin/
*.o

Abbiamo creato il file: nella prima riga abbiamo detto di escludere la cartella bin (per escludere cartelle occorre mettere / alla fine), e tutti i file con estensione .o.

Vediamo lo status:

git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   .gitignore
#   Makefile
#   src/

Modifiche al repository

Possiamo vedere come manchino adesso file e cartelle che abbiamo escluso, e si sia aggiunto .gitignore che e' un file come gli altri. Aggiungiamo tutto e committiamo. Per evitare di scrivere a mano ogni file, possiamo utilizzare "." per indicare tutti i files.

git add .
 git commit -a -m "Aggiunti sorgenti e Makefile"
[master e1e5a46] Aggiunti sorgenti e Makefile
 3 files changed, 28 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Makefile
 create mode 100644 src/main.cpp

Vediamo a che punto sta il nostro repository adesso:

git status
# On branch master
nothing to commit (working directory clean)
git lg
* e1e5a46 (HEAD, master) - user : Aggiunti sorgenti e Makefile (25 seconds ago)
* e9ea39b - user : README aggiunto (8 minutes ago)

Il contenuto della cartella e' sincronizzato col repository, ed e' stato aggiunto il commit fra i log.

Proviamo adesso a modificare un file gia' presente nel repository, mostrando la data nel messaggio:

sed -i "s/Benvenuti/`date`: Benvenuti/" src/main.cpp
cat src/main
#include <iostream>
int main() {
    std::cout << "Thu Dec  4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
    return 0;
}
make clean
rm -fr bin/ obj/
make
./src/main.cpp
mkdir bin
bin/main
Thu Dec  4 14:48:20 CET 2014: Benvenuti su Git!

Vediamo che e' successo nel frattempo:

git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   src/main.cpp
#
no changes added to commit (use "git add" and/or "git commit -a")

Vediamo che il file src/main.cpp e' stato modificato, ma che le modifiche non sono ancora state registrate nel repository. Per farlo, occorre committare (evitiamo di parlare dello stash in questa fase) il file. I file modificati vengono committati col comando commit (il flag -a sta ad indicare tutti i file modificati):

git commit -a -m "Aggiunta data in stampa."
[master 1412239] Aggiunta data in stampa.
 1 file changed, 1 insertion(+), 1 deletion(-)

Vediamo lo stato del repository:

git status
# On branch master
nothing to commit (working directory clean)
git lg
* 1412239 (HEAD, master) - user : Aggiunta data in stampa. (15 seconds ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (3 minutes ago)
* e9ea39b - user : README aggiunto (10 minutes ago)

Possiamo vedere che adesso il repository e' sincronizzato col contenuto della cartella, e che il commit con le modifiche e' stato aggiunto.

Branching

Git e' molto potente per quanto riguarda i branch. Possiamo creare alberature del repository anche molto complesse e sono molto veloci da gestire.

Per mostrare la lista dei branch presenti si utilizza il comando branch:

git branch
* master

Al momento abbiamo un solo branch attivo. Per creare un nuovo branch utilizziamo lo stesso comando, seguito dal nome del branch che si vuole creare:

git branch gp-1

Questo comando non produce nessun output ma se mostriamo di nuovo la lista dei branch, lo possiamo vedere nell'elenco:

git branch
  gp-1
* master

L'asterisco identifica il branch attivo, quello su cui stiamo lavorando in questo momento. Se vogliamo lavorare sul branch gp-1 dobbiamo utilizzare il comando checkout, che ci permette di switchare fra i vari branch:

git checkout gp-1
Switched to branch 'gp-1'

Vediamo che adesso gp-1 e' il branch su cui lavoriamo:

git branch
* gp-1
  master

Che succede nel log?

git lg
* 1412239 (HEAD, master, gp-1) - user : Aggiunta data in stampa. (2 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (5 minutes ago)
* e9ea39b - user : README aggiunto (12 minutes ago)

Vediamo che sono presenti entrambi i branch. Dato che ancora sono identici, perche' non abbiamo lavorato su nessuno dei due, puntano allo stesso commit.

Andiamo adesso a fare la modifica al codice:

sed -i '3 a \\tstd::cout << "Issue gp-1 implementata" << std::endl;' src/main.cpp
cat src/main.cpp
#include <iostream>
int main() {
    std::cout << "Thu Dec  4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
    std::cout << "Issue gp-1 implementata" << std::endl;
    return 0;
}
git status
# On branch gp-1
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   src/main.cpp
#
no changes added to commit (use "git add" and/or "git commit -a")

Possiamo vedere che Git si e' accorto della modifica, ma anche che stiamo modificando il nuovo branch (Il messaggio mostra il nome del branch su cui stiamo lavorando).

Committiamo le modifiche e vediamo che succede nel log:

git commit -a -m "Modificato codice per issue gp-1."
[gp-1 d8b610a] Modificato codice per issue gp-1.
 1 file changed, 1 insertion(+)
git lg
* d8b610a (HEAD, gp-1) - user : Modificato codice per issue gp-1. (18 seconds ago)
* 1412239 (master) - user : Aggiunta data in stampa. (4 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (7 minutes ago)
* e9ea39b - user : README aggiunto (14 minutes ago)

Che e' successo? Il branch master, che e' quello su cui lavoravamo prima, e' rimasto inalterato (infatti e' rimasto al commit precedente), mentre nel nuovo branch e' stato memorizzato il commit appena creato. Ovviamente se proviamo ad eseguire il programma otteniamo l'output modificato con la nostra aggiunta:

make clean
rm -fr bin/ obj/
make
./src/main.cpp
mkdir bin
bin/main
Thu Dec  4 14:48:20 CET 2014: Benvenuti su Git!
Issue gp-1 implementata

Torniamo nel branch master, rieseguiamo il programma e vediamo che succede:

git checkout master
Switched to branch 'master'
make clean && make && bin/main
rm -fr bin/ obj/
./src/main.cpp
mkdir bin
Thu Dec  4 14:48:20 CET 2014: Benvenuti su Git!

Come possiamo vedere, siamo ancora allo stato precedente, prima di eseguire il branch. A questo punto nel log si puo' vedere una cosa interessante:

git lg
* d8b610a (gp-1) - user : Modificato codice per issue gp-1. (2 minutes ago)
* 1412239 (HEAD, master) - user : Aggiunta data in stampa. (6 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (8 minutes ago)
* e9ea39b - user : README aggiunto (16 minutes ago)

Vi ricordate di HEAD? Adesso punta al commit master. HEAD quindi e' un puntatore al commit in cui attualmente ci troviamo. Possiamo andare in qualsiasi commit, non solo sugli ultimi dei rispettivi branch, digitando

git checkout SHA1

dove SHA1 e' il codice SHA1 del commit.

Adesso ci troviamo sul branch master. Supponiamo di voler aggiungere una cartella di documentazione:

mkdir doc
echo '== Changelog =='$'\n'$'\n''First release' > doc/Changelog
cat doc/Changelog
== Changelog ==

First release

Committiamo adesso la modifica e vediamo lo stato del nostro repository.

git add doc/
git commit -a -m "Aggiunta cartella documentazione."
[master e4064d8] Aggiunta cartella documentazione.
 1 file changed, 3 insertions(+)
 create mode 100644 doc/Changelog
git lg
* e4064d8 (HEAD, master) - user : Aggiunta cartella documentazione. (17 seconds ago)
| * d8b610a (gp-1) - user : Modificato codice per issue gp-1. (5 minutes ago)
|/  
* 1412239 - user : Aggiunta data in stampa. (9 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (11 minutes ago)
* e9ea39b - user : README aggiunto (19 minutes ago)

Interessante. Possiamo notare come adesso i nostri due branch stiano prendendo strade parallele. Sono partiti da un punto in comune, ma ognuno sta avendo modifiche indipendenti.

Merge

Adesso siamo nella situazione in cui:

  • Il branch master ha aggiunto la documentazione.

  • Il branch gp-1 ha implementato una feature.

Vogliamo adesso effettuare un merge. Cioe' vogliamo introdurre le modifiche fatte in un branch in un altro branch.

In particolare, vogliamo che nel branch master vengano introdotte le modifiche aggiunte nel branch gp-1. Vogliamo quindi effettuare il merge di gp-1 su master.

Il comando per effettuare i merge in Git e', molto fantasiosamente, merge. Il modo piu' diretto di utilizzare il comando e'

git merge altro_branch

Questo comando prende il branch altro_branch e introduce le sue modifiche sul branch corrente. Noi vogliamo effettuare il merge di gp-1 su master. Quindi prima assicuriamoci di essere sul branch master (effettuiamo il checkout in caso non lo fossimo):

git branch
  gp-1
* master

Dopodiche', effettuamo il merge:

git merge gp-1
Merge made by the 'recursive' strategy.
 src/main.cpp |    1 +
 1 file changed, 1 insertion(+)

Che e' successo? Andiamo a vedere il log:

git lg
*   f61b2e2 (HEAD, master) - user : Merge branch 'gp-1' (9 seconds ago)
|\  
| * d8b610a (gp-1) - user : Modificato codice per issue gp-1. (7 minutes ago)
* | e4064d8 - user : Aggiunta cartella documentazione. (3 minutes ago)
|/  
* 1412239 - user : Aggiunta data in stampa. (11 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (14 minutes ago)
* e9ea39b - user : README aggiunto (21 minutes ago)

Sono successe cose interessanti: come si puo' vedere, nel branch master e' stato creato un nuovo commit, che e' il commit di merge. Il suo commento dice che e' stato effettuato il merge del branch gp-1. Questo significa che le modifiche che abbiamo apportato su questo branch sono state riportate sul branch corrente (master). Ci troviamo ancora nel branch master:

git branch
  gp-1
* master

Ma possiamo vedere che sono state include le modifiche del secondo branch:

cat src/main.cpp 
#include <iostream>
int main() {
    std::cout << "Thu Dec  4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
    std::cout << "Issue gp-1 implementata" << std::endl;
    return 0;
}
make clean && make && bin/main
rm -fr bin/ obj/
./src/main.cpp
mkdir bin
Thu Dec  4 14:48:20 CET 2014: Benvenuti su Git!
Issue gp-1 implementata

Abbiamo quindi creato un branch secondario, abbiamo applicato una issue in questo branch, e poi abbiamo riportato sul branch principale le modifiche.

E' importante notare che quello che e' stato appena fatto non e' un mero esercizio. E' il modo standard in cui si lavora in Git con progetti seri. Di solito i passi da effettuare sono i seguenti:

  1. Si decide la issue da aggiungere/rimuovere/modificare.
  2. Dal branch principale (quale sia dipende dalla politica di gestione) si crea un nuovo branch, con un nome identificativo della modifica (puo' essere il codice associato a Bugzilla, ad esempio. Il nome gp-1 e' un esempio di codice, infatti).

  3. Si lavora alla issue su quel branch.
  4. Quando si completa, si effettua il merge sul branch principale in modo che il progetto includa le modifiche e siano rese pubblicamente disponibili.

Questo modo di lavorare permette di fare tutti i casini che si vogliono sul proprio branch, senza andare ad inficiare quello principale che ne rimane immune fino a quando la issue non e' completamente testata e funzionante (si spera).

Se nel frattempo arriva un'altra issue piu' urgente un bug da risolvere o quant altro, si crea semplicemente un altro branch da quello principale, come fatto in precedenza, e si lavora su quello. I branch sono indipendenti fra di loro. Questo assicura grande flessibilita' nella gestione del progetto.

Repository remoti

Quello che abbiamo fatto finora permette di lavorare da soli, ma nella maggior parte dei casi occorre che diverse persone possano accedere al repository, lavorare su di esso e mettere le modifiche.

Simuliamo in locale questo comportamento. Occorre creare una nuova cartella dove vogliamo creare il nuovo repository:

mkdir /home/user/gitproject_2
cd /home/user/gitproject_2

Adesso possiamo procedere in due modi:

Clonazione repository

Possiamo clonare direttamente un repository, avendone quindi una copia locale:

git clone /home/user/gitproject .
Cloning into '.'...
done.

Il comando clone ha permesso di prendere il repository remoto (nel nostro caso quello precedente, ma potrebbe essere un repository su web o su un server), e l'abbiamo copiato nella cartella corrente (.). Infatti possiamo vedere il log del repository:

git lg
*   f61b2e2 (HEAD, origin/master, origin/HEAD, master) - user : Merge branch 'gp-1' (4 minutes ago)
|\  
| * d8b610a (origin/gp-1) - user : Modificato codice per issue gp-1. (11 minutes ago)
* | e4064d8 - user : Aggiunta cartella documentazione. (6 minutes ago)
|/  
* 1412239 - user : Aggiunta data in stampa. (15 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (18 minutes ago)
* e9ea39b - user : README aggiunto (25 minutes ago)

Il log e' quasi uguale a quello originale. Si nota la presenza di altri branch:

  • origin/master
  • origin/HEAD
  • origin/gp-1

Questi sono i branch del repository remoto, che sono materialmente quelli creati nel repository originale. origin e' il nome di default che Git da ad un repository remoto, che possono essere elencati col comando remote

git remote
origin

Questo e' l'elenco dei repository remoti:

  • E' solamente uno perche' al momento abbiamo clonato uno gia' esistente
  • Si, Git puo' avere diversi repository remoti associato allo stesso repository locale. Questo permette molta flessibilita', ma al momento ignoriamo questo aspetto.

Abbiamo invece solamente un branch locale, master, sincronizzato con il master remoto (piu' precisamente nel punto in cui punta HEAD remoto):

git branch
* master

Aggiunta manuale remote

Il secondo metodo consiste nell'aggiungere manualmente il repository remoto a quello attuale.

Creiamo quindi un repository vuoto:

mkdir /home/user/gitproject_3
cd /home/user/gitproject_3
git init
Initialized empty Git repository in /home/user/gitproject_3/.git/

Se vediamo la lista dei repository remoti, ovviamente e' vuota:

git remote

Aggiungiamo quindi il repository remoto, tramite il comando remote add:

git remote add origin /home/user/gitproject
git remote
origin

Proviamo a vedere il log:

git lg
fatal: bad default revision 'HEAD'

Il repository e' vuoto!

ls -altr
drwxrwxr-x 22 user group 4096 Dec  4 15:06 ..
drwxrwxr-x  3 user group 4096 Dec  4 15:06 .
drwxrwxr-x  7 user group 4096 Dec  4 15:07 .git

Questo perche' e' stato aggiunto il repository remoto, ma non abbiamo ancora fatto la sincronizzazione. Per poter sincronizzare il repository locale con quello remoto, occorre scaricare le modifiche. Questo viene fatto in due modi.

Fetch e Pull

Si vuole prendere il contenuto del repository remoto e sincronizzare quello locale, in maniera da poter prendere le modifiche remote. Il comando fetch permette di scaricare in locale le modifiche remote, ma NON di applicarle.

git fetch origin
remote: Counting objects: 23, done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 23 (delta 6), reused 0 (delta 0)
Unpacking objects: 100% (23/23), done.
From /home/user/gitproject
 * [new branch]      gp-1       -> origin/gp-1
 * [new branch]      master     -> origin/master

Abbiamo scaricato le modifiche remote, ma se guardiamo il contenuto della cartella, risulta essere ancora vuota:

ls -altr
drwxrwxr-x 22 user group 4096 Dec  4 15:06 ..
drwxrwxr-x  3 user group 4096 Dec  4 15:06 .
drwxrwxr-x  7 user group 4096 Dec  4 15:09 .git

Questo perche' abbiamo scaricato le modifiche (che stanno dentro .git) ma non abbiamo ancora sincronizzato il tutto. Dal log possiamo vedere meglio quello che e' successo:

git lg
*   f61b2e2 (origin/master) - user : Merge branch 'gp-1' (10 minutes ago)
|\  
| * d8b610a (origin/gp-1) - user : Modificato codice per issue gp-1. (17 minutes ago)
* | e4064d8 - user : Aggiunta cartella documentazione. (12 minutes ago)
|/  
* 1412239 - user : Aggiunta data in stampa. (21 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (23 minutes ago)
* e9ea39b - user : README aggiunto (31 minutes ago)

Come possiamo vedere, ci sono i repository remoti, ma non quello locale su cui ci troviamo. E, dato che in quello locale non abbiamo ancora fatto niente, la nostra directory e' vuota.

Effettuamo allora il merge del branch locale con quello remoto, come abbiamo visto precedentemente, e vediamo che succede:

git merge origin/master
git lg
*   f61b2e2 (HEAD, origin/master, master) - user : Merge branch 'gp-1' (11 minutes ago)
|\  
| * d8b610a (origin/gp-1) - user : Modificato codice per issue gp-1. (18 minutes ago)
* | e4064d8 - user : Aggiunta cartella documentazione. (13 minutes ago)
|/  
* 1412239 - user : Aggiunta data in stampa. (22 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (24 minutes ago)
* e9ea39b - user : README aggiunto (32 minutes ago)

Come possiamo vedere, adesso i due branch sono allineati (puntano allo stesso commit). D'altronde, se vediamo il contenuto della cartella, possiamo vedere che e' aggiornato:

ls -altr
total 32
drwxrwxr-x 22 user group 4096 Dec  4 15:06 ..
drwxrwxr-x  2 user group 4096 Dec  4 15:11 src
-rw-rw-r--  1 user group   16 Dec  4 15:11 README
-rw-rw-r--  1 user group  258 Dec  4 15:11 Makefile
-rw-rw-r--  1 user group   10 Dec  4 15:11 .gitignore
drwxrwxr-x  8 user group 4096 Dec  4 15:11 .git
drwxrwxr-x  2 user group 4096 Dec  4 15:11 doc
drwxrwxr-x  5 user group 4096 Dec  4 15:11 .

Potevamo eseguire il tutto con un unico comando, pull. Questo racchiude in se' il fetch ed il merge, ed e' il comando solitamente utilizzato in questi casi. Ricreiamo il repository e vediamo che succede col pull (in una nuova cartella vuota). Il comando pull richiede il nome del repository remoto e il branch di cui fare l'update:

mkdir /home/user/gitproject_4
cd /home/user/gitproject_4
git init
Initialized empty Git repository in /home/user/gitproject_4/.git/
git remote add origin /home/user/gitproject
git pull origin master
remote: Counting objects: 23, done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 23 (delta 6), reused 0 (delta 0)
Unpacking objects: 100% (23/23), done.
From /home/user/gitproject
 * branch            master     -> FETCH_HEAD
git lg
*   f61b2e2 (HEAD, origin/master, master) - user : Merge branch 'gp-1' (15 minutes ago)
|\  
| * d8b610a - user : Modificato codice per issue gp-1. (22 minutes ago)
* | e4064d8 - user : Aggiunta cartella documentazione. (17 minutes ago)
|/  
* 1412239 - user : Aggiunta data in stampa. (26 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (28 minutes ago)
* e9ea39b - user : README aggiunto (36 minutes ago)

Abbiamo quindi creato un repository, e sincronizzato il suo contenuto con un repository remoto.

Pubblicazione modifiche locali

Possiamo a questo punto effettuare delle modifiche nel nostro nuovo repository:

sed -i '4 a \\tstd::cout << "Modifica da un altro repository" << std::endl;' src/main.cpp
cat src/main.cpp
#include <iostream>
int main() {
    std::cout << "Thu Dec  4 14:48:20 CET 2014: Benvenuti su Git!" << std::endl;
    std::cout << "Issue gp-1 implementata" << std::endl;
    std::cout << "Modifica da un altro repository" << std::endl;
    return 0;
}
git commit -a -m "Aggiunta riga di output."
[master 0ed9a58] Aggiunta riga di output.
 1 file changed, 1 insertion(+)
git lg
* 0ed9a58 (HEAD, master) - user : Aggiunta riga di output. (15 seconds ago)
*   f61b2e2 (origin/master) - user : Merge branch 'gp-1' (23 minutes ago)
|\  
| * d8b610a (origin/gp-1) - user : Modificato codice per issue gp-1. (30 minutes ago)
* | e4064d8 - user : Aggiunta cartella documentazione. (26 minutes ago)
|/  
* 1412239 - user : Aggiunta data in stampa. (34 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (37 minutes ago)
* e9ea39b - user : README aggiunto (44 minutes ago)

Abbiamo creato un nuovo commit, e dal log possiamo vedere che i due branch master, quello locale e quello remoto, non sono piu' (ovviamente) allineati. Per inviare al repository remoto le mostre modifiche, occorre utilizzare il comando push, specificando sempre il repository remoto e i branch da voler sincronizzare:

git push origin master
Counting objects: 7, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 395 bytes, done.
Total 4 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
To /home/user/gitproject
   f61b2e2..0ed9a58  master -> master

A questo punto abbiamo mandato nel repository remoto le nostre modifiche, in modo tale che gli altri utenti del repository, una volta sincronizzati con quello remoto, possano percepire le nostre modifiche.

Errore push

A questo punto si potrebbe ottenere il seguente messaggio di errore:

remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error: 
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error: 
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To ../gitproject/
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/home/user/gitproject'

Questo perche' il nostro repository remoto non e' di tipo bare. Al momento ignoriamo questo avviso e, per poter effettuare il push, digitare il seguente comando:

git config --global receive.denyCurrentBranch ignore

E poi rieseguire il push. Quando non e' piu' necessario settare lo stesso valore a refuse:

git config --global receive.denyCurrentBranch refuse

per ripristinare lo stato originale.

Per essere sicuri di aver fatto tutto correttamente, andiamo ad uno dei repository creato in precedenza:

cd /home/user/gitproject_3

Ed effettuiamo il pull per risincronizzarci col server remoto, che a questo punto avra' le nostre modifiche:

git pull origin master
From /home/user/gitproject
 * branch            master     -> FETCH_HEAD
Updating f61b2e2..0ed9a58
Fast-forward
 src/main.cpp |    1 +
 1 file changed, 1 insertion(+)
git lg
* 0ed9a58 (HEAD, origin/master, master) - user : Aggiunta riga di output. (4 minutes ago)
*   f61b2e2 - user : Merge branch 'gp-1' (27 minutes ago)
|\  
| * d8b610a (origin/gp-1) - user : Modificato codice per issue gp-1. (34 minutes ago)
* | e4064d8 - user : Aggiunta cartella documentazione. (30 minutes ago)
|/  
* 1412239 - user : Aggiunta data in stampa. (38 minutes ago)
* e1e5a46 - user : Aggiunti sorgenti e Makefile (41 minutes ago)
* e9ea39b - user : README aggiunto (48 minutes ago)

Conclusioni

In questa breve guida sono stati esaminate le seguenti operationi:

  • Creazione repository vuoto
  • Clonazione repository
  • Commit di modifiche
  • Creazione branch
  • Merge di branch
  • Sincronizzazione con repository remoti.

Git permette di fare molte altre cose. La guida ufficiale che potete trovare sul sito ufficiale e' molto esaustiva. Ovviamente e' possibile modificare il wiki per arricchirlo e correggerlo.


CategoryProgrammazione CategoryHomepage