Git de base

L’objectif de cette page est d’être une introduction rapide à l’utilisation de Git et non un cours détaillé sur son fonctionnement.

Dans ce document, nous utilisons Git pour créer un entrepôt ou collaborer sur un entrepôt existant. Nous nous concentrons ici sur l’utilisation basique c’est-à-dire que l’on suppose que tous les utilisateurs travaillent sur la même version de référence (dans un entrepôt central) et que les conflits (modifications en parallèle sur un même fichier) sont réduits au minimum grâce à la gestion de projet (on évite de travailler sur le même fichier). Chaque utilisateur pourra donc directement récupérer les modifications des autres et propager les siennes. L’historique de l’ensemble du développement sera conservé et commenté.

Dans ce cas d’utilisation, les seules commandes à utiliser sont celles indiquées dans le résumé ci-dessous (boite jaune).

La page Git Avancé présente des aspects plus avancés, comme le travail simultané sur des variantes du code source (branches), le contrôle de ce qui est ajouté dans l’entrepôt (pull request) ou la duplication d’un projet (fork).

Git (http://git-scm.com/) est un logiciel de gestion de version de troisième génération. A la différence de ceux de seconde génération (comme SVN), il est décentralisé. Il n’y a pas obligatoirement d’entrepôt central qui contient seul la base de données des modifications. Il n’y a pas non plus de notion de working copies. Il n’y a que des entrepôts. Les modifications sont validées dans une base de données locale (commit local). Les entrepôts peuvent récupérer les modifications d’autre entrepôts (pull) et en envoyer à d’autres (push).

Dans un premier temps ou dans les cas simples, il est possible de choisir un fonctionnement similaire à la seconde génération (mais ce n’est qu’une possibilité) en créant un entrepôt initial que chaque développeur va cloner. Chaque développeur réalise des modifications qu’il valide localement (commit). Il peut ensuite les propager dans l’entrepôt initial (push), les modifications dans l’entrepôt initial peuvent aussi être récupérées et appliquées localement (pull).

Pour commencer à utilise git, il suffit d’installer le logiciel (un simple paquet sous linux). Les échanges entre les entrepôts se font le plus souvent au dessus de ssh (parfois http/https).

Paramétrage de ssh

Le contrôle d’accès aux entrepôt est réalisé avec les clés publiques des utilisateurs. Si ce n’est pas déjà fait, chaque utilisateur doit donc créer une paire de clés avec la commande ssh-keygen.Il est conseillé de laisser la réponse par défaut à la première question (les clés seront réalisées en utilisant rsa, stockée dans ~/.ssh, la clé publique sera id_rsa.pub et la clé privée id_rsa), la passphrase est utilisée pour sécurisé la clé privée.

test@pc-bruno ~$ ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/test/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/test/.ssh/id_rsa.
Your public key has been saved in /home/test/.ssh/id_rsa.pub.
The key fingerprint is:
38:6e:d2:df:94:c9:30:4e:cf:1d:b0:db:9a:a1:8e:f4 test@pc-bruno
The key's randomart image is: 
+--[ RSA 2048]----+
|                 |
|                 |
|          .      |
|       .   o     |
|      o S . .    |
|     o + * * .   |
|    . = . X o    |
|     + + + +     |
|      ..E +      |
+-----------------+

Si vous disposez d’un système de gestion des entrepôts (github, redmine, …) votre clé publique peut maintenant être utilisée pour vous authentifier. Par exemple en l’ajoutant profil utilisateur.

Attention, des clés devront être disponibles sur toutes les machines que vous allez utiliser. Il faut donc soit les copier (les deux), soit en générer pour chaque machine.

Paramétrage de Git

Git a besoin au minimum de connaitre le nom et l’email à associer aux commit. Pour cela, exécutez les commandes suivantes (sur chaque machine utilisée) en changeant vos noms et email. La troisième commande définit le comportement par défaut du push.

test@pc-bruno $ git config --global user.name    "John Doe"
test@pc-bruno $ git config --global user.email   "john.doe@example.com"
test@pc-bruno $ git config --global push.default simple

Créer ou cloner un entrepôt

Pour utiliser un entrepôt il y a deux possibilité soit en créer un directement soit en cloner un existant.

Pour créer un entrepôt, on utilise la commande git init :

test@pc-bruno ~$ mkdir monProjet
test@pc-bruno ~$ cd monProjet/
test@pc-bruno monProjet$ git init
Dépôt Git vide initialisé dans /home/test/monProjet/.git/

Ici nous avons créé un répertoire vide mais il peut s’agit d’un répertoire déjà existant même si il a déjà du contenu.

Pour cloner un repository existant (dont on connait l’adresse) on utilise la commande git clone.

Par exemple, le projet sandbox des M1 de 2016 sur github (https://github.com/dptinfoutln/sandboxm1) possède un dépôt Git. La page d’accueil du projet indique l’adresse de l’entrepôt (dans le bouton clone)(git@github.com:dptinfoutln/sandboxm1.git).

Pour le cloner :

/tmp ❯❯❯ git clone git@github.com:dptinfoutln/sandboxm1.git
Clonage dans 'sandboxm1'...
remote: Counting objects: 17, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 17 (delta 0), reused 17 (delta 0), pack-reused 0
Réception d'objets: 100% (17/17), fait.
Vérification de la connectivité... fait.
/tmp ❯❯❯ cd sandboxm1

le répertoire sandboxm1 contient maintenant une copie de l’entrepôt.

Lors de la première connexion sur un serveur il est normal que ssh vous demande d’accepter son fingerprint (celui de github aura une autre empreinte).

The authenticity of host 'lsis.univ-tln.fr (193.49.96.32)' can't be established.
RSA key fingerprint is 5f:aa:42:17:37:45:31:05:3d:a4:b0:06:fb:20:dc:82.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'lsis.univ-tln.fr,193.49.96.32' (RSA) to the list of known hosts.

Si vous êtes sûr que c’est le bon serveur, répondez yes.

Si la commande vous demande la passphrase,

Enter passphrase for key '/home/test/.ssh/id_rsa': 

c’est bon, mais normalement, elle devrait être déjà stockée en mémoire dans l’agent ssh. Vous pouvez utiliser la commande ssh-add pour l’y ajouter manuellement.

Si la commande vous demande un mot de passe (et non la passphrase de votre clé) c’est que vous avez mal paramétré l’accès au serveur avec votre clé publique.

Quand un entrepôt est créé avec la commande clone, un lien vers l’entrepôt d’origine est conservé. Pour obtenir la liste des entrepôts distants associés à un entrepôt on utilise la commande git remote.

/t/sandboxm1 ❯❯❯ git remote -v                                                                    master
origin	git@github.com:dptinfoutln/sandboxm1.git (fetch)
origin	git@github.com:dptinfoutln/sandboxm1.git (push)

Commandes courantes

  • git status indique très précisément les changements depuis le dernier commit et conseille les commandes à utiliser. A utiliser systématiquement .
  • git add <filename> si le fichier dont le nom est filename n’est pas encore sous le contrôle de git, il est ajouté. S’il l’est déjà add indique d’accepter les modifications (pas encore committées). Il existe aussi git rm et git mv respectivement pour indiquer une suppression ou un déplacement.
  • git commit -m “message” valide les modifications précédentes dans l’entrepôt local. L’option -m indique le message décrivant le commit. Il est très utile donc obligatoire, si l’option est omise un éditeur de texte s’ouvrira avant le commit. L’option '-a’ permet d’accepter les changements dans tous les fichiers (elle évite un git add sur chacun), à utiliser avec prudence. L’option '-a’ n’ajoute, ni ne déplace ni ne supprime.
  • git pull est un raccourci pour deux commandes git fetch qui télécharge les changements depuis un serveur distant et git merge qui applique les changements en attente à l’entrepôt local.
  • git push propage les modifications vers un serveur distant.

L’exemple suivant illustre une utilisation classique : l’utilisateur test va créer un fichier test.txt (echo), le placer sous la surveillance de l’entrepôt (add), valider l’ajout (commit)). Notez le détail des réponses de git status.

test@pc-bruno sandboxgit$ git status
Sur la branche master
Votre branche est à jour avec 'origin/master'.

rien à valider, la copie de travail est propre
test@pc-bruno sandboxgit$ echo "hello" > test.txt
test@pc-bruno sandboxgit$ git status
Sur la branche master
Votre branche est à jour avec 'origin/master'.

Fichiers non suivis:
  (utilisez "git add <fichier>..." pour inclure dans ce qui sera validé)

	test.txt

aucune modification ajoutée à la validation mais des fichiers non suivis sont présents (utilisez "git add" pour les suivre)
test@pc-bruno sandboxgit$ git add test.txt 
test@pc-bruno sandboxgit$ git status
Sur la branche master
Votre branche est à jour avec 'origin/master'.

Modifications qui seront validées :
  (utilisez "git reset HEAD <fichier>..." pour désindexer)

	nouveau :   test.txt

test@pc-bruno sandboxgit$ git commit -m "Création d'un fichier texte à mon nom"[master af73a0b] Création d'un fichier texte à mon nom
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt
test@pc-bruno sandboxgit$ git status
Sur la branche master
Votre branche est en avance sur 'origin/master' de 1 commit.
  (utilisez "git push" pour publier vos commits locaux)

rien à valider, la copie de travail est propre

A ce moment, les modifications sont validées mais locales. Il est possible de consulter le journal des modifications avec git log.

test@pc-bruno sandboxgit$ git log
commit af73a0bcec8c38076a86e6758cdb088ce520f485
Author: John Doe <john.doe@example.com>
Date:   Wed Mar 19 11:39:15 2014 +0100

    Création d'un fichier texte à mon nom

commit 4160dd654c3e7bb113384a850ad77d0cae13cfc4
Author: Emmanuel Bruno <bruno@univ-tln.fr>
Date:   Tue Mar 18 10:21:40 2014 +0100

    dummy fixes #99 closes #99
...

On peut vérifier ce qui a changé à distance (git remote update puis git status) :

test@pc-bruno sandboxgit$ git remote update
Récupération de origin
test@pc-bruno sandboxgit$ git status
Sur la branche master
Votre branche est en avance sur 'origin/master' de 1 commit.
  (utilisez "git push" pour publier vos commits locaux)

rien à valider, la copie de travail est propre

Dans ce cas rien n’a changé, et l’on voit bien qu’il y a un commit à pousser.

Nous allons maintenant modifier le fichier test.txt en ajoutant du texte (echo), accepter la modification (add) et la valider (commit). Ensuite, avant de pousser les modifications sur le serveur distant (git push), nous allons vérifier si des changement sont intervenus et éventuellement mettre à jour la copie locale (git pull).

test@pc-bruno sandboxgit$ echo "encore du texte">>test.txt
test@pc-bruno sandboxgit$ git add test.txt 
test@pc-bruno sandboxgit$ git commit -m "Ajout de précisions"
[master 53fa162] Ajout de précisions
 1 file changed, 1 insertion(+)
test@pc-bruno sandboxgit$ git remote update
Récupération de origin
test@pc-bruno sandboxgit$ git status
Sur la branche master
Votre branche est en avance sur 'origin/master' de 2 commits.
  (utilisez "git push" pour publier vos commits locaux)

rien à valider, la copie de travail est propre
test@pc-bruno sandboxgit$ git pull
Already up-to-date.
test@pc-bruno sandboxgit$ git push
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 577 bytes | 0 bytes/s, done.
Total 6 (delta 1), reused 0 (delta 0)
To ssh://gitedu@lsis.univ-tln.fr/sandboxgit.git
   4160dd6..53fa162  master -> master

En résumé faire :

  • une fois git init ou git clone pour créer un entrepôt.
  • git add pour un accepter nouveau fichier ou des modifications ou git rm ou git mv.
  • git remote update && git status pour savoir ce qui a changé sur le serveur.
  • git pull pour récupérer et appliquer les modifications distantes.
  • git push pour pousser les modifications vers le serveur.

Quels fichiers versionner ?

Tous les fichiers ne doivent pas être déposé sur le système de gestion de version. Les détails sont donnés ici : http://bruno.lsis.univ-tln.fr/git/gitignore

Il est impératif de mettre en place un fichier .gitignore (cf. ci-dessus) à la racine de votre projet AVANT le premier commit et surtout le premier push.

Comme nous l’avons déjà vu git log permet de consulter le journal des modifications:

test@pc-bruno sandboxgit$ git log
commit 53fa162ffda184e6fdf9fdaa93fc60ae8b24d58a
Author: John Doe <john.doe@example.com>
Date:   Wed Mar 19 11:50:36 2014 +0100

    Ajout de précisions

commit af73a0bcec8c38076a86e6758cdb088ce520f485
Author: John Doe <john.doe@example.com>
Date:   Wed Mar 19 11:39:15 2014 +0100

    Création d'un fichier texte à mon nom
commit 4160dd654c3e7bb113384a850ad77d0cae13cfc4
Author: Emmanuel Bruno <bruno@univ-tln.fr>
Date:   Tue Mar 18 10:21:40 2014 +0100

    dummy fixes #99 closes #99
...

Nous voyons qu’à chaque commit est associé un haché (SHA-1), par exemple 53fa162ffda184e6fdf9fdaa93fc60ae8b24d58a pour le dernier commit.

Il est possible de voir ce qui s’est passé pour un commit donné :

test@pc-bruno sandboxgit$ git show 53fa162ffda184e6fdf9fdaa93fc60ae8b24d58a
commit 53fa162ffda184e6fdf9fdaa93fc60ae8b24d58a
Author: John Doe <john.doe@example.com>
Date:   Wed Mar 19 11:50:36 2014 +0100

    Ajout de précisions

diff --git a/test.txt b/test.txt
index ce01362..65581b3 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
 hello
+encore du texte

A noter, qu’il est possible de n’utiliser qu’un préfixe du hash à condition qu’il soit non ambigu. Git est même capable de les calculer seul.

test@pc-bruno sandboxgit$ git log --abbrev-commit --pretty=oneline
53fa162 Ajout de précisions
af73a0b Création d'un fichier texte à mon nom
...

Il est aussi possible de voir ce qui a changé entre deux commit avec la commande git diff soit en indiquant deux commit entre lesquels faire la différence soit un seul (l’état courant sera alors utilisé) :

test@pc-bruno sandboxgit$ git diff af73a0b
diff --git a/test.txt b/test.txt
index ce01362..65581b3 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
 hello
+encore du texte

Traitement de base des conflits

Dans cette section nous allons voir d’où viennent les conflits et comment les régler. Tous d’abord nous allons créer localement trois repositories : un central et deux clones.

Comme nous l’avons vu, un entrepôt Git contient à la fois la copie de travail et la base de données des modifications. Pour des raisons techniques, un entrepôt qui contient une copie de travail ne peut pas recevoir de push. Pour cela il faut un entrepôt bare (sans copie de travail). Un entrepôt bare peut être créé directement ou on peut faire un clone bare d’un entrepôt existant (seul la base de données des modifications sera clonée).

Les commandes suivantes permettent de créer un entrepôt monProjetCentral de type bare et deux clone “Projet1” et “Projet2”.

test@pc-bruno ~$ mkdir monProjetCentral && cd monProjetCentral && git --bare init
Dépôt Git vide initialisé dans /home/test/monProjetCentral/
test@pc-bruno monProjetCentral$ cd ..
test@pc-bruno ~$ git clone monProjetCentral monProjet1
Clonage dans 'monProjet1'...
warning: Vous semblez avoir cloné un dépôt vide.
fait.
test@pc-bruno ~$ git clone monProjetCentral monProjet2
Clonage dans 'monProjet2'...
warning: Vous semblez avoir cloné un dépôt vide.
fait.
test@pc-bruno ~$ cd monProjet1/
test@pc-bruno monProjet1$ git remote -v
origin	/home/test/monProjetCentral (fetch)
origin	/home/test/monProjetCentral (push)

On notera qu’ici on a cloné un entrepôt local, il aurait pu être distant cela ne change rien. On notera aussi qu’il est possible de changer le nom du répertoire contenant le clone. Nous avons donc trois entrepôts : monProjetCentral, monProjet1 et monProjet2. Le premier ne contient pas de copie de travail mais la base de données des changements. Les deux derniers sont des clones du premier (ils en gardent une référence), ils ont aussi chacun leur copie de travail.

Nous allons maintenant ajouter un fichier README dans monProjet1, le pousser dans l’entrepôt central.

test@pc-bruno ~$ cd monProjet1/
test@pc-bruno monProjet1$ echo "Hello">README && git add README
test@pc-bruno monProjet1$ git commit -m "Initial release"
[master (commit racine) 0a1efb5] Initial release
 1 file changed, 1 insertion(+)
 create mode 100644 README
test@pc-bruno monProjet1$ git push
Counting objects: 3, done.
Writing objects: 100% (3/3), 219 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /home/test/monProjetCentral
 * [new branch]      master -> master

et mettre à jour monProjet2.

test@pc-bruno monProjet1$ cd ../monProjet2/
test@pc-bruno monProjet2$ git pull
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
Depuis /home/test/monProjetCentral
 * [nouvelle branche] master     -> origin/master

Habituellement, il y a un cycle de add/commit/pull/push depuis monProjet1 et monProjet2. Un problème va apparaître si l’un deux tente de pousser une modification sur fichier qui a déjà été modifié.

Par exemple, on modifie le fichier README dans monProjet1 et on pousse la modification :

test@pc-bruno monProjet1$ echo "Modif depuis 1">>README && git add READMEtest@pc-bruno monProjet1$ git commit -m "chgt depuis 1" && git push
[master 5cebb57] chgt depuis 1
 1 file changed, 1 insertion(+)
Counting objects: 5, done.
Writing objects: 100% (3/3), 264 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /home/test/monProjetCentral
   0a1efb5..5cebb57  master -> master

puis on modifie aussi README dans monProjet2 sans avoir mis à jour (donc sans faire de pull).

test@pc-bruno monProjet2$ echo "Modif depuis 2">>README && git add README
test@pc-bruno monProjet2$ git commit -m "chgt depuis 2" && git push
[master ff2fc8f] chgt depuis 2
 1 file changed, 1 insertion(+)
To /home/test/monProjetCentral
 ! [rejected]        master -> master (fetch first)
error: impossible de pousser des références vers '/home/test/monProjetCentral'
astuce: Les mises à jour ont été rejetées car la branche distante contient du travail que
astuce: vous n'avez pas en local. Ceci est généralement causé par un autre dépôt poussé
astuce: vers la même référence. Vous pourriez intégrer d'abord les changements distants
astuce: (par exemple 'git pull ...') avant de pousser à nouveau.
astuce: Voir la 'Note à propos des avances rapides' dans 'git push --help' pour plus d'information.

Nous avons modifié README à partir d’une version antérieure à celle sur le serveur et donc le push automatique n’est pas possible, il faut d’abord mettre à jour la copie locale. Dans ce cas, plusieurs situation peuvent se présenter soit les modifications peuvent être appliquée automatiquement soit il faut régler le problème manuellement.

test@pc-bruno monProjet2$ git pull
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
Depuis /home/test/monProjetCentral
   0a1efb5..5cebb57  master     -> origin/master
Fusion automatique de README
CONFLIT (contenu) : Conflit de fusion dans README
La fusion automatique a échoué ; réglez les conflits et validez le résultat.

Dans le cas le contenu du fichier en conflit est modifié par git pour indiquer les différences des versions :

test@pc-bruno monProjet2$ cat README 
Hello
<<<<<<< HEAD
Modif depuis 2
=======
Modif depuis 1
>>>>>>> 5cebb57a2be60b9f7fbbe49b5735c256d1d285bf

Il faut éditer le fichier (ou utiliser un outils graphique de résolution de conflit) pour remettre le fichier dans un état cohérent c’est-à-dire : écraser la version locale par celle de l’entrepôt, faire le contraire ou fusionner en fonction de la situation.

Dans notre exemple, nous avons fusionné en utilisant un éditeurs de texte les modifications dans monProjet2. Puis il faut les valider, les pousser dans l’entrepôt monProjetCentral.

test@pc-bruno monProjet2$ git add README 
test@pc-bruno monProjet2$ git commit -m "Résolution du conflit"
[master c982ffe] Résolution du conflit
test@pc-bruno monProjet2$ git pull
Already up-to-date.
test@pc-bruno monProjet2$ git push
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 539 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To /home/test/monProjetCentral
   5cebb57..c982ffe  master -> master

On peut alors éventuellement mettre à jour monProjet1.

test@pc-bruno monProjet1$ git pull
remote: Counting objects: 10, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
Depuis /home/test/monProjetCentral
   5cebb57..c982ffe  master     -> origin/master
Mise à jour 5cebb57..c982ffe
Fast-forward
 README | 2 ++
 1 file changed, 2 insertions(+)

Vous l’aurez compris traiter les conflits est en général pénible. En gérant votre projet de façon précise, essayez au maximum d’éviter de travailler sur les même fichiers pour ne pas provoquer de conflits. Cela sera d’autant plus simple que si projet est bien construit et pensé pour la maintenabilité et la réutilisabilité : force modularité et dépendance la plus faible possible entre les modules.

ScreenCast Git avec Redmine

CETTE PARTIE VA BIENTOT ETRE REMPLACEE

Ce screencast présente la simplicité de la création et de l’utilisation d’un projet géré par Redmine et disposant d’un entrepôt Git.

https://www.youtube.com/watch?v=1GXIk-NxOXg

https://www.youtube.com/watch?v=fJelNt3W9rw

type:
Howto
technologie:
Git
theme:
VCS